bool HHVM_FUNCTION(imageconvolution, const Resource& image,
    const Array& matrix, double div, double offset) {
  gdImagePtr im_src = cast<Image>(image)->get();
  if (!im_src) return false;
  int nelem = matrix.size();
  int i, j;
  float mtx[3][3] = {{0,0,0}, {0,0,0}, {0,0,0}};
  Variant v;
  Array row;

  if (nelem != 3) {
    raise_warning("You must have 3x3 array");
    return false;
  }
  for (i=0; i<3; i++) {
    if (matrix.exists(i) && (v = matrix[i]).isArray()) {
      if ((row = v.toArray()).size() != 3) {
        raise_warning("You must have 3x3 array");
        return false;
      }

      for (j=0; j<3; j++) {
        if (row.exists(j)) {
          mtx[i][j] = row[j].toDouble();
        } else {
          raise_warning("You must have a 3x3 matrix");
          return false;
        }
      }
    }
  }
  if (hphp_gdImageConvolution(im_src, mtx, div, offset)) {
    return true;
  } else {
    return false;
  }
}


Variant HHVM_FUNCTION(imagecreatetruecolor, int64_t width, int64_t height) {
  gdImagePtr im;

  if (width <= 0 || height <= 0 || width >= INT_MAX || height >= INT_MAX) {
    raise_warning("Invalid image dimensions");
    return false;
  }

  im = gdImageCreateTrueColor(width, height);

  if (!im) {
    return false;
  }
  return Variant(req::make<Image>(im));
}

security fixes

https://hhvm.com/blog/2021/02/25/security-update.html

#define IMAGE_TYPE_GIF 1

#define IMAGE_TYPE_JPEG 2

#define IMAGE_TYPE_PNG 4

#define IMAGE_FILTER_NEGATE         0

#define IMAGE_FILTER_GRAYSCALE      1

#define IMAGE_FILTER_COLORIZE       4

#define IMAGE_FILTER_MAX            11

#define IMAGE_FILTER_MAX_ARGS       6

#define IMAGE_TYPE_WBMP 8

#define IMAGE_TYPE_XPM 16

#define IMAGE_FILTER_BRIGHTNESS     2

#define IMAGE_FILTER_CONTRAST       3

#define IMAGE_FILTER_EDGEDETECT     5

#define IMAGE_FILTER_EMBOSS         6

#define IMAGE_FILTER_SELECTIVE_BLUR 8

#define IMAGE_FILTER_SMOOTH         10

#define IMAGE_FILTER_PIXELATE       11

#define IMAGE_FILTER_MEAN_REMOVAL   9

#define IMAGE_FILTER_GAUSSIAN_BLUR  7


php_image_filter_edgedetect(gdImagePtr im, int /*arg1*/ /* = 0 */,
                            int /*arg2*/ /* = 0 */, int /*arg3*/ /* = 0 */,
                            int /*arg4*/ /* = 0 */) {
  return gdImageEdgeDetectQuick(im) == 1;
}


_php_image_output(const Resource& image, const String& filename, int quality,
                  int type, int image_type, char* /*tn*/, void (*func_p)()) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  req::ptr<File> file;
  FILE *fp;
  int q = quality, i, t = type;

  /* The quality parameter for Wbmp stands for the threshold when
     called from image2wbmp() */
  /* When called from imagewbmp() the quality parameter stands
     for the foreground color. Default: black. */
  /* The quality parameter for gd2 stands for chunk size */

  if (!filename.empty()) {
    file = php_open_plain_file(filename, "wb", &fp);
    if (!file) {
      raise_warning("Unable to open '%s' for writing", filename.c_str());
      return false;
    }

    switch (image_type) {
    case PHP_GDIMG_CONVERT_WBM:
      if (q == -1) {
        q = 0;
      } else if (q < 0 || q > 255) {
        raise_warning("Invalid threshold value '%d'. "
                        "It must be between 0 and 255", q);
        q = 0;
      }
      gdImageWBMP(im, q, fp);
      break;
    case PHP_GDIMG_TYPE_JPG: {
      // gdImageJpeg
      ((void(*)(gdImagePtr, FILE *, int))(func_p))(im, fp, q);
      break;
    }
    case PHP_GDIMG_TYPE_WBM:
      for (i = 0; i < gdImageColorsTotal(im); i++) {
        if (gdImageRed(im, i) == 0) break;
      }
      // gdImageWBMP
      ((void(*)(gdImagePtr, int, FILE *))(func_p))(im, i, fp);
      break;
    case PHP_GDIMG_TYPE_GD:
      if (im->trueColor) {
        gdImageTrueColorToPalette(im,1,256);
      }
      // gdImageGd
      ((void(*)(gdImagePtr, FILE *))(func_p))(im, fp);
      break;
    case PHP_GDIMG_TYPE_GD2:
      if (q == -1) {
        q = 128;
      }
      // gdImageGd2
      ((void(*)(gdImagePtr, FILE *, int, int))(func_p))(im, fp, q, t);
      break;
    default:
      if (q == -1) {
        q = 128;
      }
      ((void(*)(gdImagePtr, FILE *, int, int))(func_p))(im, fp, q, t);
      break;
    }
    fflush(fp);
    file->close();
  } else {
    int   b;
    FILE *tmp;
    char  buf[4096];
    char path[PATH_MAX];

    // open a temporary file
    snprintf(path, sizeof(path), "/tmp/XXXXXX");
    int fd = mkstemp(path);
    if (fd == -1 || (tmp = fdopen(fd, "r+b")) == nullptr) {
      if (fd != -1) close(fd);
      raise_warning("Unable to open temporary file");
      return false;
    }

    switch (image_type) {
    case PHP_GDIMG_CONVERT_WBM:
       if (q == -1) {
          q = 0;
        } else if (q < 0 || q > 255) {
          raise_warning("Invalid threshold value '%d'. "
                          "It must be between 0 and 255", q);
         q = 0;
        }
      gdImageWBMP(im, q, tmp);
      break;
    case PHP_GDIMG_TYPE_JPG:
      ((void(*)(gdImagePtr, FILE *, int))(func_p))(im, tmp, q);
      break;
    case PHP_GDIMG_TYPE_WBM:
      for (i = 0; i < gdImageColorsTotal(im); i++) {
        if (gdImageRed(im, i) == 0) {
          break;
        }
      }
      ((void(*)(gdImagePtr, int, FILE *))(func_p))(im, q, tmp);
      break;
    case PHP_GDIMG_TYPE_GD:
      if (im->trueColor) {
        gdImageTrueColorToPalette(im,1,256);
      }
      ((void(*)(gdImagePtr, FILE *))(func_p))(im, tmp);
      break;
    case PHP_GDIMG_TYPE_GD2:
      if (q == -1) {
        q = 128;
      }
      ((void(*)(gdImagePtr, FILE *, int, int))(func_p))(im, tmp, q, t);
      break;
    default:
      ((void(*)(gdImagePtr, FILE *))(func_p))(im, tmp);
      break;
    }

    fseek(tmp, 0, SEEK_SET);

    while ((b = fread(buf, 1, sizeof(buf), tmp)) > 0) {
      g_context->write(buf, b);
    }

    fclose(tmp);
    /* make sure that the temporary file is removed */
    unlink((const char *)path);
  }
  return true;
}


static char *php_strdup_impl(const char* s
#ifdef IM_MEMORY_CHECK
, int ln
#endif
  ) {
#ifdef IM_MEMORY_CHECK
  return php_strndup_impl(s, strlen(s), ln);
#else
  return php_strndup_impl(s, strlen(s));
#endif
}


inline bool StringData::isImmutable() const {
  return !isRefCounted() || isProxy();
}

inline folly::StringPiece

inline folly::MutableStringPiece

struct string_data_hash {
  size_t operator()(const StringData *s) const {
    return s->hash();
  }

struct string_data_same {
  bool operator()(const StringData *s1, const StringData *s2) const {
    assertx(s1 && s2);
    return s1->same(s2);
  }

struct string_data_eq_same {
  bool operator()(const StringData* a, const StringData* b) const {
    return a == b || a->same(b);
  }

struct string_data_isame {
  bool operator()(const StringData *s1, const StringData *s2) const {
    assertx(s1 && s2);
    return s1->isame(s2);
  }

struct string_data_lt {
  bool operator()(const StringData *s1, const StringData *s2) const {
    int len1 = s1->size();
    int len2 = s2->size();
    if (len1 < len2) {
      return (len1 == 0) || (memcmp(s1->data(), s2->data(), len1) <= 0);
    } else

struct string_data_lti {
  bool operator()(const StringData *s1, const StringData *s2) const {
    return bstrcasecmp(s1->data(), s1->size(), s2->data(), s2->size()) < 0;
  }


static Variant substr_replace(const Variant& str, const Variant& replacement,
                              const Variant& start, const Variant& length) {
  if (!str.isArray()) {
    String repl;
    if (replacement.isArray()) {
      repl = replacement.asCArrRef()[0].toString();
    } else {
      repl = replacement.toString();
    }
    if (start.isArray()) {
      if (!length.isArray()) {
        raise_invalid_argument_warning("start and length should be of same type - "
                               "numerical or array");
        return str;
      }
      Array startArr = start.toArray();
      Array lengthArr = length.toArray();
      if (startArr.size() != lengthArr.size()) {
        raise_invalid_argument_warning("start and length: (different item count)");
        return str;
      }
      raise_invalid_argument_warning("start and length as arrays not implemented");
      return str;
    }
    return string_replace(str.toString(), start.toInt32(), length.toInt32(),
                          repl);
  }

  // 'start' and 'length' can be arrays (in which case we step through them in
  // sync with stepping through 'str'), or not arrays, in which case we convert
  // them to ints and always use those.
  Array ret;
  Array strArr = str.toArray();
  folly::Optional<int> opStart;
  folly::Optional<int> opLength;
  if (!start.isArray()) {
    opStart = start.toInt32();
  }
  if (!length.isArray()) {
    opLength = length.toInt32();
  }

  Array startArr = start.toArray();
  Array lengthArr = length.toArray();
  ArrayIter startIter(startArr);
  ArrayIter lengthIter(lengthArr);

  if (replacement.isArray()) {
    Array replArr = replacement.toArray();
    ArrayIter replIter(replArr);
    for (ArrayIter iter(strArr); iter; ++iter) {
      auto str = iter.second().toString();
      // If 'start' or 'length' are arrays and we've gone past the end, default
      // to 0 for start and the length of the input string for length.
      int nStart =
        (opStart.has_value()
         ? opStart.value()
         : (startIter ? startIter.second().toInt32() : 0));
      int nLength =
        (opLength.has_value()
         ? opLength.value()
         : (lengthIter ? lengthIter.second().toInt32() : str.length()));
      if (startIter) ++startIter;
      if (lengthIter) ++lengthIter;

      String repl;
      if (replIter) {
        repl = replIter.second().toString();
        ++replIter;
      } else {
        repl = empty_string();
      }
      auto s2 = string_replace(str, nStart, nLength, repl);
      ret.append(s2);
    }
  } else {
    String repl = replacement.toString();
    for (ArrayIter iter(strArr); iter; ++iter) {
      auto str = iter.second().toString();
      int nStart =
        (opStart.has_value()
         ? opStart.value()
         : (startIter ? startIter.second().toInt32() : 0));
      int nLength =
        (opLength.has_value()
         ? opLength.value()
         : (lengthIter ? lengthIter.second().toInt32() : str.length()));
      if (startIter) ++startIter;
      if (lengthIter) ++lengthIter;

      auto s2 = string_replace(str, nStart, nLength, repl);
      ret.append(s2);
    }
  }
  return ret;
}

/* No explicit global definitions found. */


char* php_crypt_r(const char* key, const char* salt) {
  if (salt[0] == '$' && salt[1] == '1' && salt[2] == '$') {
    char output[MD5_HASH_MAX_LEN], *out;

    out = php_md5_crypt_r(key, salt, output);
    return out ? strdup(out) : nullptr;
  } else if (salt[0] == '$' && salt[1] == '6' && salt[2] == '$') {
    char output[PHP_MAX_SALT_LEN + 1];

    char* crypt_res = php_sha512_crypt_r(key, salt, output, PHP_MAX_SALT_LEN);
    if (!crypt_res) {
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return nullptr;
    } else {
      char* result = strdup(output);
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return result;
    }
  } else if (salt[0] == '$' && salt[1] == '5' && salt[2] == '$') {
    char output[PHP_MAX_SALT_LEN + 1];

    char* crypt_res = php_sha256_crypt_r(key, salt, output, PHP_MAX_SALT_LEN);
    if (!crypt_res) {
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return nullptr;
    } else {
      char* result = strdup(output);
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return result;
    }
  } else if (
    salt[0] == '$' &&
    salt[1] == '2' &&
    salt[3] == '$') {
    char output[PHP_MAX_SALT_LEN + 1];

    memset(output, 0, PHP_MAX_SALT_LEN + 1);

    char* crypt_res = php_crypt_blowfish_rn(key, salt, output, sizeof(output));
    if (!crypt_res) {
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return nullptr;
    } else {
      char* result = strdup(output);
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return result;
    }
  } else if (salt[0] == '*' && (salt[1] == '0' || salt[1] == '1')) {
    return nullptr;
  } else {
    struct php_crypt_extended_data buffer;
    /* DES Fallback */
    memset(&buffer, 0, sizeof(buffer));
    _crypt_extended_init_r();

    char* crypt_res = _crypt_extended_r(key, salt, &buffer);
    if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) {
      return nullptr;
    } else {
      return strdup(crypt_res);
    }
  }
}


static String preg_do_repl_func(const Variant& function, const String& subject,
                                int* offsets, const char* const* subpat_names,
                                int count) {
  Array subpats = Array::CreateDArray();
  for (int i = 0; i < count; i++) {
    auto off1 = offsets[i<<1];
    auto off2 = offsets[(i<<1)+1];
    auto sub = subject.substr(off1, off2 - off1);

    if (subpat_names[i]) {
      subpats.set(String(subpat_names[i]), sub);
    }
    subpats.set(i, sub);
  }

  return vm_call_user_func(function, make_varray(subpats)).toString();
}


void string_translate(char *str, int len, const char *str_from,
                      const char *str_to, int trlen) {
  int i;
  unsigned char xlat[256];

  if ((trlen < 1) || (len < 1)) {
    return;
  }

  for (i = 0; i < 256; xlat[i] = i, i++);
  for (i = 0; i < trlen; i++) {
    xlat[(unsigned char) str_from[i]] = str_to[i];
  }

  for (i = 0; i < len; i++) {
    str[i] = xlat[(unsigned char) str[i]];
  }
}


bool HHVM_FUNCTION(is_numeric, const Variant& v) {
  return v.isNumeric(true);
}


TypedValue HHVM_FUNCTION(str_replace,
                         const Variant& search, const Variant& replace,
                         const Variant& subject) {
  int64_t count;
  return tvReturn(str_replace(search, replace, subject, count));
}


TypedValue HHVM_FUNCTION(str_split, const String& str, int64_t split_length) {
  return tvReturn(StringUtil::Split(str, split_length));
}


static struct gfxinfo *php_handle_iff(const req::ptr<File>& stream) {
  struct gfxinfo * result;
  char *a;
  int chunkId;
  int size;
  short width, height, bits;

  String str = stream->read(8);
  if (str.length() != 8) return nullptr;
  a = (char *)str.c_str();
  if (strncmp(a+4, "ILBM", 4) && strncmp(a+4, "PBM ", 4)) {
    return nullptr;
  }

  /* loop chunks to find BMHD chunk */
  do {
    str = stream->read(8);
    if (str.length() != 8) return nullptr;
    a = (char *)str.c_str();
    chunkId = php_ifd_get32s(a+0, 1);
    size = php_ifd_get32s(a+4, 1);
    if (size < 0) return nullptr;
    if ((size & 1) == 1) {
      size++;
    }
    if (chunkId == 0x424d4844) { /* BMHD chunk */
      if (size < 9) return nullptr;
      str = stream->read(9);
      if (str.length() != 9) return nullptr;
      a = (char *)str.c_str();
      width = php_ifd_get16s(a+0, 1);
      height = php_ifd_get16s(a+2, 1);
      bits = a[8] & 0xff;
      if (width > 0 && height > 0 && bits > 0 && bits < 33) {
        result = (struct gfxinfo *)IM_CALLOC(1, sizeof(struct gfxinfo));
        CHECK_ALLOC_R(result, sizeof (struct gfxinfo), nullptr);
        result->width = width;
        result->height = height;
        result->bits = bits;
        result->channels = 0;
        return result;
      }
    } else {
      if (!stream->seek(size, SEEK_CUR)) return nullptr;
    }
  } while (1);
}


Variant HHVM_FUNCTION(imageftbbox, double size, double angle,
    const String& font_file, const String& text,
    const Array& extrainfo /*=[] */) {
  return php_imagettftext_common(TTFTEXT_BBOX, 1,
                                 size, angle, font_file, text, extrainfo);
}


bool HHVM_FUNCTION(imageline, const Resource& image,
    int64_t x1, int64_t y1, int64_t x2, int64_t y2, int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  color = SetupAntiAliasedColor(im, color);
  gdImageLine(im, x1, y1, x2, y2, color);
  return true;
}


Variant HHVM_FUNCTION(HH_first_key, const Variant& v) {
  // 1. array, vec, dict, keyset
  if (v.isArray() || v.isClsMeth()) {
    auto arr = v.toArray();
    if (arr->empty()) {
      return init_null();
    }
    return arr->getKey(arr->iter_begin());
  }

  if (v.isObject()) {
    auto obj = v.asCObjRef();
    // 2. collection
    if (obj->isCollection()) {
      // Pair
      if (obj->collectionType() == CollectionType::Pair) {
        return Variant::wrap(make_tv<KindOfInt64>(0));
      }

      // Vector, Map, Set, and Imm variants
      auto arr = collections::asArray(obj.get());
      if (arr->empty()) {
        return init_null();
      }
      return arr->getKey(arr->iter_begin());
    }
  }
  SystemLib::throwInvalidArgumentExceptionObject(
    "Argument 1 passed to HH\\Lib\\_Private\\Native\\first_key() "
    "must be a Container");
}


String stringForEach(uint32_t len, const String& str, Op action) {
  String ret = mutate ? str : String(len, ReserveString);

  auto srcSlice = str.slice();

  const char* src = srcSlice.begin();
  const char* end = srcSlice.end();

  char* dst = ret.mutableData();

  for (; src != end; ++src, ++dst) {
    *dst = action(*src);
  }

  if (!mutate) ret.setSize(len);
  return ret;
}


Variant HHVM_FUNCTION(imagecolorallocatealpha,
    const Resource& image,
    int64_t red, int64_t green, int64_t blue, int64_t alpha) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  int ct = gdImageColorAllocateAlpha(im, red, green, blue, alpha);
  if (ct < 0) {
    return false;
  }
  return ct;
}


static int exif_scan_JPEG_header(image_info_type *ImageInfo) {
  int section, sn;
  int marker = 0, last_marker = M_PSEUDO, comment_correction=1;
  int ll, lh;
  unsigned char *Data;
  size_t fpos, size, got, itemlen;
  jpeg_sof_info  sof_info;

  for(section=0;;section++) {
    // get marker byte, swallowing possible padding
    // some software does not count the length bytes of COM section
    // one company doing so is very much envolved in JPEG...
    // so we accept too
    if (last_marker==M_COM && comment_correction) {
      comment_correction = 2;
    }
    do {
      if ((marker = ImageInfo->infile->getc()) == EOF) {
        raise_warning("File structure corrupted");
        return 0;
      }
      if (last_marker==M_COM && comment_correction>0) {
        if (marker!=0xFF) {
          marker = 0xff;
          comment_correction--;
        } else  {
          last_marker = M_PSEUDO; /* stop skipping 0 for M_COM */
        }
      }
    } while (marker == 0xff);
    if (last_marker==M_COM && !comment_correction) {
      raise_notice("Image has corrupt COM section: some software set "
                   "wrong length information");
    }
    if (last_marker==M_COM && comment_correction)
      return M_EOI; /* ah illegal: char after COM section not 0xFF */

    fpos = ImageInfo->infile->tell();

    if (marker == 0xff) {
      // 0xff is legal padding, but if we get that many, something's wrong.
      raise_warning("To many padding bytes");
      return 0;
    }

    /* Read the length of the section. */

    if ((lh = ImageInfo->infile->getc()) == EOF) {
      raise_warning("File structure corrupted");
      return 0;
    }

    if ((ll = ImageInfo->infile->getc()) == EOF) {
      raise_warning("File structure corrupted");
      return 0;
    }

    itemlen = (lh << 8) | ll;

    if (itemlen < 2) {
      raise_warning("File structure corrupted");
      return 0;
    }

    sn = exif_file_sections_add(ImageInfo, marker, itemlen+1, nullptr);
    if (sn == -1) return 0;
    Data = ImageInfo->file.list[sn].data;

    /* Store first two pre-read bytes. */
    Data[0] = (unsigned char)lh;
    Data[1] = (unsigned char)ll;

    String str = ImageInfo->infile->read(itemlen-2);
    got = str.length();
    if (got != itemlen-2) {
      raise_warning("Error reading from file: "
                      "got=x%04lX(=%lu) != itemlen-2=x%04lX(=%lu)",
                      got, got, itemlen-2, itemlen-2);
      return 0;
    }
    memcpy(Data+2, str.c_str(), got);
    switch(marker) {
      case M_SOS:   /* stop before hitting compressed data  */
        // If reading entire image is requested, read the rest of the data.
        if (ImageInfo->read_all) {
          /* Determine how much file is left. */
          fpos = ImageInfo->infile->tell();
          size = ImageInfo->FileSize - fpos;
          sn = exif_file_sections_add(ImageInfo, M_PSEUDO, size, nullptr);
          if (sn == -1) return 0;
          Data = ImageInfo->file.list[sn].data;
          str = ImageInfo->infile->read(size);
          got = str.length();
          if (got != size) {
            raise_warning("Unexpected end of file reached");
            return 0;
          }
          memcpy(Data, str.c_str(), got);
        }
        return 1;

      case M_EOI:   /* in case it's a tables-only JPEG stream */
        raise_warning("No image in jpeg!");
        return (ImageInfo->sections_found&(~FOUND_COMPUTED)) ? 1 : 0;

      case M_COM: /* Comment section */
        exif_process_COM(ImageInfo, (char *)Data, itemlen);
        break;

      case M_EXIF:
        if (!(ImageInfo->sections_found&FOUND_IFD0)) {
          /*ImageInfo->sections_found |= FOUND_EXIF;*/
          /* Seen files from some 'U-lead' software with Vivitar scanner
             that uses marker 31 later in the file (no clue what for!) */
          exif_process_APP1(ImageInfo, (char *)Data, itemlen, fpos);
        }
        break;

      case M_APP12:
        exif_process_APP12(ImageInfo, (char *)Data, itemlen);
        break;

      case M_SOF0:
      case M_SOF1:
      case M_SOF2:
      case M_SOF3:
      case M_SOF5:
      case M_SOF6:
      case M_SOF7:
      case M_SOF9:
      case M_SOF10:
      case M_SOF11:
      case M_SOF13:
      case M_SOF14:
      case M_SOF15:
        if ((itemlen - 2) < 6) {
          return 0;
        }

        exif_process_SOFn(Data, marker, &sof_info);
        ImageInfo->Width  = sof_info.width;
        ImageInfo->Height = sof_info.height;
        if (sof_info.num_components == 3) {
          ImageInfo->IsColor = 1;
        } else {
          ImageInfo->IsColor = 0;
        }
        break;
      default:
        /* skip any other marker silently. */
        break;
    }

    /* keep track of last marker */
    last_marker = marker;
  }
  return 1;
}


Variant HHVM_FUNCTION(strtr,
                      const String& str,
                      const Variant& from,
                      const Variant& to /* = uninit_variant */) {
  if (str.empty()) {
    return str;
  }

  if (!to.isNull()) {
    return StringUtil::Translate(str, from.toString(), to.toString());
  }

  if (!from.isArray()) {
    raise_invalid_argument_warning("2nd argument: (not array)");
    return false;
  }

  int maxlen = 0;
  int minlen = -1;
  Array arr = from.toArray();

  if (arr.empty()) {
    // Nothing to translate
    return str;
  }

  for (ArrayIter iter(arr); iter; ++iter) {
    auto const search = iter.first().toString();
    auto const len = search.size();
    if (len < 1) return false;
    if (maxlen < len) maxlen = len;
    if (minlen == -1 || minlen > len) minlen = len;
  }

  if (arr.size() <= kBitsPerQword && maxlen <= 16) {
    return strtr_fast(str, arr, minlen, maxlen);
  }

  if (arr.size() < 1000) {
    const WuManberReplacement replacer(arr, minlen);
    return replacer.translate(str);
  }

  // wu manber cost is mostly in preprocessing the patterns into
  // tables.  this hash is much faster than initializing and most
  // codebases likely only have a few constant sets with more than
  // a thousand patterns.
  int64_t hash = 0;
  for (ArrayIter iter(arr); iter; ++iter) {
    String pattern = iter.first().toString();
    String replacement = iter.second().toString();
    hash = murmur_hash_64A(pattern.data(), pattern.size(), hash);
    hash = murmur_hash_64A(replacement.data(), replacement.size(), hash);
  }
  WuManberCache::ConstAccessor got;
  WuManberPtr replacer;
  if (wuManberCache.find(got, hash)) {
    replacer = *got;
  } else {
    replacer.reset(new WuManberReplacement(arr, minlen));
    wuManberCache.insert(hash, replacer);
  }
  return replacer->translate(str);
}


  uint32_t capacity() const {
    return m_px->capacity(); // intentionally skip nullptr check
  }

String(const StringData*) = delete;

String(const char *s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, mode) : nullptr, NoIncRef{}) {}

  // attach to binary malloc'ed string
  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // make copy of binary binary string
  String(const char *s, size_t length, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // force a copy of a String
  String(const String& s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s.c_str(), s.size(), mode)
                          : nullptr, NoIncRef{}) {}

  // make an empty string with cap reserve bytes, plus 1 for '\0'
  String(size_t cap, ReserveStringMode /*mode*/)
      : m_str(StringData::Make(cap), NoIncRef{}) {}

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});

String substr(int start, int length = StringData::MaxSize) const {
    return String::attach(
      m_str ? m_str->substr(start, length) : staticEmptyString());
  }

  /**
   * Find a character or a substring and return its position. "pos" has to be
   * within current string and it's the start point for searching.
   */

static const int npos = -1;

int find(char ch, int pos = 0, bool caseSensitive = true) const;

int find(const char *s, int pos = 0, bool caseSensitive = true) const;

int find(const String& s, int pos = 0, bool caseSensitive = true) const;

int rfind(char ch, int pos = 0, bool caseSensitive = true) const;

int rfind(const char *s, int pos = 0, bool caseSensitive = true) const;

int rfind(const String& s, int pos = 0, bool caseSensitive = true) const;

String  operator |  (const String& v) const = delete;

String  operator &  (const String& v) const = delete;

String  operator ^  (const String& v) const = delete;

explicit operator bool() const { return (bool)m_str;

bool operator == (const char* v) const = delete;

bool operator != (const char* v) const = delete;

bool operator >= (const char* v) const = delete;

bool operator <= (const char* v) const = delete;

bool operator >  (const char* v) const = delete;

bool operator <  (const char* v) const = delete;

bool operator >= (const String& v) const = delete;

bool operator <= (const String& v) const = delete;

bool operator == (const Variant& v) const = delete;

bool operator != (const Variant& v) const = delete;

bool operator >= (const Variant& v) const = delete;

bool operator <= (const Variant& v) const = delete;

bool operator >  (const Variant& v) const = delete;

bool operator <  (const Variant& v) const = delete;

String rval(const Array& key) const = delete;

String rval(const Object& key) const = delete;

String rval(const Variant& key) const = delete;

extern const String null_string;


static void exif_process_TIFF_in_JPEG(image_info_type *ImageInfo,
                                      char *CharBuf, size_t length,
                                      size_t displacement) {
  char *end = CharBuf + length;
  unsigned exif_value_2a, offset_of_ifd;

  /* set the thumbnail stuff to nothing so we can test to see if
     they get set up */
  CHECK_BUFFER(CharBuf, end, 2);
  if (memcmp(CharBuf, "II", 2) == 0) {
    ImageInfo->motorola_intel = 0;
  } else if (memcmp(CharBuf, "MM", 2) == 0) {
    ImageInfo->motorola_intel = 1;
  } else {
    raise_warning("Invalid TIFF a lignment marker");
    return;
  }

  /* Check the next two values for correctness. */
  CHECK_BUFFER(CharBuf+4, end, 4);
  exif_value_2a = php_ifd_get16u(CharBuf+2, ImageInfo->motorola_intel);
  offset_of_ifd = php_ifd_get32u(CharBuf+4, ImageInfo->motorola_intel);
  if ( exif_value_2a != 0x2a || offset_of_ifd < 0x08) {
    raise_warning("Invalid TIFF start (1)");
    return;
  }

  if (offset_of_ifd > length) {
    raise_warning("Invalid IFD start");
    return;
  }

  ImageInfo->sections_found |= FOUND_IFD0;
  /* First directory starts at offset 8. Offsets starts at 0. */
  exif_process_IFD_in_JPEG(ImageInfo, CharBuf+offset_of_ifd,
                           CharBuf, end, length/* -14*/, displacement,
                           SECTION_IFD0);

  /* Compute the CCD width, in milimeters. */
  if (ImageInfo->FocalplaneXRes != 0) {
    ImageInfo->CCDWidth = (float)(ImageInfo->ExifImageWidth *
      ImageInfo->FocalplaneUnits / ImageInfo->FocalplaneXRes);
  }
}


TypedValue HHVM_FUNCTION(strncmp,
                         const String& str1,
                         const String& str2,
                         int len) {
  if (len < 0) {
    raise_warning("Length must be greater than or equal to 0");
    return make_tv<KindOfBoolean>(false);
  }
  return tvReturn(string_strncmp(
    str1.data(), str1.size(),
    str2.data(), str2.size(), len
  ));
}


static void php_reg_eprint(int err, regex_t* re) {
  char *buf = nullptr, *message = nullptr;
  size_t len;
  size_t buf_len;

#ifdef REG_ITOA
  /* get the length of the message */
  buf_len = regerror(REG_ITOA | err, re, nullptr, 0);
  if (buf_len) {
    buf = (char *)req::malloc_noptrs(buf_len);
    if (!buf) return; /* fail silently */
    /* finally, get the error message */
    regerror(REG_ITOA | err, re, buf, buf_len);
  }
#else
  buf_len = 0;
#endif
  len = regerror(err, re, nullptr, 0);
  if (len) {
    message = (char *)req::malloc_noptrs(buf_len + len + 2);
    if (!message) {
      return; /* fail silently */
    }
    if (buf_len) {
      snprintf(message, buf_len, "%s: ", buf);
      buf_len += 1; /* so pointer math below works */
    }
    /* drop the message into place */
    regerror(err, re, message + buf_len, len);
    raise_warning("%s", message);
  }
  req::free(buf);
  req::free(message);
}


void end_profiler_frame(Profiler *p,
                        const TypedValue *retval,
                        const char *symbol) {
  p->endFrame(retval, symbol);
}

#define HP_STACK_DELIM        "==>"

#define HP_STACK_DELIM_LEN    (sizeof(HP_STACK_DELIM) - 1)


  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});
  }

String(const StringData*) = delete;

String(const char *s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, mode) : nullptr, NoIncRef{}) {}

  // attach to binary malloc'ed string
  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // make copy of binary binary string
  String(const char *s, size_t length, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // force a copy of a String
  String(const String& s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s.c_str(), s.size(), mode)
                          : nullptr, NoIncRef{}) {}

  // make an empty string with cap reserve bytes, plus 1 for '\0'
  String(size_t cap, ReserveStringMode /*mode*/)
      : m_str(StringData::Make(cap), NoIncRef{}) {}

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});

String substr(int start, int length = StringData::MaxSize) const {
    return String::attach(
      m_str ? m_str->substr(start, length) : staticEmptyString());
  }

  /**
   * Find a character or a substring and return its position. "pos" has to be
   * within current string and it's the start point for searching.
   */

static const int npos = -1;

int find(const String& s, int pos = 0, bool caseSensitive = true) const;

int rfind(const String& s, int pos = 0, bool caseSensitive = true) const;

friend String&& operator

friend String

String  operator |  (const String& v) const = delete;

String  operator &  (const String& v) const = delete;

String  operator ^  (const String& v) const = delete;

explicit operator bool() const { return (bool)m_str;

bool operator >= (const String& v) const = delete;

bool operator <= (const String& v) const = delete;

String rval(const Array& key) const = delete;

String rval(const Object& key) const = delete;

String rval(const Variant& key) const = delete;

extern const String null_string;

struct StrNR {
private:
  StringData *m_px;

public:
  StrNR() : m_px(nullptr) {}
  explicit StrNR(StringData *sd) : m_px(sd) {}
  explicit StrNR(const StringData *sd) : m_px(const_cast<StringData*>(sd)) {}
  explicit StrNR(const String &s) : m_px(s.get()) {} // XXX
  explicit StrNR(const char*) = delete;

  ~StrNR() {
    if (debug) {
      m_px = reinterpret_cast<StringData*>(0xdeadbeeffaceb004);
    }
  }

  /* implicit */ operator

static uint32_t CreateAll();

static bool s_globalInit;

static std::vector<std::tuple<const char*, size_t, StaticString*>>*
    s_registered;

inline String& String::operator=(const StaticString& v)


int64_t HHVM_FUNCTION(crc32,
                      StringArg str) {
  return (uint32_t)string_crc32(str.get()->data(), str.get()->size());
}


bool Profiler::extractStats(phpret& ret, StatsMap& stats, int flags,
                            int64_t MHz)
{
    for (typename StatsMap::const_iterator iter = stats.begin();
         iter != stats.end(); ++iter) {
      returnVals(ret, iter->first, iter->second, flags, MHz);
    }
    return true;
}


php_image_filter_grayscale(gdImagePtr im, int /*arg1*/ /* = 0 */,
                           int /*arg2*/ /* = 0 */, int /*arg3*/ /* = 0 */,
                           int /*arg4*/ /* = 0 */) {
  return gdImageGrayScale(im) == 1;
}


String(const String& str) : m_str(str.m_str) { }

String(const StringData*) = delete;

String(const char *s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, mode) : nullptr, NoIncRef{}) {}

  // attach to binary malloc'ed string
  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // make copy of binary binary string
  String(const char *s, size_t length, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // force a copy of a String
  String(const String& s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s.c_str(), s.size(), mode)
                          : nullptr, NoIncRef{}) {}

  // make an empty string with cap reserve bytes, plus 1 for '\0'
  String(size_t cap, ReserveStringMode /*mode*/)
      : m_str(StringData::Make(cap), NoIncRef{}) {}

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});

String substr(int start, int length = StringData::MaxSize) const {
    return String::attach(
      m_str ? m_str->substr(start, length) : staticEmptyString());
  }

  /**
   * Find a character or a substring and return its position. "pos" has to be
   * within current string and it's the start point for searching.
   */

static const int npos = -1;

int find(char ch, int pos = 0, bool caseSensitive = true) const;

int find(const char *s, int pos = 0, bool caseSensitive = true) const;

int find(const String& s, int pos = 0, bool caseSensitive = true) const;

int rfind(char ch, int pos = 0, bool caseSensitive = true) const;

int rfind(const char *s, int pos = 0, bool caseSensitive = true) const;

int rfind(const String& s, int pos = 0, bool caseSensitive = true) const;

friend String&& operator

friend String

String  operator |  (const String& v) const = delete;

String  operator &  (const String& v) const = delete;

String  operator ^  (const String& v) const = delete;

explicit operator bool() const { return (bool)m_str;

bool operator == (const char* v) const = delete;

bool operator != (const char* v) const = delete;

bool operator >= (const char* v) const = delete;

bool operator <= (const char* v) const = delete;

bool operator >  (const char* v) const = delete;

bool operator <  (const char* v) const = delete;

bool operator >= (const String& v) const = delete;

bool operator <= (const String& v) const = delete;

bool operator == (const Variant& v) const = delete;

bool operator != (const Variant& v) const = delete;

bool operator >= (const Variant& v) const = delete;

bool operator <= (const Variant& v) const = delete;

bool operator >  (const Variant& v) const = delete;

bool operator <  (const Variant& v) const = delete;

String rval(const Array& key) const = delete;

String rval(const Object& key) const = delete;

String rval(const Variant& key) const = delete;

extern const String null_string;


    Accessor& operator=(EntryPtr&& ep) {
      switch (m_kind) {
        case Kind::AccessorKind:
          m_u.accessor.~ConstAccessor();
        case Kind::Empty:
        case Kind::Ptr:
          m_kind = Kind::SmartPtr;
          new (&m_u.smart_ptr) EntryPtr(std::move(ep));
          break;
        case Kind::SmartPtr:
          m_u.smart_ptr = std::move(ep);
          break;
      }
      return *this;
    }


static int exif_process_undefined(char **result, char *value,
                                  size_t byte_count) {
  /* we cannot use strlcpy - here the problem is that we have to copy NUL
   * chars up to byte_count, we also have to add a single NUL character to
   * force end of string.
   */
  if (byte_count) {
    PHP_STRNDUP((*result), value, byte_count); /* NULL @ byte_count!!! */
    if (*result) return byte_count+1;
  }
  return 0;
}


Variant HHVM_FUNCTION(soundex,
                      const String& str) {
  if (str.empty()) return false;
  return string_soundex(str);
}


static void _php_image_output_ctxfree(struct gdIOCtx *ctx) {
  if (ctx) {
    IM_FREE(ctx);
  }
}


String HHVM_FUNCTION(md5,
                     const String& str,
                     bool raw_output /* = false */) {
  return StringUtil::MD5(str, raw_output);
}


static void ito64(char *s, long v, int n) {
  while (--n >= 0) {
    *s++ = itoa64[v&0x3f];
    v >>= 6;
  }
}


bool HHVM_FUNCTION(socket_create_pair,
                   int domain,
                   int type,
                   int protocol,
                   Variant& fd) {
  return socket_create_pair_impl(domain, type, protocol, fd, false);
}

#define PHP_NORMAL_READ 0x0001

#define PHP_BINARY_READ 0x0002


Variant HHVM_FUNCTION(HH_last_key, const Variant& v) {
  // 1. array, vec, dict, keyset
  if (v.isArray() || v.isClsMeth()) {
    auto arr = v.toArray();
    if (arr->empty()) {
      return init_null();
    }
    return arr->getKey(arr->iter_last());
  }

  if (v.isObject()) {
    auto obj = v.asCObjRef();
    // 2. collection
    if (obj->isCollection()) {
      // Pair
      if (obj->collectionType() == CollectionType::Pair) {
        return Variant::wrap(make_tv<KindOfInt64>(1));
      }

      // Vector, Map, Set, and Imm variants
      auto arr = collections::asArray(obj.get());
      if (arr->empty()) {
        return init_null();
      }
      return arr->getKey(arr->iter_last());
    }
  }
  SystemLib::throwInvalidArgumentExceptionObject(
    "Argument 1 passed to HH\\Lib\\_Private\\Native\\last_key() "
    "must be a Container");
}


  template<size_t N> explicit StaticString(const char(&s)[N]) {
    construct(s, N - 1);
  }


StringData* detach() { return m_str.detach(); }

String(const StringData*) = delete;

String(const char *s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, mode) : nullptr, NoIncRef{}) {}

  // attach to binary malloc'ed string
  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // make copy of binary binary string
  String(const char *s, size_t length, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // force a copy of a String
  String(const String& s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s.c_str(), s.size(), mode)
                          : nullptr, NoIncRef{}) {}

  // make an empty string with cap reserve bytes, plus 1 for '\0'
  String(size_t cap, ReserveStringMode /*mode*/)
      : m_str(StringData::Make(cap), NoIncRef{}) {}

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});

String substr(int start, int length = StringData::MaxSize) const {
    return String::attach(
      m_str ? m_str->substr(start, length) : staticEmptyString());
  }

  /**
   * Find a character or a substring and return its position. "pos" has to be
   * within current string and it's the start point for searching.
   */

explicit operator bool() const { return (bool)m_str;

struct StrNR {
private:
  StringData *m_px;

public:
  StrNR() : m_px(nullptr) {}
  explicit StrNR(StringData *sd) : m_px(sd) {}
  explicit StrNR(const StringData *sd) : m_px(const_cast<StringData*>(sd)) {}
  explicit StrNR(const String &s) : m_px(s.get()) {} // XXX
  explicit StrNR(const char*) = delete;

  ~StrNR() {
    if (debug) {
      m_px = reinterpret_cast<StringData*>(0xdeadbeeffaceb004);
    }
  }

  /* implicit */ operator


bool HHVM_FUNCTION(imagecopymergegray, const Resource& dst_im,
    const Resource& src_im,
    int64_t dst_x, int64_t dst_y,
    int64_t src_x, int64_t src_y,
    int64_t src_w, int64_t src_h, int64_t pct) {
  gdImagePtr im_src = cast<Image>(src_im)->get();
  if (!im_src) return false;
  gdImagePtr im_dst = cast<Image>(dst_im)->get();
  if (!im_dst) return false;
  gdImageCopyMergeGray(im_dst, im_src, dst_x, dst_y,
                       src_x, src_y, src_w, src_h, pct);
  return true;
}


  folly::MutableStringPiece reserve(size_t size) {
    if (!m_str) return folly::MutableStringPiece();
    auto const tmp = m_str->reserve(size);
    if (UNLIKELY(tmp != m_str)) {
      m_str = req::ptr<StringData>::attach(tmp);
    }
    return m_str->bufferSlice();
  }


String HHVM_FUNCTION(str_shuffle,
                     const String& str) {
  if (str.size() <= 1) {
    return str;
  }

  String ret(str, CopyString);
  char* buf  = ret.get()->mutableData();
  int left   = ret.size();

  while (--left) {
    int idx = HHVM_FN(rand)(0, left);
    if (idx != left) {
      char temp = buf[left];
      buf[left] = buf[idx];
      buf[idx] = temp;
    }
  }
  return ret;
}


inline strhash_t StringData::hash() const {
  strhash_t h = m_hash & STRHASH_MASK;
  return h ? h : hashHelper();
}


bool HHVM_FUNCTION(imagecopyresampled,
    const Resource& dst_im, const Resource& src_im,
    int64_t dst_x, int64_t dst_y, int64_t src_x, int64_t src_y,
    int64_t dst_w, int64_t dst_h, int64_t src_w, int64_t src_h) {
  gdImagePtr im_src = get_valid_image_resource(src_im);
  if (!im_src) return false;
  gdImagePtr im_dst = get_valid_image_resource(dst_im);
  if (!im_dst) return false;
  gdImageCopyResampled(im_dst, im_src, dst_x, dst_y, src_x, src_y,
                       dst_w, dst_h, src_w, src_h);
  return true;
}


  static bool write_rand_file(const char *file, int egdsocket, int seeded) {
    char buffer[PATH_MAX];

    if (egdsocket || !seeded) {
      /* if we did not manage to read the seed file, we should not write
       * a low-entropy seed file back */
      return false;
    }
    if (file == nullptr) {
      file = RAND_file_name(buffer, sizeof(buffer));
    }
    if (file == nullptr || !RAND_write_file(file)) {
      raise_warning("unable to write random state");
      return false;
    }
    return true;
  }

const int64_t k_OPENSSL_RAW_DATA = 1;

const int64_t k_OPENSSL_ZERO_PADDING = 2;

const int64_t k_OPENSSL_NO_PADDING = 3;

const int64_t k_OPENSSL_PKCS1_OAEP_PADDING = 4;

const int64_t k_OPENSSL_SSLV23_PADDING = 2;

const int64_t k_OPENSSL_PKCS1_PADDING = 1;

static char default_ssl_conf_filename[PATH_MAX];

struct OpenSSLInitializer {
  OpenSSLInitializer() {
    SSL_library_init();
    OpenSSL_add_all_ciphers();
    OpenSSL_add_all_digests();
    OpenSSL_add_all_algorithms();

// CCM ciphers are not added by default, so let's add them!
#if !defined(OPENSSL_NO_AES) && defined(EVP_CIPH_CCM_MODE) && \
    OPENSSL_VERSION_NUMBER < 0x100020000
    EVP_add_cipher(EVP_aes_128_ccm());
    EVP_add_cipher(EVP_aes_192_ccm());
    EVP_add_cipher(EVP_aes_256_ccm());
#endif

    ERR_load_ERR_strings();
    ERR_load_crypto_strings();
    ERR_load_EVP_strings();

    /* Determine default SSL configuration file */
    char *config_filename = getenv("OPENSSL_CONF");
    if (config_filename == nullptr) {
      config_filename = getenv("SSLEAY_CONF");
    }

    /* default to 'openssl.cnf' if no environment variable is set */
    if (config_filename == nullptr) {
      snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename),
               "%s/%s", X509_get_default_cert_area(), "openssl.cnf");
    } else {
      always_assert(
        strlen(config_filename) < sizeof(default_ssl_conf_filename));
      strcpy(default_ssl_conf_filename, config_filename);
    }
  }

  ~OpenSSLInitializer() {
    EVP_cleanup();
  }
}

static OpenSSLInitializer s_openssl_initializer;


Variant HHVM_FUNCTION(imagettftext, const Resource& image,
    const Variant& size, const Variant& angle,
    int64_t x, int64_t y, int64_t color,
    const String& fontfile, const String& text) {
  return php_imagettftext_common(TTFTEXT_DRAW, 0,
                                 image, size.toDouble(), angle.toDouble(),
                                 x, y, color, fontfile, text);
}


bool HHVM_FUNCTION(is_null, const Variant& v) {
  return is_null(v.asTypedValue());
}


String(const char *s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, mode) : nullptr, NoIncRef{}) {}

  // attach to binary malloc'ed string
  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // make copy of binary binary string
  String(const char *s, size_t length, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // force a copy of a String
  String(const String& s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s.c_str(), s.size(), mode)
                          : nullptr, NoIncRef{}) {}

  // make an empty string with cap reserve bytes, plus 1 for '\0'
  String(size_t cap, ReserveStringMode /*mode*/)
      : m_str(StringData::Make(cap), NoIncRef{}) {}

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});

struct StrNR {
private:
  StringData *m_px;

public:
  StrNR() : m_px(nullptr) {}
  explicit StrNR(StringData *sd) : m_px(sd) {}
  explicit StrNR(const StringData *sd) : m_px(const_cast<StringData*>(sd)) {}
  explicit StrNR(const String &s) : m_px(s.get()) {} // XXX
  explicit StrNR(const char*) = delete;

  ~StrNR() {
    if (debug) {
      m_px = reinterpret_cast<StringData*>(0xdeadbeeffaceb004);
    }
  }

  /* implicit */ operator


Variant HHVM_FUNCTION(mcrypt_enc_get_block_size, const Resource& td) {
  auto pm = get_valid_mcrypt_resource(td);
  if (!pm) {
    return false;
  }

  return mcrypt_enc_get_block_size(pm->m_td);
}


int64_t HHVM_FUNCTION(similar_text,
                      const String& first,
                      const String& second,
                      double& percent) {
  int ret = string_similar_text(first.data(), first.size(),
                                second.data(), second.size(), &percent);
  return ret;
}


Variant HHVM_FUNCTION(imagecolorclosest,
    const Resource& image, int64_t red, int64_t green, int64_t blue) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  return gdImageColorClosest(im, red, green, blue);
}


bool HHVM_FUNCTION(imagecharup, const Resource& image,
    int64_t font, int64_t x, int64_t y,
    const String& c, int64_t color) {
  return php_imagechar(image, font, x, y, c, color, 1);
}


  bool generatePrivateKey() {
    assertx(priv_key == nullptr);

    if (priv_key_bits < MIN_KEY_LENGTH) {
      raise_warning("private key length is too short; it needs to be "
                      "at least %d bits, not %d",
                      MIN_KEY_LENGTH, priv_key_bits);
      return false;
    }

    char *randfile = CONF_get_string(req_config, section_name, "RANDFILE");
    int egdsocket, seeded;
    load_rand_file(randfile, &egdsocket, &seeded);

    bool ret = false;
    if ((priv_key = EVP_PKEY_new()) != nullptr) {
      switch (priv_key_type) {
      case OPENSSL_KEYTYPE_RSA:
        if (EVP_PKEY_assign_RSA
            (priv_key, RSA_generate_key(priv_key_bits, 0x10001, nullptr, nullptr))) {
          ret = true;
        }
        break;
#if !defined(NO_DSA) && defined(HAVE_DSA_DEFAULT_METHOD)
      case OPENSSL_KEYTYPE_DSA:
        {
          DSA *dsapar = DSA_generate_parameters(priv_key_bits, nullptr, 0, nullptr,
                                                nullptr, nullptr, nullptr);
          if (dsapar) {
            DSA_set_method(dsapar, DSA_get_default_method());
            if (DSA_generate_key(dsapar)) {
              if (EVP_PKEY_assign_DSA(priv_key, dsapar)) {
                ret = true;
              }
            } else {
              DSA_free(dsapar);
            }
          }
        }
        break;
#endif
#ifdef HAVE_EVP_PKEY_EC
      case OPENSSL_KEYTYPE_EC:
        {
          if (curve_name == NID_undef) {
            raise_warning("Missing configuration value: 'curve_name' not set");
            return false;
          }
          if (auto const eckey = EC_KEY_new_by_curve_name(curve_name)) {
            EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
            if (EC_KEY_generate_key(eckey) &&
                EVP_PKEY_assign_EC_KEY(priv_key, eckey)) {
              ret = true;
            } else {
              EC_KEY_free(eckey);
            }
          }
        }
        break;
#endif
      default:
        raise_warning("Unsupported private key type");
      }
    }

    write_rand_file(randfile, egdsocket, seeded);
    return ret;
  }

#define MIN_KEY_LENGTH          384

const int64_t k_OPENSSL_RAW_DATA = 1;

const int64_t k_OPENSSL_ZERO_PADDING = 2;

const int64_t k_OPENSSL_NO_PADDING = 3;

const int64_t k_OPENSSL_PKCS1_OAEP_PADDING = 4;

const int64_t k_OPENSSL_SSLV23_PADDING = 2;

const int64_t k_OPENSSL_PKCS1_PADDING = 1;

static char default_ssl_conf_filename[PATH_MAX];

struct OpenSSLInitializer {
  OpenSSLInitializer() {
    SSL_library_init();
    OpenSSL_add_all_ciphers();
    OpenSSL_add_all_digests();
    OpenSSL_add_all_algorithms();

// CCM ciphers are not added by default, so let's add them!
#if !defined(OPENSSL_NO_AES) && defined(EVP_CIPH_CCM_MODE) && \
    OPENSSL_VERSION_NUMBER < 0x100020000
    EVP_add_cipher(EVP_aes_128_ccm());
    EVP_add_cipher(EVP_aes_192_ccm());
    EVP_add_cipher(EVP_aes_256_ccm());
#endif

    ERR_load_ERR_strings();
    ERR_load_crypto_strings();
    ERR_load_EVP_strings();

    /* Determine default SSL configuration file */
    char *config_filename = getenv("OPENSSL_CONF");
    if (config_filename == nullptr) {
      config_filename = getenv("SSLEAY_CONF");
    }

    /* default to 'openssl.cnf' if no environment variable is set */
    if (config_filename == nullptr) {
      snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename),
               "%s/%s", X509_get_default_cert_area(), "openssl.cnf");
    } else {
      always_assert(
        strlen(config_filename) < sizeof(default_ssl_conf_filename));
      strcpy(default_ssl_conf_filename, config_filename);
    }
  }

  ~OpenSSLInitializer() {
    EVP_cleanup();
  }
}


int64_t HHVM_FUNCTION(strnatcmp,
                      const String& str1,
                      const String& str2) {
  return string_natural_cmp(str1.data(), str1.size(), str2.data(), str2.size(),
                            false);
}


int64_t HHVM_FUNCTION(strcasecmp,
                      const String& str1,
                      const String& str2) {
  return bstrcasecmp(str1.data(), str1.size(), str2.data(), str2.size());
}


  PCRECache()
    : m_kind(CacheKind::Static), m_staticCache(nullptr)
  {
    reinit(CacheKind::Static);
  }


bool HHVM_FUNCTION(imageantialias, const Resource& image, bool on) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  SetAntiAliased(im, on);
  return true;
}


Variant HHVM_FUNCTION(mcrypt_ofb, const String& cipher, const String& key,
                                  const String& data, const Variant& mode,
                                  const Variant& viv /* = null_string */) {
  raise_deprecated("Function mcrypt_ofb() is deprecated");
  String iv = viv.toString();
  return php_mcrypt_do_crypt(cipher, key, data, "ofb", iv, mode.toInt32(),
                             "mcrypt_ofb");
}


TypedValue HHVM_FUNCTION(strrchr,
                         const String& haystack,
                         const Variant& needle) {
  if (haystack.size() == 0) {
    return make_tv<KindOfBoolean>(false);
  }

  int pos;
  if (needle.isString() && needle.toString().size() > 0) {
    pos = haystack.rfind(needle.toString().data()[0], false);
  } else {
    pos = haystack.rfind(needle.toByte(), false);
  }
  if (pos < 0) return make_tv<KindOfBoolean>(false);
  return tvReturn(haystack.substr(pos));
}


String HHVM_FUNCTION(htmlspecialchars_decode,
                     const String& str,
                     int flags /* = k_ENT_HTML_QUOTE_DOUBLE */) {
  return StringUtil::HtmlDecode(str, StringUtil::toQuoteStyle(flags),
                                "UTF-8", false);
}


static struct gfxinfo *php_handle_jp2(const req::ptr<File>& stream) {
  struct gfxinfo *result = nullptr;
  unsigned int box_length;
  unsigned int box_type;
  char jp2c_box_id[] = {(char)0x6a, (char)0x70, (char)0x32, (char)0x63};

  /* JP2 is a wrapper format for JPEG 2000. Data is contained within "boxes".
     Boxes themselves can be contained within "super-boxes". Super-Boxes can
     contain super-boxes which provides us with a hierarchical storage system.

     It is valid for a JP2 file to contain multiple individual codestreams.
     We'll just look for the first codestream at the root of the box structure
     and handle that.
  */

  for (;;)
  {
    box_length = php_read4(stream); /* LBox */
    /* TBox */
    String str = stream->read(sizeof(box_type));
    if (str.length() != sizeof(box_type)) {
      /* Use this as a general "out of stream" error */
      break;
    }
    memcpy(&box_type, str.c_str(), sizeof(box_type));

    if (box_length == 1) {
      /* We won't handle XLBoxes */
      return nullptr;
    }

    if (!memcmp(&box_type, jp2c_box_id, 4))
    {
      /* Skip the first 3 bytes to emulate the file type examination */
      stream->seek(3, SEEK_CUR);

      result = php_handle_jpc(stream);
      break;
    }

    /* Stop if this was the last box */
    if ((int)box_length <= 0) {
      break;
    }

    /* Skip over LBox (Which includes both TBox and LBox itself */
    if (!stream->seek(box_length - 8, SEEK_CUR)) {
      break;
    }
  }

  if (result == nullptr) {
    raise_warning("JP2 file has no codestreams at root level");
  }

  return result;
}


  SampleProfiler() : Profiler(true) {
    struct timeval  now;
    uint64_t truncated_us;
    uint64_t truncated_tsc;

    // Init the last_sample in tsc
    m_last_sample_tsc = cpuCycles();

    // Find the microseconds that need to be truncated
    gettimeofday(&m_last_sample_time, 0);
    now = m_last_sample_time;
    hp_trunc_time(&m_last_sample_time, SAMPLING_INTERVAL);

    // Subtract truncated time from last_sample_tsc
    truncated_us  = get_us_interval(&m_last_sample_time, &now);
    truncated_tsc = truncated_us * m_MHz;
    if (m_last_sample_tsc > truncated_tsc) {
      // just to be safe while subtracting unsigned ints
      m_last_sample_tsc -= truncated_tsc;
    }

    // Convert sampling interval to ticks
    m_sampling_interval_tsc = SAMPLING_INTERVAL * m_MHz;
  }


String HHVM_FUNCTION(htmlentities,
                     const String& str,
                     int flags /* = k_ENT_HTML_QUOTE_DOUBLE */,
                     const String& charset /* = "UTF-8" */,
                     bool double_encode /* = true */) {
  // dropping double_encode parameters and see runtime/base/zend-html.h
  const char *scharset = charset.data();
  if (!*scharset) scharset = "ISO-8859-1";
  return StringUtil::HtmlEncode(str, StringUtil::toQuoteStyleBitmask(flags),
                                scharset, double_encode, true);
}


inline const void* StringData::payload() const { return this + 1; }


Variant HHVM_FUNCTION(imageloadfont, const String& /*file*/) {
  throw_not_supported(__func__, "NYI");
  // If you decide to implement this, be careful to avoid the crash from
  // https://github.com/php/php-src/commit/9a60aed6d1925c98b1b40c19b40f5b4b65baa
}


char * ap_php_conv_p2(register uint64_t num, register int nbits,
                      char format, char *buf_end, register int *len)
{
  register int mask = (1 << nbits) - 1;
  register char *p = buf_end;
  static char low_digits[] = "0123456789abcdef";
  static char upper_digits[] = "0123456789ABCDEF";
  register char *digits = (format == 'X') ? upper_digits : low_digits;

  do {
    *--p = digits[num & mask];
    num >>= nbits;
  }
  while (num);

  *len = buf_end - p;
  return (p);
}

#define STR_TO_DEC(str, num) do {  \
  num = NUM(*str++);               \
  while (isdigit((int)*str)) {     \
    num *= 10;                     \
    num += NUM(*str++);            \
    if (num >= INT_MAX / 10) {     \
      while (isdigit((int)*str++)) \
        continue;                  \
      break;                       \
    }                              \
  }                                \
} while (0)

#define FIX_PRECISION(adjust, precision, s, s_len) do { \
  if (adjust)                                           \
    while (s_len < precision) {                         \
      *--s = '0';                                       \
      s_len++;                                          \
    }                                                   \
} while (0)

typedef uint64_t u_wide_int;

#define INT_NULL        ((int *)0)

static const char* s_null = "(null)";


Variant HHVM_FUNCTION(mcrypt_get_key_size, const String& cipher,
                                           const String& module) {
  MCRYPT td = mcrypt_module_open((char*)cipher.data(),
                                 (char*)MCG(algorithms_dir).data(),
                                 (char*)module.data(),
                                 (char*)MCG(modes_dir).data());
  if (td == MCRYPT_FAILED) {
    MCRYPT_OPEN_MODULE_FAILED("mcrypt_get_key_size");
    return false;
  }

  int64_t ret = mcrypt_enc_get_key_size(td);
  mcrypt_module_close(td);
  return ret;
}


    ~Accessor() {
      switch (m_kind) {
        case Kind::Empty:
        case Kind::Ptr:
          break;
        case Kind::SmartPtr:
          m_u.smart_ptr.~EntryPtr();
          break;
        case Kind::AccessorKind:
          m_u.accessor.~ConstAccessor();
          break;
      }
    }


TypedValue HHVM_FUNCTION(substr_count,
                         const String& haystack,
                         const String& needle,
                         int offset /* = 0 */,
                         int length /* = 0x7FFFFFFF */) {
  int lenNeedle = needle.size();
  if (lenNeedle == 0) {
    raise_invalid_argument_warning("needle: (empty)");
    return make_tv<KindOfBoolean>(false);
  }

  if (offset < 0 || offset > haystack.size()) {
    raise_invalid_argument_warning("offset: (out of range)");
    return make_tv<KindOfBoolean>(false);
  }
  if (length == 0x7FFFFFFF) {
    length = haystack.size() - offset;
  } else if (length <= 0 || length > haystack.size() - offset) {
    raise_invalid_argument_warning("length: (out of range)");
    return make_tv<KindOfBoolean>(false);
  }

  int count = 0;
  int posMax = offset + length - lenNeedle;
  for (int pos = haystack.find(needle, offset);
       pos != -1 && pos <= posMax;
       pos = haystack.find(needle, pos + lenNeedle)) {
    ++count;
  }
  return make_tv<KindOfInt64>(count);
}


static unsigned long int php_swf_get_bits(unsigned char* buffer,
                                          unsigned int pos,
                                          unsigned int count) {
  unsigned int loop;
  unsigned long int result = 0;

  for (loop = pos; loop < pos + count; loop++)
  {
    result = result +
      ((((buffer[loop / 8]) >> (7 - (loop % 8))) & 0x01) <<
      (count - (loop - pos) - 1));
  }
  return result;
}


  void collectStats(const char *symbol, bool isFuncExit, TraceEntry& te) {
    te.symbol = symbol;
    te.is_func_exit = isFuncExit;
    collectStats(te);
  }


bool HHVM_FUNCTION(socket_bind,
                   const Resource& socket,
                   const String& address,
                   int port /* = 0 */) {
  auto sock = cast<Socket>(socket);

  sockaddr_storage sa_storage;
  struct sockaddr *sa_ptr;
  size_t sa_size;
  if (!set_sockaddr(sa_storage, sock, address, port, sa_ptr, sa_size)) {
    return false;
  }

  long retval = ::bind(sock->fd(), sa_ptr, sa_size);
  if (retval != 0) {
    std::string msg = "unable to bind address";
    msg += address.data();
    msg += ":";
    msg += folly::to<std::string>(port);
    SOCKET_ERROR(sock, msg.c_str(), errno);
    return false;
  }

  return true;
}


String rval(double  key) const { return rvalImpl((int64_t)key);}


int php_iptc_next_marker(const req::ptr<File>& file,
                         int spool,
                         unsigned char **spoolbuf) {
  int c;

  /* skip unimportant stuff */

  c = php_iptc_get1(file, spool, spoolbuf);

  if (c == EOF) return M_EOI;

  while (c != 0xff) {
    if ((c = php_iptc_get1(file, spool, spoolbuf)) == EOF) {
      return M_EOI; /* we hit EOF */
    }
  }

  /* get marker byte, swallowing possible padding */
  do {
    c = php_iptc_get1(file, 0, 0);
    if (c == EOF)
      return M_EOI;       /* we hit EOF */
    else if (c == 0xff)
      php_iptc_put1(file, spool, (unsigned char)c, spoolbuf);
  } while (c == 0xff);

  return (unsigned int) c;
}


static int exif_process_string_raw(char **result, char *value,
                                   size_t byte_count) {
  /* we cannot use strlcpy - here the problem is that we have to copy NUL
   * chars up to byte_count, we also have to add a single NUL character to
   * force end of string.
   */
  *result = 0;
  if (byte_count) {
    (*result) = (char*)IM_MALLOC(byte_count + 1);
    CHECK_ALLOC_R((*result), byte_count + 1, 0);
    memcpy(*result, value, byte_count);
    (*result)[byte_count] = '\0';
    return byte_count+1;
  }
  return 0;
}


struct string_data_hash {
  size_t operator()(const StringData *s) const {
    return s->hash();
  }

struct string_data_same {
  bool operator()(const StringData *s1, const StringData *s2) const {
    assertx(s1 && s2);
    return s1->same(s2);
  }

struct string_data_eq_same {
  bool operator()(const StringData* a, const StringData* b) const {
    return a == b || a->same(b);
  }

struct string_data_isame {
  bool operator()(const StringData *s1, const StringData *s2) const {
    assertx(s1 && s2);
    return s1->isame(s2);
  }

struct string_data_lt {
  bool operator()(const StringData *s1, const StringData *s2) const {
    int len1 = s1->size();
    int len2 = s2->size();
    if (len1 < len2) {
      return (len1 == 0) || (memcmp(s1->data(), s2->data(), len1) <= 0);
    } else

struct string_data_lti {
  bool operator()(const StringData *s1, const StringData *s2) const {
    return bstrcasecmp(s1->data(), s1->size(), s2->data(), s2->size()) < 0;
  }


  ~MachineInfo() {
    delete[] m_cpu_frequencies;
  }


  const std::string getRepl() const {
    return repl;
  }


preg_return_pcre_error(int pcre_code, Variant&& return_value) {
  pcre_handle_exec_error(pcre_code);
  return std::move(return_value);
}


String stringForEachBuffered(uint32_t bufLen, const String& str, Op action) {
  StringBuffer sb(bufLen);
  auto sl = str.slice();
  const char* src = sl.begin();
  const char* end = sl.end();

  for (; src < end; ++src) {
    action(sb, src, end);
  }

  return sb.detach();
}


bool HHVM_FUNCTION(imagefilltoborder, const Resource& image,
    int64_t x, int64_t y, int64_t border, int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImageFillToBorder(im, x, y, border, color);
  return true;
}


static unsigned short php_read4(const req::ptr<File>& stream) {
  unsigned char *a;
  String str = stream->read(4);
  /* just return 0 if we hit the end-of-file */
  if (str.length() != 4) return 0;
  a = (unsigned char *)str.c_str();
  return (((unsigned int)a[0]) << 24)
       + (((unsigned int)a[1]) << 16)
       + (((unsigned int)a[2]) <<  8)
       + (((unsigned int)a[3]));
}


String HHVM_FUNCTION(str_pad,
                     const String& input,
                     int pad_length,
                     const String& pad_string /* = " " */,
                     int pad_type /* = k_STR_PAD_RIGHT */) {
  return StringUtil::Pad(input, pad_length, pad_string,
                         (StringUtil::PadType)pad_type);
}


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;
    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;
  free(p_orig);
  return (buf);
}

#define NDIG 320

#define STR_TO_DEC(str, num) do {  \
  num = NUM(*str++);               \
  while (isdigit((int)*str)) {     \
    num *= 10;                     \
    num += NUM(*str++);            \
    if (num >= INT_MAX / 10) {     \
      while (isdigit((int)*str++)) \
        continue;                  \
      break;                       \
    }                              \
  }                                \
} while (0)

#define FIX_PRECISION(adjust, precision, s, s_len) do { \
  if (adjust)                                           \
    while (s_len < precision) {                         \
      *--s = '0';                                       \
      s_len++;                                          \
    }                                                   \
} while (0)

typedef int64_t wide_int;

#define NUL             '\0'

#define INT_NULL        ((int *)0)

static const char* s_null = "(null)";

#define EXPONENT_LENGTH 10


Variant HHVM_FUNCTION(count_chars,
                      const String& str,
                      int64_t mode /* = 0 */) {
  int chars[256];
  memset((void*)chars, 0, sizeof(chars));
  const unsigned char *buf = (const unsigned char *)str.data();
  for (int len = str.size(); len > 0; len--) {
    chars[*buf++]++;
  }

  Array retarr = Array::CreateDArray();
  char retstr[256];
  int retlen = 0;
  switch (mode) {
  case 0:
    for (int inx = 0; inx < 256; inx++) {
      retarr.set(inx, chars[inx]);
    }
    return retarr;
  case 1:
    for (int inx = 0; inx < 256; inx++) {
      if (chars[inx] != 0) {
        retarr.set(inx, chars[inx]);
      }
    }
    return retarr;
  case 2:
    for (int inx = 0; inx < 256; inx++) {
      if (chars[inx] == 0) {
        retarr.set(inx, chars[inx]);
      }
    }
    return retarr;
  case 3:
    for (int inx = 0; inx < 256; inx++) {
      if (chars[inx] != 0) {
        retstr[retlen++] = inx;
      }
    }
    return String(retstr, retlen, CopyString);
  case 4:
    for (int inx = 0; inx < 256; inx++) {
      if (chars[inx] == 0) {
        retstr[retlen++] = inx;
      }
    }
    return String(retstr, retlen, CopyString);
  }

  raise_invalid_argument_warning("mode: %" PRId64, mode);
  return false;
}


CountedTraceData() : count(0)  { clear(); }


static int exif_file_sections_add(image_info_type *ImageInfo, int type,
                                  size_t size, unsigned char *data) {
  file_section *tmp;
  int count = ImageInfo->file.count;
  size_t realloc_size = (count+1) * sizeof(file_section);
  tmp = (file_section *)IM_REALLOC(ImageInfo->file.list, realloc_size);
  CHECK_ALLOC_R(tmp, realloc_size, -1);
  ImageInfo->file.list = tmp;
  ImageInfo->file.list[count].type = 0xFFFF;
  ImageInfo->file.list[count].data = nullptr;
  ImageInfo->file.list[count].size = 0;
  ImageInfo->file.count = count+1;
  if (!size) {
    data = nullptr;
  } else if (data == nullptr) {
    data = (unsigned char *)IM_MALLOC(size);
    if (data == nullptr) IM_FREE(tmp);
    CHECK_ALLOC_R(data, size, -1);
  }
  ImageInfo->file.list[count].type = type;
  ImageInfo->file.list[count].data = data;
  ImageInfo->file.list[count].size = size;
  return count;
}


  bool empty() const {
    return m_str ? m_str->empty() : true;
  }


Variant HHVM_FUNCTION(getimagesizefromstring, const String& imagedata,
                      Array& imageinfo) {
  String data = "data://text/plain;base64,";
  data += StringUtil::Base64Encode(imagedata);
  if (auto stream = File::Open(data, "r")) {
    return getImageSize(stream, imageinfo);
  }
  return false;
}


String rval(int     key) const { return rvalImpl(key);}


PCREglobals::~PCREglobals() {
  pcre_jit_stack_free(jit_stack);
}


  bool operator()(const StringData *s1, const StringData *s2) const {
    int len1 = s1->size();
    int len2 = s2->size();
    if (len1 < len2) {
      return (len1 == 0) || (memcmp(s1->data(), s2->data(), len1) <= 0);
    } else if (len1 == len2) {
      return (len1 != 0) && (memcmp(s1->data(), s2->data(), len1) < 0);
    } else /* len1 > len2 */ {
      return ((len2 != 0) && (memcmp(s1->data(), s2->data(), len2) < 0));
    }
  }


explicit FreeHelperImpl(void* p) : p(p) {}


  explicit HierarchicalProfiler(int flags) : Profiler(true), m_flags(flags) {
  }


bool HHVM_FUNCTION(HH_is_late_init_sprop_init,
                   const String& clsName,
                   const String& name) {
  auto const cls = Class::load(clsName.get());
  if (!cls) {
    SystemLib::throwInvalidArgumentExceptionObject(
      folly::sformat("Unknown class {}", clsName)
    );
  }
  auto const ctx = fromCaller(
    [] (const ActRec* fp, Offset) { return fp->func()->cls(); }
  );
  auto const lookup = cls->getSPropIgnoreLateInit(ctx, name.get());
  if (!lookup.val || !lookup.accessible) {
    SystemLib::throwInvalidArgumentExceptionObject(
      folly::sformat(
       "Unknown or inaccessible static property '{}' on class {}",
       name.get(),
       clsName.get()
      )
    );
  }
  return type(lookup.val) != KindOfUninit;
}


inline StringData* StringData::Make(const StringData* s1,
                                    folly::StringPiece s2) {
  return Make(s1->slice(), s2);
}


Variant preg_split(const String& pattern, const String& subject,
                   int limit /* = -1 */, int flags /* = 0 */) {
  PCRECache::Accessor accessor;
  if (!pcre_get_compiled_regex_cache(accessor, pattern.get())) {
    return preg_return_bad_regex_error(false);
  }
  const pcre_cache_entry* pce = accessor.get();

  int no_empty = flags & PREG_SPLIT_NO_EMPTY;
  bool delim_capture = flags & PREG_SPLIT_DELIM_CAPTURE;
  bool offset_capture = flags & PREG_SPLIT_OFFSET_CAPTURE;

  if (limit == 0) {
    limit = -1;
  }

  int size_offsets = 0;
  int* offsets = create_offset_array(pce, size_offsets);
  SmartFreeHelper offsetsFreer(offsets);
  if (offsets == nullptr) {
    return preg_return_internal_error(false);
  }

  /* Start at the beginning of the string */
  int start_offset = 0;
  int next_offset = 0;
  const char* last_match = subject.data();
  pcre_extra extra;
  init_local_extra(&extra, pce->extra);

  const bool hackArrOutput = flags & PREG_FB_HACK_ARRAYS;

  // Get next piece if no limit or limit not yet reached and something matched
  Array result = hackArrOutput ? Array::CreateDict() : Array::CreateDArray();
  int g_notempty = 0;   /* If the match should not be empty */
  int utf8_check = 0;
  PCRECache::Accessor bump_accessor;
  const pcre_cache_entry* bump_pce = nullptr; /* instance for empty matches */
  while ((limit == -1 || limit > 1)) {
    int count = pcre_exec(pce->re, &extra, subject.data(), subject.size(),
                          start_offset, g_notempty | utf8_check,
                          offsets, size_offsets);

    /* Subsequent calls to pcre_exec don't need to bother with the
     * utf8 validity check: if the subject isn't valid, the first
     * call to pcre_exec will have failed, and as long as we only
     * set start_offset to known character boundaries we won't
     * supply an invalid offset. */
    utf8_check = PCRE_NO_UTF8_CHECK;

    /* Check for too many substrings condition. */
    if (count == 0) {
      raise_warning("Matched, but too many substrings");
      count = size_offsets / 3;
    }

    /* If something matched */
    if (count > 0 && offsets[1] >= offsets[0]) {
      if (!no_empty || subject.data() + offsets[0] != last_match) {
        auto const length = subject.data() + offsets[0] - last_match;
        auto const match = String(last_match, length, CopyString);
        auto const value = offset_capture
          ? Variant(str_offset_pair(match, next_offset, hackArrOutput))
          : Variant(match);
        assertx(result->isVectorData());
        result.set(safe_cast<int64_t>(result.size()), value);

        /* One less left to do */
        if (limit != -1) limit--;
      }

      last_match = subject.data() + offsets[1];
      next_offset = offsets[1];

      if (delim_capture) {
        int i, match_len;
        for (i = 1; i < count; i++) {
          match_len = offsets[(i<<1)+1] - offsets[i<<1];
          /* If we have matched a delimiter */
          if (!no_empty || match_len > 0) {
            auto const match = subject.substr(offsets[i<<1], match_len);
            auto const value = offset_capture
              ? Variant(str_offset_pair(match, offsets[i<<1], hackArrOutput))
              : Variant(match);
            assertx(result->isVectorData());
            result.set(safe_cast<int64_t>(result.size()), value);
          }
        }
      }
    } else if (count == PCRE_ERROR_NOMATCH) {
      /* If we previously set PCRE_NOTEMPTY after a null match,
         this is not necessarily the end. We need to advance
         the start offset, and continue. Fudge the offset values
         to achieve this, unless we're already at the end of the string. */
      if (g_notempty != 0 && start_offset < subject.size()) {
        if (pce->compile_options & PCRE_UTF8) {
          if (bump_pce == nullptr) {
            auto const DEBUG_ONLY ok = pcre_get_compiled_regex_cache(
              bump_accessor, s_OneUnicodeCharPattern.get());
            assertx(ok);
            bump_pce = bump_accessor.get();
          }
          pcre_extra bump_extra;
          init_local_extra(&bump_extra, bump_pce->extra);
          count = pcre_exec(bump_pce->re, &bump_extra, subject.data(),
                            subject.size(), start_offset,
                            utf8_check, offsets, size_offsets);
          if (count < 1) {
            raise_warning("Unknown error");
            offsets[0] = start_offset;
            offsets[1] = start_offset + 1;
            if (pcre_need_log_error(count)) {
              pcre_log_error(__FUNCTION__, __LINE__, count,
                             pattern.data(), pattern.size(),
                             subject.data(), subject.size(),
                             "", 0,
                             limit, flags, start_offset);
            }
          }
        } else {
          offsets[0] = start_offset;
          offsets[1] = start_offset + 1;
        }
      } else
        break;
    } else {
      if (pcre_need_log_error(count)) {
        pcre_log_error(__FUNCTION__, __LINE__, count,
                       pattern.data(), pattern.size(),
                       subject.data(), subject.size(),
                       "", 0,
                       limit, flags, start_offset, g_notempty);
      }
      // NOTE: this returns an error together with a partial result :-(
      start_offset = last_match - subject.data(); /* offset might have
                                                   * been incremented,
                                                   * but without further
                                                   * successful matches */
      if (!no_empty || start_offset < subject.size()) {
        auto const match = subject.substr(start_offset);
        auto const value = offset_capture
          ? Variant(str_offset_pair(match, start_offset, hackArrOutput))
          : Variant(match);
        assertx(result->isVectorData());
        result.set(safe_cast<int64_t>(result.size()), value);
      }
      return preg_return_pcre_error(count, std::move(result));
    }

    /* If we have matched an empty string, mimic what Perl's /g options does.
       This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try
       the match again at the same point. If this fails (picked up above) we
       advance to the next character. */
    g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0;

    /* Advance to the position right after the last full match */
    start_offset = offsets[1];
  }

  start_offset = last_match - subject.data(); /* offset might have
                                                * been incremented,
                                                * but without further
                                                * successful matches */
  if (!no_empty || start_offset < subject.size()) {
    auto const match = subject.substr(start_offset);
    auto const value = offset_capture
      ? Variant(str_offset_pair(match, start_offset, hackArrOutput))
      : Variant(match);
    assertx(result->isVectorData());
    result.set(safe_cast<int64_t>(result.size()), value);
  }

  return preg_return_no_error(std::move(result));
}


  ~TraceProfiler() override {
    if (m_successful) {
      free(m_traceBuffer);
      IniSetting::Unbind("profiler.max_trace_buffer");
      pthread_mutex_unlock(&s_inUse);
    }
  }


StringExtension() : Extension("string") {}


void reset(StringData* str = nullptr) { m_str.reset(str); }

String(const StringData*) = delete;

String(const char *s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, mode) : nullptr, NoIncRef{}) {}

  // attach to binary malloc'ed string
  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // make copy of binary binary string
  String(const char *s, size_t length, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // force a copy of a String
  String(const String& s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s.c_str(), s.size(), mode)
                          : nullptr, NoIncRef{}) {}

  // make an empty string with cap reserve bytes, plus 1 for '\0'
  String(size_t cap, ReserveStringMode /*mode*/)
      : m_str(StringData::Make(cap), NoIncRef{}) {}

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});

String substr(int start, int length = StringData::MaxSize) const {
    return String::attach(
      m_str ? m_str->substr(start, length) : staticEmptyString());
  }

  /**
   * Find a character or a substring and return its position. "pos" has to be
   * within current string and it's the start point for searching.
   */

explicit operator bool() const { return (bool)m_str;

struct StrNR {
private:
  StringData *m_px;

public:
  StrNR() : m_px(nullptr) {}
  explicit StrNR(StringData *sd) : m_px(sd) {}
  explicit StrNR(const StringData *sd) : m_px(const_cast<StringData*>(sd)) {}
  explicit StrNR(const String &s) : m_px(s.get()) {} // XXX
  explicit StrNR(const char*) = delete;

  ~StrNR() {
    if (debug) {
      m_px = reinterpret_cast<StringData*>(0xdeadbeeffaceb004);
    }
  }

  /* implicit */ operator

void init(const char* s, size_t len);


StringData* get() const { return m_px; }


bool HHVM_FUNCTION(imagegd2, const Resource& image,
    const String& filename /* = null_string */,
    int64_t chunk_size /* = 0 */, int64_t type /* = 0 */) {
  return _php_image_output(image, filename, chunk_size, type,
                           PHP_GDIMG_TYPE_GD2, "GD2",
                           (void (*)())gdImageGd2);
}


Profiler::Profiler(bool needCPUAffinity) : m_successful(true),
                                           m_stack(nullptr),
                                           m_frame_free_list(nullptr),
                                           m_has_affinity(needCPUAffinity) {
    if (!s_rand_initialized) {
      s_rand_initialized = true;
      srand(math_generate_seed());
    }

    if (m_has_affinity) {
      //
      // Bind to a random cpu so that we can use rdtsc instruction.
      //
      int cur_cpu_id = rand() % s_machine.m_cpu_num;
      GET_AFFINITY(0, sizeof(cpu_set_t), &m_prev_mask);
      MachineInfo::BindToCPU(cur_cpu_id);
      m_MHz = s_machine.m_cpu_frequencies[cur_cpu_id];
    } else {
      //
      // Take cpu0's speed as a proxy for all cpus.
      //
      m_MHz = s_machine.m_cpu_frequencies[0];
    }

    memset(m_func_hash_counters, 0, sizeof(m_func_hash_counters));
}

#define HP_STACK_DELIM_LEN    (sizeof(HP_STACK_DELIM) - 1)


bool HHVM_FUNCTION(imagesetinterpolation,
    const Resource& image, int64_t method /*=GD_BILINEAR_FIXED*/) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  if (method == -1) method = GD_BILINEAR_FIXED;
  return gdImageSetInterpolationMethod(im, (gdInterpolationMethod) method);
}

#define IMAGE_TYPE_GIF 1

#define IMAGE_TYPE_JPEG 2

#define IMAGE_TYPE_PNG 4

#define IMAGE_FILTER_NEGATE         0

#define IMAGE_FILTER_GRAYSCALE      1

#define IMAGE_FILTER_COLORIZE       4

#define IMAGE_FILTER_MAX            11

#define IMAGE_FILTER_MAX_ARGS       6

#define IMAGE_TYPE_WBMP 8

#define IMAGE_TYPE_XPM 16

#define IMAGE_FILTER_BRIGHTNESS     2

#define IMAGE_FILTER_CONTRAST       3

#define IMAGE_FILTER_EDGEDETECT     5

#define IMAGE_FILTER_EMBOSS         6

#define IMAGE_FILTER_SELECTIVE_BLUR 8

#define IMAGE_FILTER_SMOOTH         10

#define IMAGE_FILTER_PIXELATE       11

#define IMAGE_FILTER_MEAN_REMOVAL   9

#define IMAGE_FILTER_GAUSSIAN_BLUR  7


Variant HHVM_FUNCTION(imagecolorexact, const Resource& image,
    int64_t red, int64_t green, int64_t blue) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  return gdImageColorExact(im, red, green, blue);
}


  void endFrameEx(const TypedValue* /*retval*/,
                  const char* /*given_symbol*/) override {
    char symbol[512];
    HierarchicalProfilerFrame *frame =
      dynamic_cast<HierarchicalProfilerFrame *>(m_stack);
    frame->getStack(2, symbol, sizeof(symbol));
    CountMap &counts = m_stats[symbol];
    counts.count++;
    counts.wall_time += cpuCycles() - frame->m_tsc_start;

    if (m_flags & TrackCPU) {
      counts.cpu += cpuTime(m_MHz) - frame->m_vtsc_start;
    }

    if (m_flags & TrackMemory) {
      auto const stats = tl_heap->getStats();
      int64_t mu_end = stats.usage();
      int64_t pmu_end = stats.peakUsage;
      counts.memory += mu_end - frame->m_mu_start;
      counts.peak_memory += pmu_end - frame->m_pmu_start;
    } else if (m_flags & TrackMalloc) {
      counts.memory += get_allocs() - frame->m_mu_start;
      counts.peak_memory += get_frees() - frame->m_pmu_start;
    }
  }


Variant HHVM_FUNCTION(imagecolorsforindex, const Resource& image,
                                           int64_t index) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  if (index >= 0 &&
      (gdImageTrueColor(im) || index < gdImageColorsTotal(im))) {
    return make_darray(
      s_red,  gdImageRed(im,index),
      s_green, gdImageGreen(im,index),
      s_blue, gdImageBlue(im,index),
      s_alpha, gdImageAlpha(im,index)
    );
  }
  raise_warning("Color index %" PRId64 " out of range", index);
  return false;
}


Variant HHVM_FUNCTION(mcrypt_enc_self_test, const Resource& td) {
  auto pm = get_valid_mcrypt_resource(td);
  if (!pm) {
    return false;
  }

  return mcrypt_enc_self_test(pm->m_td);
}


bool HHVM_FUNCTION(imagesavealpha, const Resource& image, bool saveflag) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImageSaveAlpha(im, saveflag);
  return true;
}


  static bool HasConverted(int n) {
    return HasConverted((int64_t)n);
  }

String(const char *s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, mode) : nullptr, NoIncRef{}) {}

  // attach to binary malloc'ed string
  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // make copy of binary binary string
  String(const char *s, size_t length, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // force a copy of a String
  String(const String& s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s.c_str(), s.size(), mode)
                          : nullptr, NoIncRef{}) {}

  // make an empty string with cap reserve bytes, plus 1 for '\0'
  String(size_t cap, ReserveStringMode /*mode*/)
      : m_str(StringData::Make(cap), NoIncRef{}) {}

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});

String substr(int start, int length = StringData::MaxSize) const {
    return String::attach(
      m_str ? m_str->substr(start, length) : staticEmptyString());
  }

  /**
   * Find a character or a substring and return its position. "pos" has to be
   * within current string and it's the start point for searching.
   */

static const int npos = -1;

int find(char ch, int pos = 0, bool caseSensitive = true) const;

int find(const char *s, int pos = 0, bool caseSensitive = true) const;

int find(const String& s, int pos = 0, bool caseSensitive = true) const;

int rfind(char ch, int pos = 0, bool caseSensitive = true) const;

int rfind(const char *s, int pos = 0, bool caseSensitive = true) const;

int rfind(const String& s, int pos = 0, bool caseSensitive = true) const;

explicit operator bool() const { return (bool)m_str;

bool operator == (const char* v) const = delete;

bool operator != (const char* v) const = delete;

bool operator >= (const char* v) const = delete;

bool operator <= (const char* v) const = delete;

bool operator >  (const char* v) const = delete;

bool operator <  (const char* v) const = delete;

bool operator >= (const String& v) const = delete;

bool operator <= (const String& v) const = delete;

bool operator == (const Variant& v) const = delete;

bool operator != (const Variant& v) const = delete;

bool operator >= (const Variant& v) const = delete;

bool operator <= (const Variant& v) const = delete;

bool operator >  (const Variant& v) const = delete;

bool operator <  (const Variant& v) const = delete;

static uint32_t CreateAll();

static bool s_globalInit;

static std::vector<std::tuple<const char*, size_t, StaticString*>>*
    s_registered;

StaticString getDataTypeString(DataType t, bool isLegacy = false);


TypedValue HHVM_FUNCTION(strripos,
                         const String& haystack,
                         const Variant& needle,
                         int offset /* = 0 */) {
  if (!is_valid_strrpos_args(haystack, needle, offset)) {
    return make_tv<KindOfBoolean>(false);
  }
  int pos;
  if (needle.isString()) {
    pos = haystack.rfind(needle.toString(), offset, false);
  } else {
    pos = haystack.rfind(needle.toByte(), offset, false);
  }
  if (pos >= 0) return make_tv<KindOfInt64>(pos);
  return make_tv<KindOfBoolean>(false);
}

/* No explicit global definitions found. */


bool HHVM_FUNCTION(imagefill, const Resource& image,
    int64_t x, int64_t y, int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImageFill(im, x, y, color);
  return true;
}


static void exif_process_APP12(image_info_type *ImageInfo,
                               char *buffer, size_t length) {
  size_t l1, l2=0;
  if ((l1 = php_strnlen(buffer+2, length-2)) > 0) {
    exif_iif_add_tag(ImageInfo, SECTION_APP12, "Company",
                     TAG_NONE, TAG_FMT_STRING, l1, buffer+2);
    if (length > 2+l1+1) {
      l2 = php_strnlen(buffer+2+l1+1, length-2-l1-1);
      exif_iif_add_tag(ImageInfo, SECTION_APP12, "Info",
                       TAG_NONE, TAG_FMT_STRING, l2, buffer+2+l1+1);
    }
  }
}


Array HHVM_FUNCTION(get_html_translation_table,
                    int table /* = 0 */,
                    int flags /* = k_ENT_HTML_QUOTE_DOUBLE */,
                    const String& encoding /* = "UTF-8" */) {
  using namespace entity_charset_enum;
  auto charset = determine_charset(encoding.data());
  if (charset == cs_unknown) {
    charset = cs_utf_8;
    if (!encoding.empty()) {
      raise_warning("get_html_translation_table(): charset `%s' not supported"
                    ", assuming utf-8", encoding.data());
    }
  }
  auto doctype = determine_doctype(flags);

  bool all = (table == k_HTML_ENTITIES);

  Array ret = Array::CreateDArray();
  switch (table) {
  case k_HTML_ENTITIES: {
    if (charset == cs_utf_8) {
      auto entity_map = get_doctype_entity_table(doctype);
      for (const auto& item : *entity_map) {
        auto key = encode_as_utf8(item.first);

        char buffer[32];
        snprintf(buffer, sizeof(buffer), "&%s;", item.second.c_str());
        ret.set(key, String(buffer, CopyString));
      }
      if (doctype == entity_doctype::html5) {
        for (const auto& item: *get_multicode_table()) {
          auto codes = item.first;
          String key = encode_as_utf8(codes.first);
          key += encode_as_utf8(codes.second);

          char buffer[32];
          snprintf(buffer, sizeof(buffer), "&%s", item.second.c_str());
          ret.set(key, String(buffer, CopyString));
        }
      }
    } else {
      const auto& entity_map = get_doctype_entity_table(doctype);
      auto charset_table = get_charset_table(charset);
      for (const auto& item : *charset_table) {
        const auto iter = entity_map->find(item.second);
        if (iter != entity_map->end()) {
          char buffer[16];
          snprintf(buffer, sizeof(buffer), "&%s;", iter->second.c_str());

          auto key = String::FromChar(item.first);
          ret.set(key, String(buffer, CopyString));
        }
      }
    }
    /* fall thru */
  }
  case k_HTML_SPECIALCHARS:
    const auto& basic_table = get_basic_table(all, doctype);
    for (int j = 0; basic_table[j].charcode != 0; j++) {
      const auto& item = basic_table[j];
      if (item.flags && (flags & item.flags) == 0)
        continue;

      ret.set(String::FromChar(item.charcode), item.entity);
    }
    ret.set(s_amp, s_ampsemi);
    break;
  }
  if (ret.empty()) {
    return Array();
  }

  return ret;
}


  const String& shrink(size_t len) {
    assertx(m_str && !m_str->isImmutable());
    if (m_str->capacity() - len > kMinShrinkThreshold) {
      m_str = req::ptr<StringData>::attach(m_str->shrinkImpl(len));
    } else {
      assertx(len < StringData::MaxSize);
      m_str->setSize(len);
    }
    return *this;
  }


ALWAYS_INLINE String empty_string() {
  return String::attach(staticEmptyString());
}

String(const StringData*) = delete;

String(const char *s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, mode) : nullptr, NoIncRef{}) {}

  // attach to binary malloc'ed string
  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // make copy of binary binary string
  String(const char *s, size_t length, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }

  // force a copy of a String
  String(const String& s, CopyStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s.c_str(), s.size(), mode)
                          : nullptr, NoIncRef{}) {}

  // make an empty string with cap reserve bytes, plus 1 for '\0'
  String(size_t cap, ReserveStringMode /*mode*/)
      : m_str(StringData::Make(cap), NoIncRef{}) {}

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});

String substr(int start, int length = StringData::MaxSize) const {
    return String::attach(
      m_str ? m_str->substr(start, length) : staticEmptyString());
  }

  /**
   * Find a character or a substring and return its position. "pos" has to be
   * within current string and it's the start point for searching.
   */

int find(const String& s, int pos = 0, bool caseSensitive = true) const;

int rfind(const String& s, int pos = 0, bool caseSensitive = true) const;

friend String&& operator

friend String

String  operator |  (const String& v) const = delete;

String  operator &  (const String& v) const = delete;

String  operator ^  (const String& v) const = delete;

explicit operator bool() const { return (bool)m_str;

bool operator >= (const String& v) const = delete;

bool operator <= (const String& v) const = delete;

String rval(const Array& key) const = delete;

String rval(const Object& key) const = delete;

String rval(const Variant& key) const = delete;

extern const String null_string;

struct StrNR {
private:
  StringData *m_px;

public:
  StrNR() : m_px(nullptr) {}
  explicit StrNR(StringData *sd) : m_px(sd) {}
  explicit StrNR(const StringData *sd) : m_px(const_cast<StringData*>(sd)) {}
  explicit StrNR(const String &s) : m_px(s.get()) {} // XXX
  explicit StrNR(const char*) = delete;

  ~StrNR() {
    if (debug) {
      m_px = reinterpret_cast<StringData*>(0xdeadbeeffaceb004);
    }
  }

  /* implicit */ operator

inline String& String::operator=(const StaticString& v)


Variant WuManberReplacement::translate(String source) const {
  size_t  pos      = 0,
          nextwpos = 0,
          lastpos  = source.size() - m;

  if (!valid) {
    return false;
  }

  // all patterns are longer than the source
  if (m > source.size()) {
    return source;
  }

  StringBuffer  result(source.size());
  while (pos <= lastpos) {
    uint16_t h = strtr_hash(source.data() + pos + m - B, B) & SHIFT_TAB_MASK;
    size_t shift_pos = shift[h];

    if (shift_pos > 0) {
      pos += shift_pos;
    } else {
      uint16_t  h2        = h & HASH_TAB_MASK,
                prefix_h  = strtr_hash(source.data() + pos, Bp);
      int offset_start  = hash[h2],
          offset_end    = hash[h2 + 1], // exclusive
          i             = 0;

      for (i = offset_start; i < offset_end; i++) {
        if (prefix[i] != prefix_h) {
          continue;
        }

        const PatAndRepl *pnr = &patterns[i];
        if (pnr->getPat().size() > source.size() - pos ||
            memcmp(pnr->getPat().data(), source.data() + pos,
                   pnr->getPat().size()) != 0) {
          continue;
        }

        result.append(source.data() + nextwpos, pos - nextwpos);
        result.append(pnr->getRepl());
        pos += pnr->getPat().size();
        nextwpos = pos;
        goto end_outer_loop;
      }

      pos++;
end_outer_loop: ;
    }
  }

  result.append(source.data() + nextwpos, source.size() - nextwpos );

  return result.detach();
}


bool Strobelight::isXenonActive() {
  if (RuntimeOption::XenonForceAlwaysOn) {
    return true;
  }

  bool xenonProfiled = Xenon::getInstance().getIsProfiledRequest();
  if (xenonProfiled) {
    return true;
  }

  return false;
}


int64_t HHVM_FUNCTION(mcrypt_module_get_algo_key_size, const String& algorithm,
                                   const String& lib_dir /* = null_string */) {
  String dir = lib_dir.empty() ? String(MCG(algorithms_dir)) : lib_dir;
  return mcrypt_module_get_algo_key_size((char*)algorithm.data(),
                                         (char*)dir.data());
}


void Image::reset() {
  if (m_gdImage) {
    gdImageDestroy(m_gdImage);
    m_gdImage = nullptr;
  }
}