Esempio n. 1
0
void checkered_alloc(struct smart_alloc *sa)
{
    int i = 0;
    int j;
    char *pointers[10];

    for(i = 0; i < 10; i++) {
        pointers[i] = smart_alloc(sa, 100);
        assert(pointers[i]);
        for(j = 0; j < 100; j++) {
            pointers[i][j] = 1;
        }
    }
    for(i = 1; i < 10; i += 2) {
        smart_free(sa, pointers[i]);
    }
    for(i = 1; i < 10; i += 2) {
        pointers[i] = smart_alloc(sa, 90);
        assert(pointers[i]);
        for(j = 0; j < 90; j++) {
            pointers[i][j] = 1;
        }
    }

    for(i = 0; i < 10; i++) {
        smart_free(sa, pointers[i]);
    }
}
Esempio n. 2
0
MySQLStmtVariables::~MySQLStmtVariables() {
  for (int i = 0; i < m_arr.size(); i++) {
    auto buf = &m_vars[i];
    if (buf->buffer_length > 0) {
      smart_free(buf->buffer);
    }
  }

  smart_free(m_vars);
  smart_free(m_null);
  smart_free(m_length);
}
Esempio n. 3
0
void
two_alloc(struct smart_alloc *sa)
{
    char *message1;
    char *message2;

    message1 = smart_dup(sa, "Hello");
    message2 = smart_dup(sa, "World");

    printf("%s, %s!\n", message1, message2);
    smart_free(sa, message1);
    smart_free(sa, message2);
}
Esempio n. 4
0
void
basic_free(struct smart_alloc *sa)
{
    char *mem = smart_alloc(sa, BIG_ALLOC);
    smart_free(sa, mem);
    mem = smart_alloc(sa, BIG_ALLOC);

    if(mem) {
        printf("%d: OK\n", __LINE__);
    } else {
        printf("%d: NOT OK\n", __LINE__);
    }

    smart_free(sa, mem);
}
Esempio n. 5
0
void
oom(struct smart_alloc *sa)
{
    char *mem1 = smart_alloc(sa, BIG_ALLOC);
    char *mem2 = smart_alloc(sa, BIG_ALLOC);

    if(! mem2) {
        printf("%d: OK\n", __LINE__);
    } else {
        printf("%d: NOT OK\n", __LINE__);
    }

    smart_free(sa, mem1);
    smart_free(sa, mem2);
}
Esempio n. 6
0
inline void* MemoryManager::smartRealloc(void* inputPtr, size_t nbytes) {
  FTRACE(1, "smartRealloc: {} to {}\n", inputPtr, nbytes);
  assert(nbytes > 0);

  void* ptr = debug ? static_cast<DebugHeader*>(inputPtr) - 1 : inputPtr;

  auto const n = static_cast<SweepNode*>(ptr) - 1;
  if (LIKELY(n->padbytes <= kMaxSmartSize)) {
    void* newmem = smart_malloc(nbytes);
    auto const copySize = std::min(
      n->padbytes - sizeof(SmallNode) - (debug ? sizeof(DebugHeader) : 0),
      nbytes
    );
    newmem = memcpy(newmem, inputPtr, copySize);
    smart_free(inputPtr);
    return newmem;
  }

  // Ok, it's a big allocation.  Since we don't know how big it is
  // (i.e. how much data we should memcpy), we have no choice but to
  // ask malloc to realloc for us.
  auto const oldNext = n->next;
  auto const oldPrev = n->prev;

  auto const newNode = static_cast<SweepNode*>(
    realloc(n, debugAddExtra(nbytes + sizeof(SweepNode)))
  );

  refreshStatsHelper();
  if (newNode != n) {
    oldNext->prev = oldPrev->next = newNode;
  }
  return debugPostAllocate(newNode + 1, 0, 0);
}
Esempio n. 7
0
inline void* MemoryManager::smartRealloc(void* ptr, size_t nbytes) {
  FTRACE(3, "smartRealloc: {} to {}\n", ptr, nbytes);
  assert(nbytes > 0);
  auto const n = static_cast<MallocNode*>(ptr) - 1;
  if (LIKELY(n->small.padbytes <= kMaxSmartSize)) {
    void* newmem = smart_malloc(nbytes);
    auto const copySize = std::min(
      n->small.padbytes - sizeof(SmallNode),
      nbytes
    );
    newmem = memcpy(newmem, ptr, copySize);
    smart_free(ptr);
    return newmem;
  }

  // Ok, it's a big allocation.  Since we don't know how big it is
  // (i.e. how much data we should memcpy), we have no choice but to
  // ask malloc to realloc for us.
  auto const oldNext = n->big.next;
  auto const oldPrev = n->big.prev;

  auto const newNode = static_cast<BigNode*>(
    safe_realloc(n, nbytes + sizeof(BigNode))
  );

  refreshStats();
  if (newNode != &n->big) {
    oldNext->prev = oldPrev->next = newNode;
  }
  return newNode + 1;
}
Esempio n. 8
0
static String HHVM_FUNCTION(get_current_user) {
  int pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (pwbuflen < 1) {
    return "";
  }
  char *pwbuf = (char*)smart_malloc(pwbuflen);
  struct passwd pw;
  struct passwd *retpwptr = NULL;
  if (getpwuid_r(getuid(), &pw, pwbuf, pwbuflen, &retpwptr) != 0) {
    smart_free(pwbuf);
    return "";
  }
  String ret(pw.pw_name, CopyString);
  smart_free(pwbuf);
  return ret;
}
Esempio n. 9
0
MIterCtx::~MIterCtx() {
    m_mArray->~MutableArrayIter();
    smart_free(m_mArray);
    tvRefcountedDecRef(&m_key);
    tvRefcountedDecRef(&m_val);
    if (m_ref) decRefRef(const_cast<RefData*>(m_ref));
}
Esempio n. 10
0
MIterCtx::~MIterCtx() {
  m_mArray->~MutableArrayIter();
  smart_free(m_mArray);
  tvRefcountedDecRef(&m_key);
  tvRefcountedDecRef(&m_val);
  if (m_ref && m_ref->decRefCount() == 0) {
    const_cast<RefData*>(m_ref)->release();
  }
}
Esempio n. 11
0
void* smart_realloc(void* ptr, size_t nbytes) {
  auto& mm = MM();
  if (!ptr) return smart_malloc(nbytes);
  if (!nbytes) {
    smart_free(ptr);
    return nullptr;
  }
  return mm.smartRealloc(ptr, nbytes);
}
Esempio n. 12
0
void
basic_test(struct smart_alloc *sa)
{
    char *message = smart_dup(sa, "Hello, World!");

    printf("%s\n", message);
    smart_free(sa, message);

}
Esempio n. 13
0
/**
 * Delegate the responsibility for freeing the buffer to the
 * frozen copy, if it exists.
 */
BaseVector::~BaseVector() {
  if (m_frozenCopy.isNull() && m_data) {
    for (uint i = 0; i < m_size; ++i) {
      tvRefcountedDecRef(&m_data[i]);
    }

    smart_free(m_data);
    m_data = nullptr;
  }
}
Esempio n. 14
0
ALWAYS_INLINE SharedMap::~SharedMap() {
  if (m_localCache) {
    for (TypedValue* tv = m_localCache, *end = tv + m_arr->arrCap();
         tv < end; ++tv) {
      tvRefcountedDecRef(tv);
    }
    smart_free(m_localCache);
  }
  m_arr->decRef();
}
Esempio n. 15
0
HOT_FUNC
SharedMap::~SharedMap() {
  if (m_localCache) {
    for (TypedValue* tv = m_localCache, *end = tv + size();
         tv < end; ++tv) {
      tvRefcountedDecRef(tv);
    }
    smart_free(m_localCache);
  }
}
Esempio n. 16
0
ALWAYS_INLINE APCLocalArray::~APCLocalArray() {
  if (m_localCache) {
    for (TypedValue* tv = m_localCache, *end = tv + m_arr->capacity();
         tv < end; ++tv) {
      tvRefcountedDecRef(tv);
    }
    smart_free(m_localCache);
  }
  m_arr->getHandle()->unreference();
}
Esempio n. 17
0
HOT_FUNC_HPHP
VectorArray::~VectorArray() {
  uint size = m_size;
  for (uint i = 0; i < size; i++) {
    tvAsVariant(&m_elems[i]).~Variant();
  }
  if (m_allocMode == kSmart) {
    smart_free(m_elems);
  } else if (m_allocMode == kMalloc) {
    free(m_elems);
  }
}
Esempio n. 18
0
static Variant HHVM_FUNCTION(gmp_strval,
                            const Variant& data,
                            const int64_t base /* = 10 */) {
  mpz_t gmpData;

  if (base < GMP_MIN_BASE || (base > -2 && base < 2) || base > GMP_MAX_BASE) {
    raise_warning(cs_GMP_INVALID_BASE_VALUE,
                  cs_GMP_FUNC_NAME_GMP_STRVAL,
                  base,
                  GMP_MAX_BASE);
    return false;
  }

  if (!variantToGMPData(cs_GMP_FUNC_NAME_GMP_STRVAL, gmpData, data, base)) {
    return false;
  }

  int charLength = mpz_sizeinbase(gmpData, abs(base)) + 1;
  if (mpz_sgn(gmpData) < 0) {
    ++charLength;
  }

  char *charStr = (char*) smart_malloc(charLength);
  if (!mpz_get_str(charStr, base, gmpData)) {
    smart_free(charStr);
    mpz_clear(gmpData);

    return false;
  }

  String returnValue(charStr);

  smart_free(charStr);
  mpz_clear(gmpData);

  return returnValue;
}
Esempio n. 19
0
void XDebugServer::deinitDbgp() {
  setStatus(Status::STOPPING, Reason::OK);

  // Send the xml shutdown response
  xdebug_xml_node* response = xdebug_xml_node_init("response");
  addXmnls(*response);
  addCmdAndTrans(*response);
  sendMessage(*response);
  xdebug_xml_node_dtor(response);

  // Wait for a response from the client
  doCommandLoop();

  // Free the input buffer
  smart_free(m_buffer);
}
Esempio n. 20
0
inline void* MemoryManager::smartRealloc(void* ptr, size_t nbytes) {
  FTRACE(3, "smartRealloc: {} to {}\n", ptr, nbytes);
  assert(nbytes > 0);
  auto const n = static_cast<MallocNode*>(ptr) - 1;
  if (LIKELY(n->small.padbytes <= kMaxSmartSize)) {
    void* newmem = smart_malloc(nbytes);
    auto const copySize = std::min(
      n->small.padbytes - sizeof(SmallNode),
      nbytes
    );
    newmem = memcpy(newmem, ptr, copySize);
    smart_free(ptr);
    return newmem;
  }
  // Ok, it's a big allocation.
  auto block = m_heap.resizeBig(ptr, nbytes);
  refreshStats();
  return block.ptr;
}
Esempio n. 21
0
String StringUtil::Implode(const Variant& items, const String& delim) {
  if (!isContainer(items)) {
    throw_param_is_not_container();
  }
  int size = getContainerSize(items);
  if (size == 0) return "";

  String* sitems = (String*)smart_malloc(size * sizeof(String));
  int len = 0;
  int lenDelim = delim.size();
  int i = 0;
  for (ArrayIter iter(items); iter; ++iter) {
    new (&sitems[i]) String(iter.second().toString());
    len += sitems[i].size() + lenDelim;
    i++;
  }
  len -= lenDelim; // always one delimiter less than count of items
  assert(i == size);

  String s = String(len, ReserveString);
  char *buffer = s.bufferSlice().ptr;
  const char *sdelim = delim.data();
  char *p = buffer;
  for (int i = 0; i < size; i++) {
    String &item = sitems[i];
    if (i && lenDelim) {
      memcpy(p, sdelim, lenDelim);
      p += lenDelim;
    }
    int lenItem = item.size();
    if (lenItem) {
      memcpy(p, item.data(), lenItem);
      p += lenItem;
    }
    sitems[i].~String();
  }
  smart_free(sitems);
  assert(p - buffer == len);
  s.setSize(len);
  return s;
}
Esempio n. 22
0
void StringBuffer::printf(const char *format, ...) {
  va_list ap;
  va_start(ap, format);

  bool printed = false;
  for (int len = 1024; !printed; len <<= 1) {
    va_list v;
    va_copy(v, ap);

    char *buf = (char*)smart_malloc(len);
    if (vsnprintf(buf, len, format, v) < len) {
      append(buf);
      printed = true;
    }
    smart_free(buf);

    va_end(v);
  }

  va_end(ap);
}
Esempio n. 23
0
HOT_FUNC
void StringData::releaseDataSlowPath() {
  assert(!isSmall());
  assert(checkSane());

  auto const loadedMode = mode();

  if (LIKELY(loadedMode == Mode::Smart)) {
    smart_free(m_data);
    return;
  }

  if (loadedMode == Mode::Shared) {
    assert(checkSane());
    m_big.shared->decRef();
    delist();
    return;
  }

  assert(loadedMode == Mode::Malloc);
  assert(checkSane());
  free(m_data);
}
Esempio n. 24
0
/*
 * Convert a floating point number to a string formats 'f', 'e' or 'E'.
 * The result is placed in buf, and len denotes the length of the string
 * The sign is returned in the is_negative argument (and is not placed
 * in buf).
 */
char * php_conv_fp(register char format, register double num,
                   bool add_dp, int precision, char dec_point,
                   int *is_negative, char *buf, int *len) {
  register char *s = buf;
  register char *p, *p_orig;
  int decimal_point;

  if (precision >= NDIG - 1) {
    precision = NDIG - 2;
  }

  if (format == 'F') {
    p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
  } else { // either e or E format
    p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
  }

  // Check for Infinity and NaN
  if (isalpha((int)*p)) {
    *len = strlen(p);
    memcpy(buf, p, *len + 1);
    *is_negative = 0;
    smart_free(p_orig);
    return (buf);
  }
  if (format == 'F') {
    if (decimal_point <= 0) {
      if (num != 0 || precision > 0) {
        *s++ = '0';
        if (precision > 0) {
          *s++ = dec_point;
          while (decimal_point++ < 0) {
            *s++ = '0';
          }
        } else if (add_dp) {
          *s++ = dec_point;
        }
      }
    } else {
      int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
      decimal_point -= addz;
      while (decimal_point-- > 0) {
        *s++ = *p++;
      }
      while (addz-- > 0) {
        *s++ = '0';
      }
      if (precision > 0 || add_dp) {
        *s++ = dec_point;
      }
    }
  } else {
    *s++ = *p++;
    if (precision > 0 || add_dp) {
      *s++ = '.';
    }
  }

  // copy the rest of p, the NUL is NOT copied
  while (*p) {
    *s++ = *p++;
  }

  if (format != 'F') {
    char temp[EXPONENT_LENGTH]; // for exponent conversion
    int t_len;
    int exponent_is_negative;

    *s++ = format; // either e or E
    decimal_point--;
    if (decimal_point != 0) {
      p = ap_php_conv_10((int64_t) decimal_point, false,
                         &exponent_is_negative, &temp[EXPONENT_LENGTH],
                         &t_len);
      *s++ = exponent_is_negative ? '-' : '+';

      // Make sure the exponent has at least 2 digits
      while (t_len--) {
        *s++ = *p++;
      }
    } else {
      *s++ = '+';
      *s++ = '0';
    }
  }
  *len = s - buf;
  smart_free(p_orig);
  return (buf);
}
Esempio n. 25
0
static Array HHVM_FUNCTION(getopt, const String& options,
                                   const Variant& longopts /*=null */) {
  opt_struct *opts, *orig_opts;
  int len = parse_opts(options.data(), options.size(), &opts);

  if (!longopts.isNull()) {
    Array arropts = longopts.toArray();
    int count = arropts.size();

    /* the first <len> slots are filled by the one short ops
     * we now extend our array and jump to the new added structs */
    opts = (opt_struct *)smart_realloc(
      opts, sizeof(opt_struct) * (len + count + 1));
    orig_opts = opts;
    opts += len;

    memset(opts, 0, count * sizeof(opt_struct));

    for (ArrayIter iter(arropts); iter; ++iter) {
      String entry = iter.second().toString();

      opts->need_param = 0;
      opts->opt_name = strdup(entry.data());
      len = strlen(opts->opt_name);
      if ((len > 0) && (opts->opt_name[len - 1] == ':')) {
        opts->need_param++;
        opts->opt_name[len - 1] = '\0';
        if ((len > 1) && (opts->opt_name[len - 2] == ':')) {
          opts->need_param++;
          opts->opt_name[len - 2] = '\0';
        }
      }
      opts->opt_char = 0;
      opts++;
    }
  } else {
    opts = (opt_struct*) smart_realloc(opts, sizeof(opt_struct) * (len + 1));
    orig_opts = opts;
    opts += len;
  }

  /* php_getopt want to identify the last param */
  opts->opt_char   = '-';
  opts->need_param = 0;
  opts->opt_name   = NULL;

  static const StaticString s_argv("argv");
  Array vargv = php_global(s_argv).toArray();
  int argc = vargv.size();
  char **argv = (char **)smart_malloc((argc+1) * sizeof(char*));
  std::vector<String> holders;
  int index = 0;
  for (ArrayIter iter(vargv); iter; ++iter) {
    String arg = iter.second().toString();
    holders.push_back(arg);
    argv[index++] = (char*)arg.data();
  }
  argv[index] = NULL;

  /* after our pointer arithmetic jump back to the first element */
  opts = orig_opts;

  int o;
  char *php_optarg = NULL;
  int php_optind = 1;

  SCOPE_EXIT {
    free_longopts(orig_opts);
    smart_free(orig_opts);
    smart_free(argv);
  };

  Array ret = Array::Create();

  Variant val;
  int optchr = 0;
  int dash = 0; /* have already seen the - */
  char opt[2] = { '\0' };
  char *optname;
  int optname_len = 0;
  int php_optidx;
  while ((o = php_getopt(argc, argv, opts, &php_optarg, &php_optind, 0, 1,
                         optchr, dash, php_optidx))
         != -1) {
    /* Skip unknown arguments. */
    if (o == '?') {
      continue;
    }

    /* Prepare the option character and the argument string. */
    if (o == 0) {
      optname = opts[php_optidx].opt_name;
    } else {
      if (o == 1) {
        o = '-';
      }
      opt[0] = o;
      optname = opt;
    }

    if (php_optarg != NULL) {
      /* keep the arg as binary, since the encoding is not known */
      val = String(php_optarg, CopyString);
    } else {
      val = false;
    }

    /* Add this option / argument pair to the result hash. */
    optname_len = strlen(optname);
    if (!(optname_len > 1 && optname[0] == '0') &&
        is_numeric_string(optname, optname_len, NULL, NULL, 0) ==
        KindOfInt64) {
      /* numeric string */
      int optname_int = atoi(optname);
      if (ret.exists(optname_int)) {
        Variant &e = ret.lvalAt(optname_int);
        if (!e.isArray()) {
          ret.set(optname_int, make_packed_array(e, val));
        } else {
          e.toArrRef().append(val);
        }
      } else {
        ret.set(optname_int, val);
      }
    } else {
      /* other strings */
      String key(optname, strlen(optname), CopyString);
      if (ret.exists(key)) {
        Variant &e = ret.lvalAt(key);
        if (!e.isArray()) {
          ret.set(key, make_packed_array(e, val));
        } else {
          e.toArrRef().append(val);
        }
      } else {
        ret.set(key, val);
      }
    }

    php_optarg = NULL;
  }

  return ret;
}
Esempio n. 26
0
void
free_null(struct smart_alloc *sa)
{
    smart_free(sa, NULL);
}
Esempio n. 27
0
ZendCustomElement::~ZendCustomElement() {
  if (m_destructor) m_destructor(m_data);
  smart_free(m_data);
}
Esempio n. 28
0
static double collator_u_strtod(const UChar *nptr, UChar **endptr) {
  const UChar *u = nptr, *nstart;
  UChar c = *u;
  int any = 0;

  while (u_isspace(c)) {
    c = *++u;
  }
  nstart = u;

  if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) {
    c = *++u;
  }

  while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
    any = 1;
    c = *++u;
  }

  if (c == 0x2E /*'.'*/) {
    c = *++u;
    while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
      any = 1;
      c = *++u;
    }
  }

  if ((c == 0x65 /*'e'*/ || c == 0x45 /*'E'*/) && any) {
    const UChar *e = u;
    int any_exp = 0;

    c = *++u;
    if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) {
      c = *++u;
    }

    while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
      any_exp = 1;
      c = *++u;
    }

    if (!any_exp) {
      u = e;
    }
  }

  if (any) {
    char buf[64], *numbuf, *bufpos;
    int length = u - nstart;
    double value;

    if (length < (int)sizeof(buf)) {
      numbuf = buf;
    } else {
      numbuf = (char *) smart_malloc(length + 1);
    }

    bufpos = numbuf;

    while (nstart < u) {
      *bufpos++ = (char) *nstart++;
    }

    *bufpos = '\0';
    value = zend_strtod(numbuf, nullptr);

    if (numbuf != buf) {
      smart_free(numbuf);
    }

    if (endptr != nullptr) {
      *endptr = (UChar *)u;
    }

    return value;
  }

  if (endptr != nullptr) {
    *endptr = (UChar *)nptr;
  }

  return 0;
}
Esempio n. 29
0
static void php_xml_free_wrapper(void *ptr) {
  if (ptr) {
    smart_free(ptr);
  }
}
Esempio n. 30
0
static bool HHVM_METHOD(Collator, sortWithSortKeys, VRefParam arr) {
  FETCH_COL(data, this_, false);
  data->clearError();

  if (!arr.isArray()) {
    return true;
  }

  Array hash = arr.toArray();
  if (hash.size() == 0) {
    return true;
  }

  // Preallocate sort keys buffer
  size_t sortKeysOffset = 0;
  size_t sortKeysLength = DEF_SORT_KEYS_BUF_SIZE;
  char*  sortKeys = (char*)smart_malloc(sortKeysLength);
  if (!sortKeys) {
    throw Exception("Out of memory");
  }
  SCOPE_EXIT{ smart_free(sortKeys); };

  // Preallocate index buffer
  size_t sortIndexPos = 0;
  size_t sortIndexLength = DEF_SORT_KEYS_INDX_BUF_SIZE;
  auto   sortIndex = (collator_sort_key_index_t*)smart_malloc(
                  sortIndexLength * sizeof(collator_sort_key_index_t));
  if (!sortIndex) {
    throw Exception("Out of memory");
  }
  SCOPE_EXIT{ smart_free(sortIndex); };

  // Translate input hash to sortable index
  auto pos_limit = hash->iter_end();
  for (ssize_t pos = hash->iter_begin(); pos != pos_limit;
       pos = hash->iter_advance(pos)) {
    Variant val(hash->getValue(pos));

    // Convert to UTF16
    icu::UnicodeString strval;
    if (val.isString()) {
      UErrorCode error = U_ZERO_ERROR;
      strval = u16(val.toString(), error);
      if (U_FAILURE(error)) {
        return false;
      }
     }

    // Generate sort key
    int sortkey_len =
      ucol_getSortKey(data->collator(),
                      strval.getBuffer(), strval.length(),
                      (uint8_t*)(sortKeys + sortKeysOffset),
                      sortKeysLength - sortKeysOffset);

    // Check for key buffer overflow
    if (sortkey_len > (sortKeysLength - sortKeysOffset)) {
      int32_t inc = (sortkey_len > DEF_SORT_KEYS_BUF_INCREMENT)
                  ?  sortkey_len : DEF_SORT_KEYS_BUF_INCREMENT;
      sortKeysLength += inc;
      sortKeys = (char*)smart_realloc(sortKeys, sortKeysLength);
      if (!sortKeys) {
        throw Exception("Out of memory");
      }
      sortkey_len =
        ucol_getSortKey(data->collator(),
                        strval.getBuffer(), strval.length(),
                        (uint8_t*)(sortKeys + sortKeysOffset),
                        sortKeysLength - sortKeysOffset);
      assert(sortkey_len <= (sortKeysLength - sortKeysOffset));
    }

    // Check for index buffer overflow
    if ((sortIndexPos + 1) > sortIndexLength) {
      sortIndexLength += DEF_SORT_KEYS_INDX_BUF_INCREMENT;
      sortIndex = (collator_sort_key_index_t*)smart_realloc(sortIndex,
                      sortIndexLength * sizeof(collator_sort_key_index_t));
      if (!sortIndex) {
        throw Exception("Out of memory");
      }
    }

    // Initially store offset into buffer, update later to deal with reallocs
    sortIndex[sortIndexPos].key = (char*)sortKeysOffset;
    sortKeysOffset += sortkey_len;

    sortIndex[sortIndexPos].valPos = pos;
    ++sortIndexPos;
  }

  // Update keys to location in realloc'd buffer
  for (int i = 0; i < sortIndexPos; ++i) {
    sortIndex[i].key = sortKeys + (ptrdiff_t)sortIndex[i].key;
  }

  zend_qsort(sortIndex, sortIndexPos,
             sizeof(collator_sort_key_index_t),
             collator_cmp_sort_keys, nullptr);

  Array ret = Array::Create();
  for (int i = 0; i < sortIndexPos; ++i) {
    ret.append(hash->getValue(sortIndex[i].valPos));
  }
  arr = ret;
  return true;
}