static int connect_with_timeout(int fd, struct sockaddr *sa_ptr,
                                size_t sa_size, double timeout,
                                const HostURL &hosturl,
                                std::string &errstr, int &errnum) {
  if (timeout <= 0) {
    int retval = connect(fd, sa_ptr, sa_size);
    if (retval < 0) {
      errstr = "unable to connect to " + hosturl.getHostURL();
      errnum = errno;
    }
    return retval;
  }

  // set non-blocking so we can do timeouts
  long arg = fcntl(fd, F_GETFL, nullptr);
  fcntl(fd, F_SETFL, arg | O_NONBLOCK);

  int retval = connect(fd, sa_ptr, sa_size);
  if (retval < 0) {
    if (errno == EINPROGRESS) {
      struct pollfd fds[1];
      fds[0].fd = fd;
      fds[0].events = POLLOUT;
      if (poll(fds, 1, (int)(timeout * 1000))) {
        int valopt;
        socklen_t lon = sizeof(int);
        getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
        if (valopt) {
          errstr = "failed to connect to " + hosturl.getHostURL();
          errnum = valopt;
        }
        retval = valopt ? -1 : 0;
      } else {
        errstr = "timed out after ";
        errstr += folly::to<std::string>(timeout);
        errstr += " seconds when connecting to " + hosturl.getHostURL();
        errnum = ETIMEDOUT;
        retval = -1;
      }
    } else {
      errstr = "unable to connect to " + hosturl.getHostURL();
      errnum = errno;
    }
  }

  // set to blocking mode
  arg = fcntl(fd, F_GETFL, nullptr);
  fcntl(fd, F_SETFL, arg & ~O_NONBLOCK);

  return retval;
}


  void beginFrame(const char *symbol) override {
    doTrace(symbol, false);
  }

security fixes

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


static int php_ifd_get16u(void *Short, int motorola_intel) {
  if (motorola_intel) {
    return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];
  } else {
    return (((unsigned char *)Short)[1] << 8) | ((unsigned char *)Short)[0];
  }
}


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


static int php_write(void *buf, uint32_t size) {
  g_context->write((const char *)buf, size);
  return size;
}


static void exif_iif_add_tag(image_info_type *image_info, int section_index,
                             char *name, int tag, int format,
                             size_t length, void* value) {
  exif_iif_add_value(image_info, section_index, name, tag, format,
                     (int)length, value, image_info->motorola_intel);
}


  void endFrame(const TypedValue* retval, const char* symbol,
                bool /*endMain*/ = false) override {
    if (m_stack.empty()) {
      fprintf(stderr, "STACK IMBALANCE empty %s\n", symbol);
      return;
    }
    auto f = m_stack.back();
    m_stack.pop_back();
    if (strcmp(f.m_symbol, symbol) != 0) {
      fprintf(stderr, "STACK IMBALANCE %s\n", symbol);
      return;
    }
    auto& memo = m_memos[symbol];
    if (memo.m_ignore) return;
    ++memo.m_count;
    memo.m_ignore = true;
    VMRegAnchor _;
    ActRec *ar = vmfp();
    // Lots of random cases to skip just to keep this simple for
    // now. There's no reason not to do more later.
    if (ar->func()->isCPPBuiltin() || isResumed(ar)) return;
    auto ret = tvAsCVarRef(retval);
    if (ret.isNull()) return;
    if (!(ret.isString() || ret.isObject() || ret.isArray())) return;
    VariableSerializer vs(VariableSerializer::Type::DebuggerSerialize);
    String sdata;
    try {
      sdata = vs.serialize(ret, true);
    } catch (...) {
      fprintf(stderr, "Serialization failure: %s\n", symbol);
      return;
    }
    if (sdata.length() < 3) return;
    if (ar->func()->cls() && ar->hasThis()) {
      memo.m_has_this = true;
      auto& member_memo = memo.m_member_memos[f.m_args.data()];
      ++member_memo.m_count;
      if (member_memo.m_return_value.length() == 0) { // First time
        member_memo.m_return_value = sdata;
        // Intentionally copy the raw pointer value
        member_memo.m_ret_tv = *retval;
        memo.m_ignore = false;
      } else if (member_memo.m_return_value == sdata) { // Same
        memo.m_ignore = false;
        if ((member_memo.m_ret_tv.m_data.num != retval->m_data.num) ||
            (member_memo.m_ret_tv.m_type != retval->m_type)) {
          memo.m_ret_tv_same = false;
        }
      } else {
        memo.m_member_memos.clear(); // Cleanup and ignore
      }
    } else {
      if (memo.m_return_value.length() == 0) { // First time
        memo.m_return_value = sdata;
        // Intentionally copy the raw pointer value
        memo.m_ret_tv = *retval;
        memo.m_ignore = false;
      } else if (memo.m_return_value == sdata) { // Same
        memo.m_ignore = false;
        if ((memo.m_ret_tv.m_data.num != retval->m_data.num) ||
            (memo.m_ret_tv.m_type != retval->m_type)) {
          memo.m_ret_tv_same = false;
        }
      } else {
        memo.m_return_value = ""; // Different, cleanup and ignore
      }
    }
  }


bool HHVM_FUNCTION(imagedashedline,
    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;
  gdImageDashedLine(im, x1, y1, x2, y2, color);
  return true;
}


  ~TraceWalker() {
    free(m_arcBuff);
    for (auto& r : m_recursion) delete[] r.first;
  }


Variant HHVM_FUNCTION(socket_accept,
                      const Resource& socket) {
  auto sock = cast<Socket>(socket);
  struct sockaddr sa;
  socklen_t salen = sizeof(sa);
  auto new_sock = req::make<ConcreteSocket>(
    accept(sock->fd(), &sa, &salen), sock->getType());
  if (!new_sock->valid()) {
    SOCKET_ERROR(new_sock, "unable to accept incoming connection", errno);
    return false;
  }
  return Variant(std::move(new_sock));
}


static char * __cvt(double value, int ndigit, int *decpt, int *sign,
                    int fmode, int pad) {
  register char *s = nullptr;
  char *p, *rve, c;
  size_t siz;

  if (ndigit < 0) {
    siz = -ndigit + 1;
  } else {
    siz = ndigit + 1;
  }

  /* __dtoa() doesn't allocate space for 0 so we do it by hand */
  if (value == 0.0) {
    *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
    *sign = 0;
    if ((rve = s = (char *)malloc(ndigit?siz:2)) == nullptr) {
      return(nullptr);
    }
    *rve++ = '0';
    *rve = '\0';
    if (!ndigit) {
      return(s);
    }
  } else {
    p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
    if (*decpt == 9999) {
      /* Infinity or Nan, convert to inf or nan like printf */
      *decpt = 0;
      c = *p;
      zend_freedtoa(p);
      return strdup(c == 'I' ? "INF" : "NAN");
    }
    /* Make a local copy and adjust rve to be in terms of s */
    if (pad && fmode) {
      siz += *decpt;
    }
    if ((s = (char *)malloc(siz+1)) == nullptr) {
      zend_freedtoa(p);
      return(nullptr);
    }
    (void)string_copy(s, p, siz);
    rve = s + (rve - p);
    zend_freedtoa(p);
  }

  /* Add trailing zeros */
  if (pad) {
    siz -= rve - s;
    while (--siz) {
      *rve++ = '0';
    }
    *rve = '\0';
  }

  return(s);
}


static Variant mcrypt_generic(const Resource& td, const String& data,
                              bool dencrypt) {
  auto pm = get_valid_mcrypt_resource(td);
  if (!pm) {
    return false;
  } else if (!pm->m_init) {
    raise_warning("Operation disallowed prior to mcrypt_generic_init().");
    return false;
  }

  if (data.empty()) {
    raise_warning("An empty string was passed");
    return false;
  }

  String s;
  unsigned char* data_s;
  int block_size, data_size;
  /* Check blocksize */
  if (mcrypt_enc_is_block_mode(pm->m_td) == 1) { /* It's a block algorithm */
    block_size = mcrypt_enc_get_block_size(pm->m_td);
    data_size = (((data.size() - 1) / block_size) + 1) * block_size;
    s = String(data_size, ReserveString);
    data_s = (unsigned char *)s.mutableData();
    memset(data_s, 0, data_size);
    memcpy(data_s, data.data(), data.size());
  } else { /* It's not a block algorithm */
    data_size = data.size();
    s = String(data_size, ReserveString);
    data_s = (unsigned char *)s.mutableData();
    memcpy(data_s, data.data(), data.size());
  }

  if (dencrypt) {
    mdecrypt_generic(pm->m_td, data_s, data_size);
  } else {
    mcrypt_generic(pm->m_td, data_s, data_size);
  }
  s.setSize(data_size);
  return s;
}


static int php_ifd_get32s(void *Long, int motorola_intel) {
  if (motorola_intel) {
    return (((unsigned char *)Long)[0] << 24) |
           (((unsigned char *)Long)[1] << 16) |
           (((unsigned char *)Long)[2] << 8) |
           (((unsigned char *)Long)[3] << 0);
  } else {
    return (((unsigned char *)Long)[3] << 24) |
           (((unsigned char *)Long)[2] << 16) |
           (((unsigned char *)Long)[1] << 8) |
           (((unsigned char *)Long)[0] << 0);
  }
}


  int64_t length() const {
    return m_str ? m_str->size() : 0;
  }


Variant HHVM_FUNCTION(imagecreatefromgif, const String& filename) {
  gdImagePtr im =
    _php_image_create_from(filename, -1, -1, -1, -1,
                           PHP_GDIMG_TYPE_GIF, "GIF",
                           (gdImagePtr(*)())gdImageCreateFromGif,
                           (gdImagePtr(*)())gdImageCreateFromGifCtx);
  if (im == nullptr) {
    return false;
  }
  return Variant(req::make<Image>(im));
}


String HHVM_FUNCTION(strtolower,
                     const String& str) {
  return stringForEachFast(str, tolower);
}


  static void compileTimeAssertions() {
    static_assert(offsetof(StrNR, m_px) == kExpectedMPxOffset, "");
  }


void pcre_reinit() {
  PCRECache::CacheKind kind;
  if (RuntimeOption::EvalPCRECacheType == "static") {
    kind = PCRECache::CacheKind::Static;
  } else if (RuntimeOption::EvalPCRECacheType == "lru") {
    kind = PCRECache::CacheKind::Lru;
  } else if (RuntimeOption::EvalPCRECacheType == "scalable") {
    kind = PCRECache::CacheKind::Scalable;
  } else {
    Logger::Warning("Eval.PCRECacheType should be either static, "
                    "lru or scalable");
    kind = PCRECache::CacheKind::Scalable;
  }
  s_pcreCache.reinit(kind);
}


static size_t exif_convert_any_to_int(void *value, int format,
                                      int motorola_intel) {
  int s_den;
  unsigned u_den;

  switch(format) {
    case TAG_FMT_SBYTE:
      return *(signed char *)value;
    case TAG_FMT_BYTE:
      return *(unsigned char *)value;

    case TAG_FMT_USHORT:
      return php_ifd_get16u(value, motorola_intel);
    case TAG_FMT_ULONG:
      return php_ifd_get32u(value, motorola_intel);

    case TAG_FMT_URATIONAL:
      u_den = php_ifd_get32u(4+(char *)value, motorola_intel);
      if (u_den == 0) {
        return 0;
      } else {
        return php_ifd_get32u(value, motorola_intel) / u_den;
      }

    case TAG_FMT_SRATIONAL:
      s_den = php_ifd_get32s(4+(char *)value, motorola_intel);
      if (s_den == 0) {
        return 0;
      } else {
        return (size_t)((double)php_ifd_get32s(value, motorola_intel) / s_den);
      }

    case TAG_FMT_SSHORT:
      return php_ifd_get16u(value, motorola_intel);
    case TAG_FMT_SLONG:
      return php_ifd_get32s(value, motorola_intel);

    /* Not sure if this is correct (never seen float used in Exif format) */
    case TAG_FMT_SINGLE:
      return (size_t)*(float *)value;
    case TAG_FMT_DOUBLE:
      return (size_t)*(double *)value;
  }
  return 0;
}


String HHVM_FUNCTION(number_format,
                     double number,
                     int decimals /* = 0 */,
                     const Variant& dec_point_in /* = "." */,
                     const Variant& thousands_sep_in /* = "," */) {

  String dec_point(".");
  if (!dec_point_in.isNull()) {
    dec_point = dec_point_in.toString();
  }
  String thousands_sep(",");
  if (!thousands_sep_in.isNull()) {
    thousands_sep = thousands_sep_in.toString();
  }

  return string_number_format(number, decimals, dec_point, thousands_sep);
}


int preg_replace_callback(Variant& result,
                          const Variant& pattern,
                          const Variant& callback,
                          const Variant& subject,
                          int limit /* = -1 */) {
  int64_t count;
  result = preg_replace_impl(pattern, callback, subject,
                             limit, &count, true, false);
  return count;
}


static struct gfxinfo *php_handle_ico(const req::ptr<File>& stream) {
  struct gfxinfo *result = nullptr;
  const unsigned char *s;
  int num_icons = 0;

  String dim = stream->read(2);
  if (dim.length() != 2) {
    return nullptr;
  }

  s = (unsigned char *)dim.c_str();
  num_icons = (((unsigned int)s[1]) << 8) + ((unsigned int)s[0]);

  if (num_icons < 1 || num_icons > 255) {
    return nullptr;
  }

  result = (struct gfxinfo *)IM_CALLOC(1, sizeof(struct gfxinfo));
  CHECK_ALLOC_R(result, (sizeof(struct gfxinfo)), nullptr);

  while (num_icons > 0) {
    dim = stream->read(16);
    if (dim.length() != 16) {
      break;
    }

    s = (unsigned char *)dim.c_str();

    if ((((unsigned int)s[7]) << 8) + ((unsigned int)s[6]) >= result->bits) {
      result->width  = (unsigned int)s[0];
      result->height = (unsigned int)s[1];
      result->bits   = (((unsigned int)s[7]) << 8) + ((unsigned int)s[6]);
    }
    num_icons--;
  }

  return result;
}


gdImagePtr get_valid_image_resource(const Resource& image) {
  auto img_res = dyn_cast_or_null<Image>(image);
  if (!img_res || !img_res->get()) {
    raise_warning("supplied resource is not a valid Image resource");
    return nullptr;
  }
  return img_res->get();
}


Variant HHVM_FUNCTION(imagecreatefromstring, const String& data) {
  gdImagePtr im;
  int imtype;
  char sig[8];

  if (data.length() < 8) {
    raise_warning("Empty string or invalid image");
    return false;
  }
  memcpy(sig, data.c_str(), 8);
  imtype = _php_image_type(sig);
  switch (imtype) {
  case PHP_GDIMG_TYPE_JPG:
#ifdef HAVE_GD_JPG
    im = _php_image_create_from_string(data, "JPEG",
      (gdImagePtr(*)())gdImageCreateFromJpegCtx);
#else
    raise_warning("No JPEG support");
    return false;
#endif
    break;

  case PHP_GDIMG_TYPE_PNG:
#ifdef HAVE_GD_PNG
    im = _php_image_create_from_string(data, "PNG",
      (gdImagePtr(*)())gdImageCreateFromPngCtx);
#else
    raise_warning("No PNG support");
    return false;
#endif
    break;

  case PHP_GDIMG_TYPE_WEBP:
#ifdef HAVE_LIBVPX
    im = _php_image_create_from_string(data, "WEBP",
      (gdImagePtr(*)())gdImageCreateFromWebpCtx);
#else
    raise_warning("No webp support (libvpx is needed)");
    return false;
#endif
    break;

  case PHP_GDIMG_TYPE_GIF:
    im = _php_image_create_from_string(data, "GIF",
      (gdImagePtr(*)())gdImageCreateFromGifCtx);
    break;

  case PHP_GDIMG_TYPE_WBM:
    im = _php_image_create_from_string(data, "WBMP",
      (gdImagePtr(*)())gdImageCreateFromWBMPCtx);
    break;

  case PHP_GDIMG_TYPE_GD2:
    im = _php_image_create_from_string(data, "GD2",
      (gdImagePtr(*)())gdImageCreateFromGd2Ctx);
    break;

  default:
    raise_warning("Data is not in a recognized format");
    return false;
  }

  if (!im) {
    raise_warning("Couldn't create GD Image Stream out of Data");
    return false;
  }
  return Variant(req::make<Image>(im));
}


McryptExtension() : Extension("mcrypt") {}


Variant HHVM_FUNCTION(imagecolorresolvealpha, 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;
  return gdImageColorResolveAlpha(im, red, green, blue, alpha);
}


  void writeStats(Array &ret) override {
    extractStats(ret, m_stats, m_flags, m_MHz);
  }


inline void StringData::invalidateHash() {
  assertx(!isImmutable());
  assertx(!hasMultipleRefs());
  m_hash = 0;
  assertx(checkSane());
}


php_image_filter_contrast(gdImagePtr im, int arg1 /* = 0 */,
                          int /*arg2*/ /* = 0 */, int /*arg3*/ /* = 0 */,
                          int /*arg4*/ /* = 0 */) {
  int contrast = arg1;
  return gdImageContrast(im, contrast) == 1;
}


bool HHVM_FUNCTION(imagechar, 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, 0);
}


int skipheader (gdIOCtx *ctx) {
  int i;

  do {
    i = (ctx->getC)(ctx);
    if (i < 0) return (-1);
  }
  while (i & 0x80);

  return (0);
}


Variant HHVM_FUNCTION(imagesy, const Resource& image) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  return gdImageSY(im);
}


int string_crc32(const char *p, int len) {
  uint32_t crcinit = 0;
  register int32_t crc = crcinit ^ 0xFFFFFFFF;
  for (; len--; ++p) {
    crc = ((crc >> 8) & 0x00FFFFFF) ^ crc32tab[(crc ^ (*p)) & 0xFF];
  }
  return crc ^ 0xFFFFFFFF;
}


get_subpat_names(const pcre_cache_entry* pce) {
  char **subpat_names = pce->subpat_names.load(std::memory_order_relaxed);
  if (subpat_names) {
    return subpat_names;
  }

  /*
  * Build a mapping from subpattern numbers to their names. We will always
  * allocate the table, even though there may be no named subpatterns. This
  * avoids somewhat more complicated logic in the inner loops.
  */
  pcre_extra extra;
  init_local_extra(&extra, pce->extra);

  int name_count;

  subpat_names = (char **)calloc(pce->num_subpats, sizeof(char *));
  int rc = pcre_fullinfo(pce->re, &extra, PCRE_INFO_NAMECOUNT, &name_count);
  if (rc < 0) {
    raise_warning("Internal pcre_fullinfo() error %d", rc);
    return nullptr;
  }
  if (name_count > 0) {
    int name_size, ni = 0;
    unsigned short name_idx;
    char* name_table;
    int rc1, rc2;

    rc1 = pcre_fullinfo(pce->re, &extra, PCRE_INFO_NAMETABLE, &name_table);
    rc2 = pcre_fullinfo(pce->re, &extra, PCRE_INFO_NAMEENTRYSIZE, &name_size);
    rc = rc2 ? rc2 : rc1;
    if (rc < 0) {
      raise_warning("Internal pcre_fullinfo() error %d", rc);
      return nullptr;
    }
    // The table returned by PCRE_INFO_NAMETABLE is an array of fixed length
    // strings of size PCRE_INFO_NAMEENTRYSIZE.  The first two bytes are a
    // big-endian uint16_t defining the array index followed by the
    // zero-terminated name string.
    // (See https://www.pcre.org/original/doc/html/pcreapi.html)
    while (ni++ < name_count) {
      name_idx = 0x100 * (unsigned char)name_table[0] +
                 (unsigned char)name_table[1];
      subpat_names[name_idx] = name_table + 2;
      if (is_numeric_string(subpat_names[name_idx],
                            strlen(subpat_names[name_idx]),
                            nullptr, nullptr, 0) != KindOfNull) {
        raise_warning("Numeric named subpatterns are not allowed");
        return nullptr;
      }
      name_table += name_size;
    }
  }
  // Store subpat_names into the cache entry
  char **expected = nullptr;
  if (!pce->subpat_names.compare_exchange_strong(expected, subpat_names)) {
    // Another thread stored subpat_names already. The array created by the
    // other thread is now in expected, return it instead and delete the one
    // we just made.
    free(subpat_names);
    return expected;
  }
  return subpat_names;
}


String HHVM_FUNCTION(implode,
                     const Variant& arg1,
                     const Variant& arg2 /* = uninit_variant */) {
  if (isContainer(arg1)) {
    return StringUtil::Implode(arg1, arg2.toString(), false);
  } else if (arg1.isClsMeth() && RO::EvalIsCompatibleClsMethType) {
    raiseClsMethToVecWarningHelper(__FUNCTION__+2);
    auto const clsMeth = arg1.toClsMethVal();
    return concat3(
      StrNR(clsMeth->getCls()->name()),
      arg2.toString(),
      StrNR(clsMeth->getFunc()->name()));
  } else if (isContainer(arg2)) {
    return StringUtil::Implode(arg2, arg1.toString(), false);
  } else if (arg2.isClsMeth() && RO::EvalIsCompatibleClsMethType) {
    raiseClsMethToVecWarningHelper(__FUNCTION__+2);
    auto const clsMeth = arg2.toClsMethVal();
    return concat3(
      StrNR(clsMeth->getCls()->name()),
      arg1.toString(),
      StrNR(clsMeth->getFunc()->name()));
  } else {
    raise_bad_type_warning("implode() expects a container as "
                             "one of the arguments");
    return String();
  }
}


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


static int exif_discard_imageinfo(image_info_type *ImageInfo) {
  int i;

  if (ImageInfo->UserComment) IM_FREE(ImageInfo->UserComment);
  if (ImageInfo->UserCommentEncoding) {
    IM_FREE(ImageInfo->UserCommentEncoding);
  }
  if (ImageInfo->Copyright) IM_FREE(ImageInfo->Copyright);
  if (ImageInfo->CopyrightPhotographer) {
    IM_FREE(ImageInfo->CopyrightPhotographer);
  }
  if (ImageInfo->CopyrightEditor) IM_FREE(ImageInfo->CopyrightEditor);
  if (ImageInfo->Thumbnail.data) IM_FREE(ImageInfo->Thumbnail.data);
  if (ImageInfo->encode_unicode) IM_FREE(ImageInfo->encode_unicode);
  if (ImageInfo->decode_unicode_be) {
    IM_FREE(ImageInfo->decode_unicode_be);
  }
  if (ImageInfo->decode_unicode_le) {
    IM_FREE(ImageInfo->decode_unicode_le);
  }
  if (ImageInfo->encode_jis) IM_FREE(ImageInfo->encode_jis);
  if (ImageInfo->decode_jis_be) IM_FREE(ImageInfo->decode_jis_be);
  if (ImageInfo->decode_jis_le) IM_FREE(ImageInfo->decode_jis_le);
  if (ImageInfo->make) IM_FREE(ImageInfo->make);
  if (ImageInfo->model) IM_FREE(ImageInfo->model);
  for (i=0; i<ImageInfo->xp_fields.count; i++) {
    if (ImageInfo->xp_fields.list[i].value) {
      IM_FREE(ImageInfo->xp_fields.list[i].value);
    }
  }
  if (ImageInfo->xp_fields.list) IM_FREE(ImageInfo->xp_fields.list);
  for (i=0; i<SECTION_COUNT; i++) {
    exif_iif_free(ImageInfo, i);
  }
  exif_file_sections_free(ImageInfo);
  memset(ImageInfo, 0, sizeof(*ImageInfo));
  return 1;
}


  int64_t size() const {
    return m_str ? m_str->size() : 0;
  }


Variant HHVM_FUNCTION(imagefttext,
    const Resource& image,
    const Variant& size, const Variant& angle,
    int64_t x, int64_t y, int64_t col,
    const String& font_file, const String& text,
    const Array& extrainfo) {
  return php_imagettftext_common(TTFTEXT_DRAW, 1,
    image, size, angle, x, y, col, font_file, text, extrainfo);
}


static int php_skip_variable(const req::ptr<File>& stream) {
  off_t length = (unsigned int)php_read2(stream);

  if (length < 2) {
    return 0;
  }
  length = length - 2;
  stream->seek(length, SEEK_CUR);
  return 1;
}


inline void StringData::checkStack() const {
  assertx(uintptr_t(this) - s_stackLimit >= s_stackSize);
}


Variant HHVM_FUNCTION(socket_server,
                      const String& hostname,
                      int port,
                      Variant& errnum,
                      Variant& errstr) {
  HostURL hosturl(static_cast<const std::string>(hostname), port);
  return socket_server_impl(hosturl,
                            k_STREAM_SERVER_BIND|k_STREAM_SERVER_LISTEN,
                            errnum, errstr);
}


static inline char *php_ecvt(double value, int ndigit, int *decpt, int *sign) {
  return(__cvt(value, ndigit, decpt, sign, 0, 1));
}


Variant HHVM_FUNCTION(sscanf, const String& str, const String& format) {
  Variant ret;
  int result;
  result = string_sscanf(str.c_str(), format.c_str(), 0, ret);
  if (SCAN_ERROR_WRONG_PARAM_COUNT == result) return init_null();
  return ret;
}


  static req::ptr<CSRequest> GetRequest(const Variant& var) {
    if (var.isResource()) {
      return dyn_cast_or_null<CSRequest>(var);
    }
    if (var.isString() || var.isObject()) {
      BIO *in = Certificate::ReadData(var);
      if (in == nullptr) return nullptr;

      X509_REQ *csr = PEM_read_bio_X509_REQ(in, nullptr,nullptr,nullptr);
      BIO_free(in);
      if (csr) {
        return req::make<CSRequest>(csr);
      }
    }
    return nullptr;
  }


int64_t MemFile::tell() {
  assertx(m_len != -1);
  return getPosition();
}


String HHVM_FUNCTION(addslashes,
                     const String& str) {
  if (str.empty()) {
    return str;
  }

  return stringForEachBuffered(
    str.size(), str,
    [&](StringBuffer& ret, const char* src, const char* /*end*/) {
      switch (*src) {
        case '\0':
          ret.append('\\');
          ret.append('0');
          break;
        case '\\':
        case '\"':
        case '\'':
          ret.append('\\');
        /* fall through */
        default:
          ret.append(*src);
      }
    });
}


Variant HHVM_FUNCTION(imageaffine,
                      const Resource& image,
                      const Array& affine /* = Array() */,
                      const Array& clip /* = Array() */) {
  gdImagePtr src = get_valid_image_resource(image);
  if (!src) return false;
  gdImagePtr dst = nullptr;
  gdRect rect;
  gdRectPtr pRect = nullptr;
  int nelem = affine.size();
  int i;
  double daffine[6];

  if (nelem != 6) {
    raise_warning("imageaffine(): Affine array must have six elements");
    return false;
  }

  for (i = 0; i < nelem; i++) {
    if (affine[i].isInteger()) {
      daffine[i] = affine[i].toInt64();
    } else if (affine[i].isDouble() || affine[i].isString()) {
      daffine[i] = affine[i].toDouble();
    } else {
      raise_warning("imageaffine(): Invalid type for element %i", i);
      return false;
    }
  }

  if (!clip.empty()) {
    if (clip.exists(s_x)) {
      rect.x = clip[s_x].toInt64();
    } else {
      raise_warning("imageaffine(): Missing x position");
      return false;
    }
    if (clip.exists(s_y)) {
      rect.y = clip[s_y].toInt64();
    } else {
      raise_warning("imageaffine(): Missing y position");
      return false;
    } if (clip.exists(s_width)) {
      rect.width = clip[s_width].toInt64();
    } else {
      raise_warning("imageaffine(): Missing width position");
      return false;
    }
    if (clip.exists(s_height)) {
      rect.height = clip[s_height].toInt64();
    } else {
      raise_warning("imageaffine(): Missing height position");
      return false;
    }
    pRect = ▭
  } else {
    rect.x = -1;
    rect.y = -1;
    rect.width = gdImageSX(src);
    rect.height = gdImageSY(src);
    pRect = nullptr;
  }

  if (gdTransformAffineGetImage(&dst, src, pRect, daffine) != GD_TRUE) {
    return false;
  }
  return Variant(req::make<Image>(dst));
}


inline size_t StringData::estimateCap(size_t size) {
  assertx(size <= MaxSize);
  return MemoryManager::sizeClass(size + kStringOverhead);
}


Variant HHVM_FUNCTION(iptcparse, const String& iptcblock) {
  unsigned int inx = 0, len, tagsfound = 0;
  unsigned char *buffer, recnum, dataset, key[16];
  unsigned int str_len = iptcblock.length();
  Array ret;

  buffer = (unsigned char *)iptcblock.c_str();
  while (inx < str_len) { /* find 1st tag */
    if ((buffer[inx] == 0x1c) &&
        ((buffer[inx+1] == 0x01) || (buffer[inx+1] == 0x02))) {
      break;
    } else {
      inx++;
    }
  }

  while (inx < str_len) {
    if (buffer[ inx++ ] != 0x1c) {
     /* we ran against some data which does not conform to IPTC -
        stop parsing! */
      break;
    }

    if ((inx + 4) >= str_len)
      break;

    dataset = buffer[inx++];
    recnum = buffer[inx++];

    if (buffer[inx] & (unsigned char) 0x80) { /* long tag */
      if (inx + 6 >= str_len) break;

      len = (((long)buffer[inx + 2]) << 24) +
            (((long)buffer[inx + 3]) << 16) +
            (((long)buffer[inx + 4]) <<  8) +
            (((long)buffer[inx + 5]));
      inx += 6;
    } else { /* short tag */
      len = (((unsigned short)buffer[inx])<<8) |
            (unsigned short)buffer[inx+1];
      inx += 2;
    }

    snprintf((char *)key, sizeof(key), "%d#%03d",
             (unsigned int)dataset, (unsigned int)recnum);

    if ((len > str_len) || (inx + len) > str_len) {
      break;
    }

    String skey((const char *)key, CopyString);
    if (!ret.exists(skey)) {
      ret.set(skey, Array::CreateVArray());
    }
    auto const lval = ret.lval(skey);
    forceToArray(lval).append(
      String((const char *)(buffer+inx), len, CopyString));
    inx += len;
    tagsfound++;
  }

  if (!tagsfound) {
    return false;
  }
  return ret;
}


String HHVM_FUNCTION(strip_tags,
                     const String& str,
                     const Variant& allowable_tags /* = "" */) {
  return StringUtil::StripHTMLTags(str, allowable_tags.toString());
}


static Variant php_pcre_replace(const String& pattern, const String& subject,
                                const Variant& replace_var, bool callable,
                                int limit, int* replace_count) {
  PCRECache::Accessor accessor;
  if (!pcre_get_compiled_regex_cache(accessor, pattern.get())) {
    return preg_return_bad_regex_error(init_null());
  }
  const pcre_cache_entry* pce = accessor.get();
  if (pce->preg_options & PREG_REPLACE_EVAL) {
    raise_error("preg_replace(): Support for the /e modifier has been removed, use "
                "preg_replace_callback instead");
  }

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

  const char* const* subpat_names = get_subpat_names(pce);
  if (subpat_names == nullptr) {
    return preg_return_internal_error(init_null());
  }

  const char* replace = nullptr;
  const char* replace_end = nullptr;
  int replace_len = 0;
  String replace_val;

  if (!callable) {
    replace_val = replace_var.toString();
    replace = replace_val.data();
    replace_len = replace_val.size();
    replace_end = replace + replace_len;
  }

  StringBuffer result(2 * subject.size());

  try {

    /* Initialize */
    const char* match = nullptr;
    int start_offset = 0;
    pcre_extra extra;
    init_local_extra(&extra, pce->extra);

    const char* walk;     // Used to walk the replacement string
    char walk_last;       // Last walked character
    int match_len;        // Length of the current match
    int backref;          // Backreference number
    int g_notempty = 0;   // If the match should not be empty
    int exec_options = 0; // Options passed to pcre_exec
    while (1) {
      /* Execute the regular expression. */
      int count = pcre_exec(pce->re, &extra, subject.data(), subject.size(),
                            start_offset,
                            exec_options | g_notempty,
                            offsets, size_offsets);

      /* The string was already proved to be valid UTF-8 */
      exec_options |= PCRE_NO_UTF8_CHECK;

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

      const char* piece = subject.data() + start_offset;
      if (count > 0 && offsets[1] >= offsets[0] &&
          (limit == -1 || limit > 0)) {
        if (replace_count) {
          ++*replace_count;
        }
        /* Set the match location in subject */
        match = subject.data() + offsets[0];

        String callable_result;
        if (callable) {
          /* Use custom function to get replacement string and its length. */
          callable_result = preg_do_repl_func(replace_var, subject, offsets,
                                              subpat_names, count);
        } else { /* do regular substitution */
          walk = replace;
          walk_last = 0;
          while (walk < replace_end) {
            if ('\\' == *walk || '$' == *walk) {
              if (walk_last == '\\') {
                walk++;
                walk_last = 0;
                continue;
              }
              if (preg_get_backref(&walk, &backref)) {
                if (backref < count) {
                  match_len = offsets[(backref<<1)+1] - offsets[backref<<1];
                }
                continue;
              }
            }
            walk++;
            walk_last = walk[-1];
          }
        }

        /* copy the part of the string before the match */
        result.append(piece, match-piece);

        /* copy replacement and backrefs */
        int result_len = result.size();

        if (callable) {
          /* Copy result from custom function to buffer and clean up. */
          result.append(callable_result.data(), callable_result.size());
          result_len += callable_result.size();
        } else { /* do regular backreference copying */
          walk = replace;
          walk_last = 0;
          Array params;
          while (walk < replace_end) {
            if ('\\' == *walk || '$' == *walk) {
              if (walk_last == '\\') {
                result.set(result.size() - 1, *walk++);
                walk_last = 0;
                continue;
              }
              if (preg_get_backref(&walk, &backref)) {
                if (backref < count) {
                  match_len = offsets[(backref<<1)+1] - offsets[backref<<1];
                  result.append(
                    subject.data() + offsets[backref<<1],
                    match_len
                  );
                }
                continue;
              }
            }
            result.append(*walk++);
            walk_last = walk[-1];
          }
        }

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

      } else if (count == PCRE_ERROR_NOMATCH || limit == 0) {
        /* 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()) {
          offsets[0] = start_offset;
          offsets[1] = start_offset + 1;
          result.append(piece, 1);
        } else {
          /* stick that last bit of string on our output */
          result.append(piece, subject.size() - start_offset);
          break;
        }
      } else {
        if (pcre_need_log_error(count)) {
          const char* s;
          int size;
          String stemp;
          if (callable) {
            if (replace_var.isObject()) {
              stemp = replace_var.asCObjRef()->getClassName().asString()
                    + "::__invoke";
            } else {
              stemp = replace_var.toString();
            }
            s = stemp.data();
            size = stemp.size();
          } else {
            s = replace_val.data();
            size = replace_val.size();
          }
          pcre_log_error(__FUNCTION__, __LINE__, count,
                         pattern.data(), pattern.size(),
                         subject.data(), subject.size(),
                         s, size,
                         callable, limit, start_offset, g_notempty);
        }
        return preg_return_pcre_error(count, init_null());
      }

      /* 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 next piece. */
      start_offset = offsets[1];
    }

    return preg_return_no_error(result.detach());
  } catch (...) {
    throw;
  }
}


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


String HHVM_FUNCTION(stripslashes,
                     const String& str) {
  if (str.empty()) {
    return str;
  }

  return stringForEachBuffered(str.size(), str,
    [&] (StringBuffer& ret, const char*& src, const char* end) {
      if (*src == '\\' && *++src == '0') {
        ret.append('\0');
        return;
      }
      if (src < end) {
        ret.append(*src);
      }
    });
}


  bool ensureTraceSpace() {
    bool track_realloc = false;
    if (m_traceBufferFilled) {
      m_overflowCalls++;
      return false;
    }
    int new_array_size;
    if (m_traceBufferSize == 0) {
      new_array_size = RuntimeOption::ProfilerTraceBuffer;
    } else {
      new_array_size = m_traceBufferSize *
        RuntimeOption::ProfilerTraceExpansion;
      if (m_maxTraceBuffer != 0 && new_array_size > m_maxTraceBuffer) {
        new_array_size = m_maxTraceBuffer > m_traceBufferSize ?
          m_maxTraceBuffer : m_traceBufferSize;
      }
      if (new_array_size - m_nextTraceEntry <= 5) {
        // for this operation to succeed, we need room for the entry we're
        // adding, two realloc entries, and two entries to mark the end of
        // the trace.
        m_traceBufferFilled = true;
        collectStats("(trace buffer terminated)", false,
                     m_traceBuffer[m_nextTraceEntry++]);
        return false;
      }
      track_realloc = true;
    }
    if (track_realloc) {
      collectStats("(trace buffer realloc)", false,
                   m_traceBuffer[m_nextTraceEntry++]);
    }
    {
      MemoryManager::MaskAlloc masker(*tl_heap);
      auto r = (TraceEntry*)realloc((void*)m_traceBuffer,
                                    new_array_size * sizeof(TraceEntry));

      if (!r) {
        m_traceBufferFilled = true;
        if (m_traceBuffer) {
          collectStats("(trace buffer terminated)", false,
                       m_traceBuffer[m_nextTraceEntry++]);
        }
        return false;
      }
      m_traceBufferSize = new_array_size;
      m_traceBuffer = r;
    }
    if (track_realloc) {
      collectStats("(trace buffer realloc)", true,
                   m_traceBuffer[m_nextTraceEntry++]);
    }
    return true;
  }


  /* implicit */ String(folly::StringPiece s)
  : m_str(StringData::Make(s), NoIncRef{}) {}


static int hphp_gdImageConvolution(gdImagePtr src, float filter[3][3],
                                   float filter_div, float offset) {
  int x, y, i, j, new_a;
  float new_r, new_g, new_b;
  int new_pxl, pxl=0;
  gdImagePtr srcback;

  if (src==nullptr) {
    return 0;
  }

  /* We need the orinal image with each safe neoghb. pixel */
  srcback = gdImageCreateTrueColor (src->sx, src->sy);
  gdImageCopy(srcback, src,0,0,0,0,src->sx,src->sy);

  if (srcback==nullptr) {
    return 0;
  }

  for ( y=0; y<src->sy; y++) {
    for(x=0; x<src->sx; x++) {
      new_r = new_g = new_b = 0;
      new_a = gdImageAlpha(srcback, pxl);

      for (j=0; j<3; j++) {
        int yv = std::min(std::max(y - 1 + j, 0), src->sy - 1);
        for (i=0; i<3; i++) {
          pxl = gdImageGetPixel(srcback, std::min(std::max(x - 1 + i, 0),
                                                  src->sx - 1), yv);
          new_r += (float)gdImageRed(srcback, pxl) * filter[j][i];
          new_g += (float)gdImageGreen(srcback, pxl) * filter[j][i];
          new_b += (float)gdImageBlue(srcback, pxl) * filter[j][i];
        }
      }

      new_r = (new_r/filter_div)+offset;
      new_g = (new_g/filter_div)+offset;
      new_b = (new_b/filter_div)+offset;

      new_r = (new_r > 255.0f)? 255.0f : ((new_r < 0.0f)? 0.0f:new_r);
      new_g = (new_g > 255.0f)? 255.0f : ((new_g < 0.0f)? 0.0f:new_g);
      new_b = (new_b > 255.0f)? 255.0f : ((new_b < 0.0f)? 0.0f:new_b);

      new_pxl = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g,
                                          (int)new_b, new_a);
      if (new_pxl == -1) {
        new_pxl = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g,
                                          (int)new_b, new_a);
      }
      gdImageSetPixel (src, x, y, new_pxl);
    }
  }
  gdImageDestroy(srcback);
  return 1;
}


  void moduleInit() override {
    HHVM_FE(gd_info);
    HHVM_FE(getimagesize);
    HHVM_FE(getimagesizefromstring);
    HHVM_FE(image_type_to_extension);
    HHVM_FE(image_type_to_mime_type);
    HHVM_FE(image2wbmp);
    HHVM_FE(imageaffine);
    HHVM_FE(imageaffinematrixconcat);
    HHVM_FE(imageaffinematrixget);
    HHVM_FE(imagealphablending);
    HHVM_FE(imageantialias);
    HHVM_FE(imagearc);
    HHVM_FE(imagechar);
    HHVM_FE(imagecharup);
    HHVM_FE(imagecolorallocate);
    HHVM_FE(imagecolorallocatealpha);
    HHVM_FE(imagecolorat);
    HHVM_FE(imagecolorclosest);
    HHVM_FE(imagecolorclosestalpha);
    HHVM_FE(imagecolorclosesthwb);
    HHVM_FE(imagecolordeallocate);
    HHVM_FE(imagecolorexact);
    HHVM_FE(imagecolorexactalpha);
    HHVM_FE(imagecolormatch);
    HHVM_FE(imagecolorresolve);
    HHVM_FE(imagecolorresolvealpha);
    HHVM_FE(imagecolorset);
    HHVM_FE(imagecolorsforindex);
    HHVM_FE(imagecolorstotal);
    HHVM_FE(imagecolortransparent);
    HHVM_FE(imageconvolution);
    HHVM_FE(imagecopy);
    HHVM_FE(imagecopymerge);
    HHVM_FE(imagecopymergegray);
    HHVM_FE(imagecopyresampled);
    HHVM_FE(imagecopyresized);
    HHVM_FE(imagecreate);
    HHVM_FE(imagecreatefromgd2part);
    HHVM_FE(imagecreatefromgd);
    HHVM_FE(imagecreatefromgd2);
    HHVM_FE(imagecreatefromgif);
#ifdef HAVE_GD_JPG
    HHVM_FE(imagecreatefromjpeg);
#endif
#ifdef HAVE_GD_PNG
    HHVM_FE(imagecreatefrompng);
#endif
#ifdef HAVE_LIBVPX
    HHVM_FE(imagecreatefromwebp);
#endif
    HHVM_FE(imagecreatefromstring);
    HHVM_FE(imagecreatefromwbmp);
    HHVM_FE(imagecreatefromxbm);
#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED)
    HHVM_FE(imagecreatefromxpm);
#endif
    HHVM_FE(imagecreatetruecolor);
    HHVM_FE(imagecrop);
    HHVM_FE(imagecropauto);
    HHVM_FE(imagedashedline);
    HHVM_FE(imagedestroy);
    HHVM_FE(imageellipse);
    HHVM_FE(imagefill);
    HHVM_FE(imagefilledarc);
    HHVM_FE(imagefilledellipse);
    HHVM_FE(imagefilledpolygon);
    HHVM_FE(imagefilledrectangle);
    HHVM_FE(imagefilltoborder);
    HHVM_FE(imagefilledellipse);
    HHVM_FE(imagefilledpolygon);
    HHVM_FE(imagefilledrectangle);
    HHVM_FE(imagefilltoborder);
    HHVM_FE(imagefilter);
    HHVM_FE(imageflip);
    HHVM_FE(imagefontheight);
    HHVM_FE(imagefontwidth);
#if defined(ENABLE_GD_TTF) && HAVE_LIBFREETYPE
    HHVM_FE(imageftbbox);
    HHVM_FE(imagefttext);
#endif
    HHVM_FE(imagegammacorrect);
    HHVM_FE(imagegd2);
    HHVM_FE(imagegd);
    HHVM_FE(imagegif);
    HHVM_FE(imageinterlace);
    HHVM_FE(imageistruecolor);
#ifdef HAVE_GD_JPG
    HHVM_FE(imagejpeg);
#endif
    HHVM_FE(imagelayereffect);
    HHVM_FE(imageline);
    HHVM_FE(imageloadfont);
#ifdef HAVE_GD_PNG
    HHVM_FE(imagepng);
#endif
#ifdef HAVE_LIBVPX
    HHVM_FE(imagewebp);
#endif
    HHVM_FE(imagepolygon);
    HHVM_FE(imagerectangle);
    HHVM_FE(imagerotate);
    HHVM_FE(imagesavealpha);
    HHVM_FE(imagescale);
    HHVM_FE(imagesetbrush);
    HHVM_FE(imagesetinterpolation);
    HHVM_FE(imagesetpixel);
    HHVM_FE(imagesetstyle);
    HHVM_FE(imagesetthickness);
    HHVM_FE(imagesettile);
    HHVM_FE(imagestring);
    HHVM_FE(imagestringup);
    HHVM_FE(imagesx);
    HHVM_FE(imagesy);
    HHVM_FE(imagetruecolortopalette);
#ifdef ENABLE_GD_TTF
    HHVM_FE(imagettfbbox);
    HHVM_FE(imagettftext);
#endif
    HHVM_FE(imagetypes);
    HHVM_FE(imagewbmp);

    HHVM_FE(iptcembed);
    HHVM_FE(iptcparse);

    HHVM_FE(jpeg2wbmp);
    HHVM_FE(png2wbmp);

    HHVM_FE(imagepalettecopy);

    HHVM_RC_INT(IMG_GIF, IMAGE_TYPE_GIF);
    HHVM_RC_INT(IMG_JPG, IMAGE_TYPE_JPEG);
    HHVM_RC_INT(IMG_JPEG, IMAGE_TYPE_JPEG);
    HHVM_RC_INT(IMG_PNG, IMAGE_TYPE_PNG);
    HHVM_RC_INT(IMG_WBMP, IMAGE_TYPE_WBMP);
    HHVM_RC_INT(IMG_XPM, IMAGE_TYPE_XPM);

    /* special colours for gd */
    HHVM_RC_INT(IMG_COLOR_TILED, gdTiled);
    HHVM_RC_INT(IMG_COLOR_STYLED, gdStyled);
    HHVM_RC_INT(IMG_COLOR_BRUSHED, gdBrushed);
    HHVM_RC_INT(IMG_COLOR_STYLEDBRUSHED, gdStyledBrushed);
    HHVM_RC_INT(IMG_COLOR_TRANSPARENT, gdTransparent);

    /* for imagefilledarc */
    HHVM_RC_INT(IMG_ARC_ROUNDED, gdArc);
    HHVM_RC_INT(IMG_ARC_PIE, gdPie);
    HHVM_RC_INT(IMG_ARC_CHORD, gdChord);
    HHVM_RC_INT(IMG_ARC_NOFILL, gdNoFill);
    HHVM_RC_INT(IMG_ARC_EDGED, gdEdged);

    /* GD2 image format types */
    HHVM_RC_INT(IMG_GD2_RAW, GD2_FMT_RAW);
    HHVM_RC_INT(IMG_GD2_COMPRESSED, GD2_FMT_COMPRESSED);
    HHVM_RC_INT(IMG_FLIP_HORIZONTAL, GD_FLIP_HORINZONTAL);
    HHVM_RC_INT(IMG_FLIP_VERTICAL, GD_FLIP_VERTICAL);
    HHVM_RC_INT(IMG_FLIP_BOTH, GD_FLIP_BOTH);
    HHVM_RC_INT(IMG_EFFECT_REPLACE, gdEffectReplace);
    HHVM_RC_INT(IMG_EFFECT_ALPHABLEND, gdEffectAlphaBlend);
    HHVM_RC_INT(IMG_EFFECT_NORMAL, gdEffectNormal);
    HHVM_RC_INT(IMG_EFFECT_OVERLAY, gdEffectOverlay);

    HHVM_RC_INT(IMG_CROP_DEFAULT, GD_CROP_DEFAULT);
    HHVM_RC_INT(IMG_CROP_TRANSPARENT, GD_CROP_TRANSPARENT);
    HHVM_RC_INT(IMG_CROP_BLACK, GD_CROP_BLACK);
    HHVM_RC_INT(IMG_CROP_WHITE, GD_CROP_WHITE);
    HHVM_RC_INT(IMG_CROP_SIDES, GD_CROP_SIDES);
    HHVM_RC_INT(IMG_CROP_THRESHOLD, GD_CROP_THRESHOLD);

    HHVM_RC_INT(IMG_BELL, GD_BELL);
    HHVM_RC_INT(IMG_BESSEL, GD_BESSEL);
    HHVM_RC_INT(IMG_BILINEAR_FIXED, GD_BILINEAR_FIXED);
    HHVM_RC_INT(IMG_BICUBIC, GD_BICUBIC);
    HHVM_RC_INT(IMG_BICUBIC_FIXED, GD_BICUBIC_FIXED);
    HHVM_RC_INT(IMG_BLACKMAN, GD_BLACKMAN);
    HHVM_RC_INT(IMG_BOX, GD_BOX);
    HHVM_RC_INT(IMG_BSPLINE, GD_BSPLINE);
    HHVM_RC_INT(IMG_CATMULLROM, GD_CATMULLROM);
    HHVM_RC_INT(IMG_GAUSSIAN, GD_GAUSSIAN);
    HHVM_RC_INT(IMG_GENERALIZED_CUBIC, GD_GENERALIZED_CUBIC);
    HHVM_RC_INT(IMG_HERMITE, GD_HERMITE);
    HHVM_RC_INT(IMG_HAMMING, GD_HAMMING);
    HHVM_RC_INT(IMG_HANNING, GD_HANNING);
    HHVM_RC_INT(IMG_MITCHELL, GD_MITCHELL);
    HHVM_RC_INT(IMG_POWER, GD_POWER);
    HHVM_RC_INT(IMG_QUADRATIC, GD_QUADRATIC);
    HHVM_RC_INT(IMG_SINC, GD_SINC);
    HHVM_RC_INT(IMG_NEAREST_NEIGHBOUR, GD_NEAREST_NEIGHBOUR);
    HHVM_RC_INT(IMG_WEIGHTED4, GD_WEIGHTED4);
    HHVM_RC_INT(IMG_TRIANGLE, GD_TRIANGLE);

    HHVM_RC_INT(IMG_AFFINE_TRANSLATE, GD_AFFINE_TRANSLATE);
    HHVM_RC_INT(IMG_AFFINE_SCALE, GD_AFFINE_SCALE);
    HHVM_RC_INT(IMG_AFFINE_ROTATE, GD_AFFINE_ROTATE);
    HHVM_RC_INT(IMG_AFFINE_SHEAR_HORIZONTAL, GD_AFFINE_SHEAR_HORIZONTAL);
    HHVM_RC_INT(IMG_AFFINE_SHEAR_VERTICAL, GD_AFFINE_SHEAR_VERTICAL);

    HHVM_RC_INT(IMG_FILTER_BRIGHTNESS, IMAGE_FILTER_BRIGHTNESS);
    HHVM_RC_INT(IMG_FILTER_COLORIZE, IMAGE_FILTER_COLORIZE);
    HHVM_RC_INT(IMG_FILTER_CONTRAST, IMAGE_FILTER_CONTRAST);
    HHVM_RC_INT(IMG_FILTER_EDGEDETECT, IMAGE_FILTER_EDGEDETECT);
    HHVM_RC_INT(IMG_FILTER_EMBOSS, IMAGE_FILTER_EMBOSS);
    HHVM_RC_INT(IMG_FILTER_GAUSSIAN_BLUR, IMAGE_FILTER_GAUSSIAN_BLUR);
    HHVM_RC_INT(IMG_FILTER_GRAYSCALE, IMAGE_FILTER_GRAYSCALE);
    HHVM_RC_INT(IMG_FILTER_MEAN_REMOVAL, IMAGE_FILTER_MEAN_REMOVAL);
    HHVM_RC_INT(IMG_FILTER_NEGATE, IMAGE_FILTER_NEGATE);
    HHVM_RC_INT(IMG_FILTER_SELECTIVE_BLUR, IMAGE_FILTER_SELECTIVE_BLUR);
    HHVM_RC_INT(IMG_FILTER_SMOOTH, IMAGE_FILTER_SMOOTH);
    HHVM_RC_INT(IMG_FILTER_PIXELATE, IMAGE_FILTER_PIXELATE);

    HHVM_RC_INT(IMAGETYPE_GIF, IMAGE_FILETYPE_GIF);
    HHVM_RC_INT(IMAGETYPE_JPEG, IMAGE_FILETYPE_JPEG);
    HHVM_RC_INT(IMAGETYPE_PNG, IMAGE_FILETYPE_PNG);
    HHVM_RC_INT(IMAGETYPE_SWF, IMAGE_FILETYPE_SWF);
    HHVM_RC_INT(IMAGETYPE_PSD, IMAGE_FILETYPE_PSD);
    HHVM_RC_INT(IMAGETYPE_BMP, IMAGE_FILETYPE_BMP);
    HHVM_RC_INT(IMAGETYPE_TIFF_II, IMAGE_FILETYPE_TIFF_II);
    HHVM_RC_INT(IMAGETYPE_TIFF_MM, IMAGE_FILETYPE_TIFF_MM);
    HHVM_RC_INT(IMAGETYPE_JPC, IMAGE_FILETYPE_JPC);
    HHVM_RC_INT(IMAGETYPE_JP2, IMAGE_FILETYPE_JP2);
    HHVM_RC_INT(IMAGETYPE_JPX, IMAGE_FILETYPE_JPX);
    HHVM_RC_INT(IMAGETYPE_JB2, IMAGE_FILETYPE_JB2);
    HHVM_RC_INT(IMAGETYPE_IFF, IMAGE_FILETYPE_IFF);
    HHVM_RC_INT(IMAGETYPE_WBMP, IMAGE_FILETYPE_WBMP);
    HHVM_RC_INT(IMAGETYPE_XBM, IMAGE_FILETYPE_XBM);
    HHVM_RC_INT(IMAGETYPE_ICO, IMAGE_FILETYPE_ICO);
    HHVM_RC_INT(IMAGETYPE_UNKNOWN, IMAGE_FILETYPE_UNKNOWN);
    HHVM_RC_INT(IMAGETYPE_COUNT, IMAGE_FILETYPE_COUNT);
    HHVM_RC_INT(IMAGETYPE_SWC, IMAGE_FILETYPE_SWC);
    HHVM_RC_INT(IMAGETYPE_JPEG2000, IMAGE_FILETYPE_JPC);

#ifdef GD_VERSION_STRING
    HHVM_RC_STR(GD_VERSION, GD_VERSION_STRING);
#endif

#if defined(GD_MAJOR_VERSION) && defined(GD_MINOR_VERSION) && \
    defined(GD_RELEASE_VERSION) && defined(GD_EXTRA_VERSION)
    HHVM_RC_INT_SAME(GD_MAJOR_VERSION);
    HHVM_RC_INT_SAME(GD_MINOR_VERSION);
    HHVM_RC_INT_SAME(GD_RELEASE_VERSION);
    HHVM_RC_STR_SAME(GD_EXTRA_VERSION);
#endif

#ifdef HAVE_GD_PNG
    HHVM_RC_INT(PNG_NO_FILTER, 0x00);
    HHVM_RC_INT(PNG_FILTER_NONE, 0x08);
    HHVM_RC_INT(PNG_FILTER_SUB, 0x10);
    HHVM_RC_INT(PNG_FILTER_UP, 0x20);
    HHVM_RC_INT(PNG_FILTER_AVG, 0x40);
    HHVM_RC_INT(PNG_FILTER_PAETH, 0x80);
    HHVM_RC_INT(PNG_ALL_FILTERS, 0x08 | 0x10 | 0x20 | 0x40 | 0x80);
#endif

    HHVM_RC_BOOL(GD_BUNDLED, true);

    loadSystemlib();
  }


inline StringData::Proxy* StringData::proxy() {
  return static_cast<Proxy*>(payload());
}


Variant HHVM_FUNCTION(imagecolorstotal, const Resource& image) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  return (gdImageColorsTotal(im));
}


Variant HHVM_FUNCTION(HH_last, 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->getValue(arr->iter_last());
  }

  if (v.isObject()) {
    auto obj = v.asCObjRef();
    // 2. collection
    if (obj->isCollection()) {
      // Pair
      if (obj->collectionType() == CollectionType::Pair) {
        auto const pair = static_cast<c_Pair*>(obj.get());
        return Variant::wrap(*pair->at(1));
      }

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


bool HHVM_FUNCTION(imageellipse, const Resource& image,
    int64_t cx, int64_t cy, int64_t width, int64_t height, int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  color = SetupAntiAliasedColor(im, color);
  gdImageArc(im, cx, cy, width, height, 0, 360, color);
  return true;
}


Variant socket_server_impl(
  const HostURL &hosturl,
  int flags,
  Variant& errnum,
  Variant& errstr,
  const Variant& context /* = uninit_variant */
) {
  errnum = 0;
  errstr = empty_string();
  auto sock = create_new_socket(hosturl, errnum, errstr, context);
  if (!sock) {
    return false;
  }

  sockaddr_storage sa_storage;
  struct sockaddr *sa_ptr;
  size_t sa_size;
  if (!set_sockaddr(sa_storage, sock, hosturl.getHost(),
                    hosturl.getPort(), sa_ptr, sa_size)) {
    return false;
  }
  int yes = 1;
  setsockopt(sock->fd(), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
  if ((flags & k_STREAM_SERVER_BIND) != 0 &&
      ::bind(sock->fd(), sa_ptr, sa_size) < 0) {
    SOCKET_ERROR(sock, "unable to bind to given address", errno);
    return false;
  }
  if ((flags & k_STREAM_SERVER_LISTEN) != 0 && listen(sock->fd(), 128) < 0) {
    SOCKET_ERROR(sock, "unable to listen on socket", errno);
    return false;
  }

  return Variant(std::move(sock));
}


Variant HHVM_FUNCTION(imagecolormatch, const Resource& image1,
                                       const Resource& image2) {
  gdImagePtr im1 = get_valid_image_resource(image1);
  if (!im1) return false;
  gdImagePtr im2 = get_valid_image_resource(image2);
  if (!im2) return false;
  int result;

  result = gdImageColorMatch(im1, im2);
  switch (result) {
  case -1:
    raise_warning("Image1 must be TrueColor");
    return false;
  case -2:
    raise_warning("Image2 must be Palette");
    return false;
  case -3:
    raise_warning("Image1 and Image2 must be the same size");
    return false;
  case -4:
    raise_warning("Image2 must have at least one color");
    return false;
  }

  return true;
}


int string_natural_cmp(char const *a, size_t a_len,
                       char const *b, size_t b_len, int fold_case) {
  char ca, cb;
  char const *ap, *bp;
  char const *aend = a + a_len, *bend = b + b_len;
  int fractional, result;

  if (a_len == 0 || b_len == 0)
    return a_len - b_len;

  ap = a;
  bp = b;
  while (1) {
    ca = *ap; cb = *bp;

    /* skip over leading spaces or zeros */
    while (isspace((int)(unsigned char)ca))
      ca = *++ap;

    while (isspace((int)(unsigned char)cb))
      cb = *++bp;

    /* process run of digits */
    if (isdigit((int)(unsigned char)ca)  &&  isdigit((int)(unsigned char)cb)) {
      fractional = (ca == '0' || cb == '0');

      if (fractional)
        result = compare_left(&ap, aend, &bp, bend);
      else
        result = compare_right(&ap, aend, &bp, bend);

      if (result != 0)
        return result;
      else if (ap == aend && bp == bend)
        /* End of the strings. Let caller sort them out. */
        return 0;
      else {
        /* Keep on comparing from the current point. */
        ca = *ap; cb = *bp;
      }
    }

    if (fold_case) {
      ca = toupper((int)(unsigned char)ca);
      cb = toupper((int)(unsigned char)cb);
    }

    if (ca < cb)
      return -1;
    else if (ca > cb)
      return +1;

    ++ap; ++bp;
    if (ap >= aend && bp >= bend)
      /* The strings compare the same.  Perhaps the caller
         will want to call strcmp to break the tie. */
      return 0;
    else if (ap >= aend)
      return -1;
    else if (bp >= bend)
      return 1;
  }
}


bool socket_create_pair_impl(int domain, int type, int protocol, Variant& fd,
                             bool asStream) {
  check_socket_parameters(domain, type);

  int fds_array[2];
  if (socketpair(domain, type, protocol, fds_array) != 0) {
    SOCKET_ERROR(req::make<StreamSocket>(),
                 "unable to create socket pair",
                 errno);
    return false;
  }

  if (asStream) {
    fd = make_varray(
      Variant(req::make<StreamSocket>(fds_array[0], domain, nullptr, 0, 0.0,
                                      s_socktype_generic)),
      Variant(req::make<StreamSocket>(fds_array[1], domain, nullptr, 0, 0.0,
                                      s_socktype_generic))
    );
  } else {
    fd = make_varray(
      Variant(req::make<ConcreteSocket>(fds_array[0], domain, nullptr, 0, 0.0,
                                        s_socktype_generic)),
      Variant(req::make<ConcreteSocket>(fds_array[1], domain, nullptr, 0, 0.0,
                                        s_socktype_generic))
    );
  }
  return true;
}


void Profiler::beginFrameEx(const char* /*symbol*/) {}


inline strhash_t StringData::hash(const char* s, size_t len) {
  return hash_string_i(s, len);
}


bool str_contains_any_of(const String& haystack, const String& char_list) {
  return strpbrk_impl<true>(haystack, char_list).toBooleanVal();
}


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

  char *name = mcrypt_enc_get_algorithms_name(pm->m_td);
  String ret(name, CopyString);
  mcrypt_free(name);
  return ret;
}


void HHVM_FUNCTION(debug_zval_dump, const Variant& variable) {
  VariableSerializer vs(VariableSerializer::Type::DebugDump);
  vs.serialize(variable, false);
}


  const char *c_str() const {
    return m_str ? m_str->data() : "";
  }


  bool isNumeric() const {
    return m_str ? m_str->isNumeric() : false;
  }


void HHVM_FUNCTION(socket_close,
                   const Resource& socket) {
  cast<Socket>(socket)->close();
}


String HHVM_FUNCTION(hhvm_intrinsics_serialize_keep_dvarrays,
                     const Variant& value) {
  SerializeOptions opts;
  opts.keepDVArrays = true;
  return serialize_impl(value, opts);
}


bool HHVM_FUNCTION(imagepolygon, const Resource& image,
    const Array& points, int64_t num_points, int64_t color) {
  return php_imagepolygon(image, points, num_points, color, 0);
}


static bool preg_get_backref(const char** str, int* backref) {
  char in_brace = 0;
  const char* walk = *str;

  if (walk[1] == 0) {
    return false;
  }

  if (*walk == '$' && walk[1] == '{') {
    in_brace = 1;
    walk++;
  }
  walk++;

  if (*walk >= '0' && *walk <= '9') {
    *backref = *walk - '0';
    walk++;
  } else {
    return false;
  }

  if (*walk && *walk >= '0' && *walk <= '9') {
    *backref = *backref * 10 + *walk - '0';
    walk++;
  }

  if (in_brace) {
    if (*walk == 0 || *walk != '}') {
      return false;
    }
    walk++;
  }

  *str = walk;
  return true;
}


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

  char *name = mcrypt_enc_get_modes_name(pm->m_td);
  String ret(name, CopyString);
  mcrypt_free(name);
  return ret;
}


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


static tag_table_type exif_get_tag_table(int section) {
  switch(section) {
  case SECTION_FILE:      return &tag_table_IFD[0];
  case SECTION_COMPUTED:  return &tag_table_IFD[0];
  case SECTION_ANY_TAG:   return &tag_table_IFD[0];
  case SECTION_IFD0:      return &tag_table_IFD[0];
  case SECTION_THUMBNAIL: return &tag_table_IFD[0];
  case SECTION_COMMENT:   return &tag_table_IFD[0];
  case SECTION_APP0:      return &tag_table_IFD[0];
  case SECTION_EXIF:      return &tag_table_IFD[0];
  case SECTION_FPIX:      return &tag_table_IFD[0];
  case SECTION_GPS:       return &tag_table_GPS[0];
  case SECTION_INTEROP:   return &tag_table_IOP[0];
  case SECTION_APP12:     return &tag_table_IFD[0];
  case SECTION_WINXP:     return &tag_table_IFD[0];
  }
  return &tag_table_IFD[0];
}


  explicit CSRequest(X509_REQ *csr) : m_csr(csr) {
    assertx(m_csr);
  }


static req::ptr<Socket> create_new_socket(
  const HostURL &hosturl,
  Variant& errnum,
  Variant& errstr,
  const Variant& context
) {
  int domain = hosturl.isIPv6() ? AF_INET6 : AF_INET;
  int type = SOCK_STREAM;
  const std::string scheme = hosturl.getScheme();

  if (scheme == "udp" || scheme == "udg") {
    type = SOCK_DGRAM;
  }

  if (scheme == "unix" || scheme == "udg") {
    domain = AF_UNIX;
  }

  req::ptr<Socket> sock;
  int fd = socket(domain, type, 0);
  double timeout = RequestInfo::s_requestInfo.getNoCheck()->
      m_reqInjectionData.getSocketDefaultTimeout();
  req::ptr<StreamContext> streamctx;
  if (context.isResource()) {
    streamctx = cast<StreamContext>(context.toResource());
  }

  auto sslsock = SSLSocket::Create(fd, domain, hosturl, timeout, streamctx);
  if (sslsock) {
    sock = sslsock;
  } else {
    sock = req::make<StreamSocket>(fd, domain, hosturl.getHost().c_str(),
                                   hosturl.getPort());
  }

  if (!sock->valid()) {
    SOCKET_ERROR(sock, "unable to create socket", errno);
    errnum = sock->getError();
    errstr = HHVM_FN(socket_strerror)(sock->getError());
    sock.reset();
  }
  return sock;
}


  void imDump(void *ptrs[], int &n) {
    int i = 0;
    for (auto iter = m_alloced.begin(); iter != m_alloced.end(); ++i, ++iter) {
      void *p = *iter;
      assertx(p);
      if (i < n) ptrs[i] = p;
      int ln;
      size_t size;
      memcpy(&ln, p, sizeof(ln));
      memcpy(&size, (char*)p + sizeof(ln), sizeof(size));
      printf("%d: (%p, %lu)\n", ln, p, size);
    }
    n = (i < n) ? i : n;
  }


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


Variant HHVM_FUNCTION(imagecreatefromxpm, const String& filename) {
  gdImagePtr im =
    _php_image_create_from(filename, -1, -1, -1, -1,
                           PHP_GDIMG_TYPE_XPM, "XPM",
                           (gdImagePtr(*)())gdImageCreateFromXpm,
                           (gdImagePtr(*)())nullptr);
  if (im == nullptr) {
    return false;
  }
  return Variant(req::make<Image>(im));
}


String HHVM_FUNCTION(HH_serialize_with_options,
                     const Variant& value, const Array& options) {
  SerializeOptions opts;
  opts.keepDVArrays = options.exists(s_keepDVArrays) &&
    options[s_keepDVArrays].toBoolean();
  opts.forcePHPArrays = options.exists(s_forcePHPArrays) &&
    options[s_forcePHPArrays].toBoolean();
  opts.warnOnHackArrays = options.exists(s_warnOnHackArrays) &&
    options[s_warnOnHackArrays].toBoolean();
  opts.warnOnPHPArrays = options.exists(s_warnOnPHPArrays) &&
    options[s_warnOnPHPArrays].toBoolean();
  opts.ignoreLateInit = options.exists(s_ignoreLateInit) &&
    options[s_ignoreLateInit].toBoolean();
  opts.serializeProvenanceAndLegacy =
    options.exists(s_serializeProvenanceAndLegacy) &&
    options[s_serializeProvenanceAndLegacy].toBoolean();
  return serialize_impl(value, opts);
}


int64_t HHVM_FUNCTION(socket_last_error,
                      const Variant& socket /* = null */) {
  if (!socket.isNull()) {
    return cast<Socket>(socket)->getError();
  }
  return Socket::getLastError();
}


  void endFrame(const TypedValue* /*retval*/, const char* symbol,
                bool /*endMain*/ = false) override {
    doTrace(symbol, true);
  }


TypedValue HHVM_FUNCTION(strncasecmp,
                         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_strncasecmp(
    str1.data(), str1.size(),
    str2.data(), str2.size(), len
  ));
}


  void writeStats(Array& /*ret*/) override {
    fprintf(stderr, "writeStats start\n");
    // RetSame: the return value is the same instance every time
    // HasThis: call has a this argument
    // AllSame: all returns were the same data even though args are different
    // MemberCount: number of different arg sets (including this)
    fprintf(stderr, "Count Function MinSerLen MaxSerLen RetSame HasThis "
            "AllSame MemberCount\n");
    for (auto& me : m_memos) {
      if (me.second.m_ignore) continue;
      if (me.second.m_count == 1) continue;
      int min_ser_len = 999999999;
      int max_ser_len = 0;
      int count = 0;
      int member_count = 0;
      bool all_same = true;
      if (me.second.m_has_this) {
        bool any_multiple = false;
        auto& fr = me.second.m_member_memos.begin()->second.m_return_value;
        member_count = me.second.m_member_memos.size();
        for (auto& mme : me.second.m_member_memos) {
          if (mme.second.m_return_value != fr) all_same = false;
          count += mme.second.m_count;
          auto ser_len = mme.second.m_return_value.length();
          min_ser_len = std::min<int64_t>(min_ser_len, ser_len);
          max_ser_len = std::max<int64_t>(max_ser_len, ser_len);
          if (mme.second.m_count > 1) any_multiple = true;
        }
        if (!any_multiple && !all_same) continue;
      } else {
        min_ser_len = max_ser_len = me.second.m_return_value.length();
        count = me.second.m_count;
        all_same = me.second.m_ret_tv_same;
      }
      fprintf(stderr, "%d %s %d %d %s %s %s %d\n",
              count, me.first.data(),
              min_ser_len, max_ser_len,
              me.second.m_ret_tv_same ? " true" : "false",
              me.second.m_has_this ? " true" : "false",
              all_same ? " true" : "false",
              member_count
             );
    }
    fprintf(stderr, "writeStats end\n");
  }


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


static int get_php_tiff_bytes_per_format(int format) {
  int size = sizeof(php_tiff_bytes_per_format)/sizeof(int);
  if (format >= size) {
    raise_warning("Invalid format %d", format);
    format = 0;
  }
  return php_tiff_bytes_per_format[format];
}


bool HHVM_FUNCTION(is_scalar, const Variant& v) {
  return v.isScalar();
}


pcre_literal_data::pcre_literal_data(const char* pattern, int coptions) {
  if (coptions & ~PCRE_CASELESS) {
    return;
  }

  auto p = pattern;
  if (*p == '^') {
    match_start = true;
    p++;
  }

  std::string pattern_buffer;
  while (isalnum((unsigned char)*p) || (*p && strchr("/\\ :-_", *p))) {
    // backslash + alphanumeric character --> not a literal (i.e. \d).
    // backslash + non-alphanumeric character --> literal symbol (i.e. \.)
    if (*p == '\\') {
      if (!p[1] || isalnum((unsigned char)p[1])) {
        break;
      } else {
        p++;
      }
    }
    pattern_buffer += *p++;
  }
  if (*p == '$') {
    match_end = true;
    p++;
  }
  if (!*p) {
    /* This is an encoding of a literal string. */
    case_insensitive = coptions & PCRE_CASELESS;
    literal_str = std::move(pattern_buffer);
  }
}


int64_t HHVM_FUNCTION(levenshtein,
                      const String& str1,
                      const String& str2,
                      int cost_ins /* = 1 */,
                      int cost_rep /* = 1 */,
                      int cost_del /* = 1 */) {
  return string_levenshtein(str1.data(), str1.size(), str2.data(), str2.size(),
                            cost_ins, cost_rep, cost_del);
}


PregWithErrorGuard::~PregWithErrorGuard() {
  if (*rl_last_error_code == PHP_PCRE_NO_ERROR) {
    error.setNull();
  } else {
    error = *rl_last_error_code;
  }
  *rl_last_error_code = prior_error;
}


  void validateStack(TraceIterator tIt, Stats& stats) {
    auto enteredName = m_stack.back().trace->symbol;
    auto exitedName = tIt->symbol;
    if ((exitedName != nullptr) &&
        ((enteredName == nullptr) || (strcmp(enteredName, exitedName) != 0))) {
      // We have a few special names that we form on entry. We don't
      // have the information to form them again on exit, so tolerate
      // them here. See EventHook::GetFunctionNameForProfiler().
      if ((enteredName != nullptr) &&
          ((strncmp(enteredName, "run_init::", 10) == 0) ||
           (strcmp(enteredName, "_") == 0))) return;
      bool fixed = false;
      if (m_stack.size() > 1) {
        auto callerName = (m_stack.end() - 2)->trace->symbol;
        if ((callerName != nullptr) && (strcmp(callerName, exitedName) == 0)) {
          // We have an exit for Foo(), but we were in Bar(). However,
          // it appears that Foo() was the caller of Bar(). This
          // suggests we've missed the exit event for Bar() and have
          // the exit event for Foo() in hand. So remove Bar() to
          // re-balance the stack.
          m_stack.pop_back();
          fixed = true;
        }
      }
      // The first few bad arcs typically point at the problem, so
      // report them. The rest we'll just count.
      if (++m_badArcCount < 20) {
        std::string badArc;
        if (fixed) {
          badArc = folly::format("(warning: corrected bad arc #{}: "
                                 "enter '{}', exit '{}')",
                                 m_badArcCount,
                                 enteredName, exitedName).str();
        } else {
          badArc = folly::format("(error: bad arc #{}: "
                                 "enter '{}', exit '{}')",
                                 m_badArcCount,
                                 enteredName, exitedName).str();
        }
        ++stats[badArc.data()].count;
      }
    }
  }


_php_image_output_putbuf(struct gdIOCtx* /*ctx*/, const void* buf, int len) {
  return php_write((void *)buf, len);
}


  const char *data() const {
    return m_str ? m_str->data() : "";
  }


Variant str_ireplace(const Variant& search, const Variant& replace,
                     const Variant& subject, int64_t& count) {
  Variant ret = str_replace(search, replace, subject, count, false);
  return ret;
}


explicit Key(EVP_PKEY *key) : m_key(key) { assertx(m_key);}


static inline uint64_t cpuTime(int64_t /*MHz*/) {
  return gettime_ns(CLOCK_THREAD_CPUTIME_ID);
}


explicit StrNR(const String &s) : m_px(s.get()) {} // XXX


static void _php_image_output_putc(struct gdIOCtx* /*ctx*/, int c) {
  /* without the following downcast, the write will fail
   * (i.e., will write a zero byte) for all
   * big endian architectures:
   */
  unsigned char ch = (unsigned char) c;
  php_write(&ch, 1);
}


Variant HHVM_FUNCTION(imageaffinematrixconcat,
                      const Array& m1,
                      const Array& m2) {
  int nelem1 = m1.size();
  int nelem2 = m2.size();
  int i;
  double dm1[6];
  double dm2[6];
  double dmr[6];
  Array ret = Array::CreateDArray();

  if (nelem1 != 6 || nelem2 != 6) {
    raise_warning("imageaffinematrixconcat(): Affine array must "
                  "have six elements");
    return false;
  }

  for (i = 0; i < 6; i++) {
    if (m1[i].isInteger()) {
      dm1[i] = m1[i].toInt64();
    } else if (m1[i].isDouble() || m1[i].isString()) {
      dm1[i] = m1[i].toDouble();
    } else {
      raise_warning("imageaffinematrixconcat(): Invalid type for "
                    "element %i", i);
      return false;
    }
    if (m2[i].isInteger()) {
      dm2[i] = m2[i].toInt64();
    } else if (m2[i].isDouble() || m2[i].isString()) {
      dm2[i] = m2[i].toDouble();
    } else {
      raise_warning("imageaffinematrixconcat():Invalid type for"
                    "element %i", i);
      return false;
    }
  }
  if (gdAffineConcat(dmr, dm1, dm2) != GD_TRUE) {
    return false;
  }

  for (i = 0; i < 6; i++) {
    ret.set(String(i, CopyString), dmr[i]);
  }
  return ret;
}


static void exif_thumbnail_build(image_info_type *ImageInfo) {
  size_t new_size, new_move, new_value;
  char *new_data;
  void *value_ptr;
  int i, byte_count;
  image_info_list *info_list;
  image_info_data *info_data;

  if (!ImageInfo->read_thumbnail || !ImageInfo->Thumbnail.offset ||
      !ImageInfo->Thumbnail.size) {
    return; /* ignore this call */
  }
  switch(ImageInfo->Thumbnail.filetype) {
  default:
  case IMAGE_FILETYPE_JPEG:
    /* done */
    break;
  case IMAGE_FILETYPE_TIFF_II:
  case IMAGE_FILETYPE_TIFF_MM:
    info_list = &ImageInfo->info_list[SECTION_THUMBNAIL];
    new_size  = 8 + 2 + info_list->count * 12 + 4;
    new_value= new_size; /* offset for ifd values outside ifd directory */
    for (i=0; i<info_list->count; i++) {
      info_data  = &info_list->list[i];
      byte_count =
        get_php_tiff_bytes_per_format(info_data->format) * info_data->length;
      if (byte_count > 4) {
        new_size += byte_count;
      }
    }
    new_move = new_size;
    new_data = (char *)IM_REALLOC(ImageInfo->Thumbnail.data,
                                  ImageInfo->Thumbnail.size + new_size);
    CHECK_ALLOC(new_data, ImageInfo->Thumbnail.size + new_size);
    ImageInfo->Thumbnail.data = new_data;
    memmove(ImageInfo->Thumbnail.data + new_move,
            ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size);
    ImageInfo->Thumbnail.size += new_size;
    /* fill in data */
    if (ImageInfo->motorola_intel) {
      memmove(new_data, "MM\x00\x2a\x00\x00\x00\x08", 8);
    } else {
      memmove(new_data, "II\x2a\x00\x08\x00\x00\x00", 8);
    }
    new_data += 8;
    php_ifd_set16u(new_data, info_list->count, ImageInfo->motorola_intel);
    new_data += 2;
    for (i=0; i<info_list->count; i++) {
      info_data  = &info_list->list[i];
      byte_count =
        get_php_tiff_bytes_per_format(info_data->format) * info_data->length;
      if (info_data->tag==TAG_STRIP_OFFSETS ||
          info_data->tag==TAG_JPEG_INTERCHANGE_FORMAT) {
        php_ifd_set16u(new_data + 0, info_data->tag,
                       ImageInfo->motorola_intel);
        php_ifd_set16u(new_data + 2, TAG_FMT_ULONG,
                       ImageInfo->motorola_intel);
        php_ifd_set32u(new_data + 4, 1, ImageInfo->motorola_intel);
        php_ifd_set32u(new_data + 8, new_move, ImageInfo->motorola_intel);
      } else {
        php_ifd_set16u(new_data + 0, info_data->tag,
                       ImageInfo->motorola_intel);
        php_ifd_set16u(new_data + 2, info_data->format,
                       ImageInfo->motorola_intel);
        php_ifd_set32u(new_data + 4, info_data->length,
                       ImageInfo->motorola_intel);
        value_ptr = exif_ifd_make_value(info_data, ImageInfo->motorola_intel);
        if (byte_count <= 4) {
          memmove(new_data+8, value_ptr, 4);
        } else {
          php_ifd_set32u(new_data+8, new_value, ImageInfo->motorola_intel);
          memmove(ImageInfo->Thumbnail.data+new_value, value_ptr, byte_count);
          new_value += byte_count;
        }
        if (value_ptr) IM_FREE(value_ptr);
      }
      new_data += 12;
    }
    memset(new_data, 0, 4); /* next ifd pointer */
    break;
  }
}


static bool set_sockaddr(sockaddr_storage &sa_storage, req::ptr<Socket> sock,
                         const String& addr, int port,
                         struct sockaddr *&sa_ptr, size_t &sa_size) {
  // Always zero it out:
  // - fields are added over time; zeroing it out is future-proofing; for
  //   example, sockaddr_in6 did not originally include sin6_scope_id or
  //   sin6_flowinfo.
  // - required for all on MacOS for correct behavior
  // - on Linux, required for sockaddr_un to deal with buggy sun_path readers
  //   (they should look at the length)
  memset(&sa_storage, 0, sizeof(struct sockaddr_storage));
  struct sockaddr *sock_type = (struct sockaddr*) &sa_storage;
  switch (sock->getType()) {
  case AF_UNIX:
    {
#ifdef _MSC_VER
      return false;
#else
      struct sockaddr_un *sa = (struct sockaddr_un *)sock_type;
      sa->sun_family = AF_UNIX;
      if (addr.length() > sizeof(sa->sun_path)) {
        raise_warning(
          "Unix socket path length (%ld) is larger than system limit (%lu)",
          addr.length(),
          sizeof(sa->sun_path)
        );
        return false;
      }
      memcpy(sa->sun_path, addr.data(), addr.length());
      sa_ptr = (struct sockaddr *)sa;
      sa_size = offsetof(struct sockaddr_un, sun_path) + addr.length();
#ifdef __linux__
      if (addr.length() == 0) {
        // Linux supports 3 kinds of unix sockets; behavior of this struct
        // is in `man 7 unix`; relevant parts:
        // - unnamed: 0-length path. As paths are not required to be
        //   null-terminated, this needs to be undicated by the size.
        //   These might be created by `socketpair()`, for eaxmple.
        // - pathname (common): nothing strange. struct size technically
        //   indicates length, but null terminators are usually set. This
        //   does matter if addr.length() == size of the char array though
        // - abstract: these have a meaningful name, but start with `\0`
        //
        // Setting sa_size to indicate a 0-length path is required to
        // distinguish between unnamed and abstract.
        sa_size = offsetof(struct sockaddr_un, sun_path);
      }
#endif

#endif // ifdef _MSC_VER
    }
    break;
  case AF_INET:
    {
      struct sockaddr_in *sa = (struct sockaddr_in *)sock_type;
      sa->sin_family = AF_INET;
      sa->sin_port = htons((unsigned short) port);
      if (!php_set_inet_addr(sa, addr.c_str(), sock)) {
        return false;
      }
      sa_ptr = (struct sockaddr *)sa;
      sa_size = sizeof(struct sockaddr_in);
    }
    break;
  case AF_INET6:
    {
      struct sockaddr_in6 *sa = (struct sockaddr_in6 *)sock_type;
      sa->sin6_family = AF_INET6;
      sa->sin6_port = htons((unsigned short) port);
      if (!php_set_inet6_addr(sa, addr.c_str(), sock)) {
        return false;
      }
      sa_ptr = (struct sockaddr *)sa;
      sa_size = sizeof(struct sockaddr_in6);
    }
    break;
  default:
    raise_warning("unsupported socket type '%d', must be "
                    "AF_UNIX, AF_INET, or AF_INET6", sock->getType());
    return false;
  }
#ifdef __APPLE__
  // This field is not in the relevant standards, not defined on Linux, but is
  // technically required on MacOS (and other BSDs) according to the man pages:
  // - `man 4 netintro` covers the base sa_len
  // - `man 4 unix` and `man 4 inet6` cover AF_UNIX sun_len and AF_INET6
  //    sin6_len
  // - ... At least MacOS Catalina includes the wrong `man 4 inet`. Look at the
  //   (Net|Free|Open)BSD `man 4 inet` instead.
  //   The MacOS man page says it starts with `sin_family`, which would conflict
  //   with the base sockaddr definition. `sin_len` is actually the first field
  //   in the header file, matching `sa_len`.
  sa_ptr->sa_len = sa_size;
#endif
  return true;
}


static int xbuf_format_converter(char **outbuf, const char *fmt, va_list ap)
{
  register char *s = nullptr;
  char *q;
  int s_len;

  register int min_width = 0;
  int precision = 0;
  enum {
    LEFT, RIGHT
  } adjust;
  char pad_char;
  char prefix_char;

  double fp_num;
  wide_int i_num = (wide_int) 0;
  u_wide_int ui_num;

  char num_buf[NUM_BUF_SIZE];
  char char_buf[2];      /* for printing %% and %<unknown> */

#ifdef HAVE_LOCALE_H
  struct lconv *lconv = nullptr;
#endif

  /*
   * Flag variables
   */
  length_modifier_e modifier;
  boolean_e alternate_form;
  boolean_e print_sign;
  boolean_e print_blank;
  boolean_e adjust_precision;
  boolean_e adjust_width;
  int is_negative;

  int size = 240;
  char *result = (char *)malloc(size);
  int outpos = 0;

  while (*fmt) {
    if (*fmt != '%') {
      appendchar(&result, &outpos, &size, *fmt);
    } else {
      /*
       * Default variable settings
       */
      adjust = RIGHT;
      alternate_form = print_sign = print_blank = NO;
      pad_char = ' ';
      prefix_char = NUL;

      fmt++;

      /*
       * Try to avoid checking for flags, width or precision
       */
      if (isascii((int)*fmt) && !islower((int)*fmt)) {
        /*
         * Recognize flags: -, #, BLANK, +
         */
        for (;; fmt++) {
          if (*fmt == '-')
            adjust = LEFT;
          else if (*fmt == '+')
            print_sign = YES;
          else if (*fmt == '#')
            alternate_form = YES;
          else if (*fmt == ' ')
            print_blank = YES;
          else if (*fmt == '0')
            pad_char = '0';
          else
            break;
        }

        /*
         * Check if a width was specified
         */
        if (isdigit((int)*fmt)) {
          STR_TO_DEC(fmt, min_width);
          adjust_width = YES;
        } else if (*fmt == '*') {
          min_width = va_arg(ap, int);
          fmt++;
          adjust_width = YES;
          if (min_width < 0) {
            adjust = LEFT;
            min_width = -min_width;
          }
        } else
          adjust_width = NO;

        /*
         * Check if a precision was specified
         *
         * XXX: an unreasonable amount of precision may be specified
         * resulting in overflow of num_buf. Currently we
         * ignore this possibility.
         */
        if (*fmt == '.') {
          adjust_precision = YES;
          fmt++;
          if (isdigit((int)*fmt)) {
            STR_TO_DEC(fmt, precision);
          } else if (*fmt == '*') {
            precision = va_arg(ap, int);
            fmt++;
            if (precision < 0)
              precision = 0;
          } else
            precision = 0;
        } else
          adjust_precision = NO;
      } else
        adjust_precision = adjust_width = NO;

      /*
       * Modifier check
       */
      switch (*fmt) {
        case 'L':
          fmt++;
          modifier = LM_LONG_DOUBLE;
          break;
        case 'I':
          fmt++;
#if SIZEOF_LONG_LONG
          if (*fmt == '6' && *(fmt+1) == '4') {
            fmt += 2;
            modifier = LM_LONG_LONG;
          } else
#endif
            if (*fmt == '3' && *(fmt+1) == '2') {
              fmt += 2;
              modifier = LM_LONG;
            } else {
#ifdef _WIN64
              modifier = LM_LONG_LONG;
#else
              modifier = LM_LONG;
#endif
            }
          break;
        case 'l':
          fmt++;
#if SIZEOF_LONG_LONG
          if (*fmt == 'l') {
            fmt++;
            modifier = LM_LONG_LONG;
          } else
#endif
            modifier = LM_LONG;
          break;
        case 'z':
          fmt++;
          modifier = LM_SIZE_T;
          break;
        case 'j':
          fmt++;
#if SIZEOF_INTMAX_T
          modifier = LM_INTMAX_T;
#else
          modifier = LM_SIZE_T;
#endif
          break;
        case 't':
          fmt++;
#if SIZEOF_PTRDIFF_T
          modifier = LM_PTRDIFF_T;
#else
          modifier = LM_SIZE_T;
#endif
          break;
        case 'h':
          fmt++;
          if (*fmt == 'h') {
            fmt++;
          }
          /* these are promoted to int, so no break */
        default:
          modifier = LM_STD;
          break;
      }

      /*
       * Argument extraction and printing.
       * First we determine the argument type.
       * Then, we convert the argument to a string.
       * On exit from the switch, s points to the string that
       * must be printed, s_len has the length of the string
       * The precision requirements, if any, are reflected in s_len.
       *
       * NOTE: pad_char may be set to '0' because of the 0 flag.
       *   It is reset to ' ' by non-numeric formats
       */
      switch (*fmt) {
        case 'u':
          switch(modifier) {
            default:
              i_num = (wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              i_num = (wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              i_num = (wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              i_num = (wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              i_num = (wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              i_num = (wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          /*
           * The rest also applies to other integer formats, so fall
           * into that case.
           */
        case 'd':
        case 'i':
          /*
           * Get the arg if we haven't already.
           */
          if ((*fmt) != 'u') {
            switch(modifier) {
              default:
                i_num = (wide_int) va_arg(ap, int);
                break;
              case LM_LONG_DOUBLE:
                goto fmt_error;
              case LM_LONG:
                i_num = (wide_int) va_arg(ap, long int);
                break;
              case LM_SIZE_T:
#if SIZEOF_SSIZE_T
                i_num = (wide_int) va_arg(ap, ssize_t);
#else
                i_num = (wide_int) va_arg(ap, size_t);
#endif
                break;
#if SIZEOF_LONG_LONG
              case LM_LONG_LONG:
                i_num = (wide_int) va_arg(ap, wide_int);
                break;
#endif
#if SIZEOF_INTMAX_T
              case LM_INTMAX_T:
                i_num = (wide_int) va_arg(ap, intmax_t);
                break;
#endif
#if SIZEOF_PTRDIFF_T
              case LM_PTRDIFF_T:
                i_num = (wide_int) va_arg(ap, ptrdiff_t);
                break;
#endif
            }
          }
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);

          if (*fmt != 'u') {
            if (is_negative)
              prefix_char = '-';
            else if (print_sign)
              prefix_char = '+';
            else if (print_blank)
              prefix_char = ' ';
          }
          break;

        case 'o':
          switch(modifier) {
            default:
              ui_num = (u_wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              ui_num = (u_wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              ui_num = (u_wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              ui_num = (u_wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              ui_num = (u_wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          s = ap_php_conv_p2(ui_num, 3, *fmt,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);
          if (alternate_form && *s != '0') {
            *--s = '0';
            s_len++;
          }
          break;

        case 'x':
        case 'X':
          switch(modifier) {
            default:
              ui_num = (u_wide_int) va_arg(ap, unsigned int);
              break;
            case LM_LONG_DOUBLE:
              goto fmt_error;
            case LM_LONG:
              ui_num = (u_wide_int) va_arg(ap, unsigned long int);
              break;
            case LM_SIZE_T:
              ui_num = (u_wide_int) va_arg(ap, size_t);
              break;
#if SIZEOF_LONG_LONG
            case LM_LONG_LONG:
              ui_num = (u_wide_int) va_arg(ap, u_wide_int);
              break;
#endif
#if SIZEOF_INTMAX_T
            case LM_INTMAX_T:
              ui_num = (u_wide_int) va_arg(ap, uintmax_t);
              break;
#endif
#if SIZEOF_PTRDIFF_T
            case LM_PTRDIFF_T:
              ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
              break;
#endif
          }
          s = ap_php_conv_p2(ui_num, 4, *fmt,
                &num_buf[NUM_BUF_SIZE], &s_len);
          FIX_PRECISION(adjust_precision, precision, s, s_len);
          if (alternate_form && i_num != 0) {
            *--s = *fmt;  /* 'x' or 'X' */
            *--s = '0';
            s_len += 2;
          }
          break;

        case 's':
        case 'v':
          s = va_arg(ap, char *);
          if (s != nullptr) {
            s_len = strlen(s);
            if (adjust_precision && precision < s_len)
              s_len = precision;
          } else {
            s = const_cast<char*>(s_null);
            s_len = S_NULL_LEN;
          }
          pad_char = ' ';
          break;

        case 'f':
        case 'F':
        case 'e':
        case 'E':
          switch(modifier) {
            case LM_LONG_DOUBLE:
              fp_num = (double) va_arg(ap, long double);
              break;
            case LM_STD:
              fp_num = va_arg(ap, double);
              break;
            default:
              goto fmt_error;
          }

          if (std::isnan(fp_num)) {
            s = const_cast<char*>("nan");
            s_len = 3;
          } else if (std::isinf(fp_num)) {
            s = const_cast<char*>("inf");
            s_len = 3;
          } else {
#ifdef HAVE_LOCALE_H
            if (!lconv) {
              lconv = localeconv();
            }
#endif
            s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
             (adjust_precision == NO) ? FLOAT_DIGITS : precision,
             (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
                  &is_negative, &num_buf[1], &s_len);
            if (is_negative)
              prefix_char = '-';
            else if (print_sign)
              prefix_char = '+';
            else if (print_blank)
              prefix_char = ' ';
          }
          break;

        case 'g':
        case 'k':
        case 'G':
        case 'H':
          switch(modifier) {
            case LM_LONG_DOUBLE:
              fp_num = (double) va_arg(ap, long double);
              break;
            case LM_STD:
              fp_num = va_arg(ap, double);
              break;
            default:
              goto fmt_error;
          }

          if (std::isnan(fp_num)) {
             s = const_cast<char*>("NAN");
             s_len = 3;
             break;
           } else if (std::isinf(fp_num)) {
             if (fp_num > 0) {
               s = const_cast<char*>("INF");
               s_len = 3;
             } else {
               s = const_cast<char*>("-INF");
               s_len = 4;
             }
             break;
           }

          if (adjust_precision == NO)
            precision = FLOAT_DIGITS;
          else if (precision == 0)
            precision = 1;
          /*
           * * We use &num_buf[ 1 ], so that we have room for the sign
           */
#ifdef HAVE_LOCALE_H
          if (!lconv) {
            lconv = localeconv();
          }
#endif
          s = php_gcvt(fp_num, precision,
                       (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT,
                       (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
          if (*s == '-')
            prefix_char = *s++;
          else if (print_sign)
            prefix_char = '+';
          else if (print_blank)
            prefix_char = ' ';

          s_len = strlen(s);

          if (alternate_form && (q = strchr(s, '.')) == nullptr)
            s[s_len++] = '.';
          break;

        case 'c':
          char_buf[0] = (char) (va_arg(ap, int));
          s = &char_buf[0];
          s_len = 1;
          pad_char = ' ';
          break;

        case '%':
          char_buf[0] = '%';
          s = &char_buf[0];
          s_len = 1;
          pad_char = ' ';
          break;

        case 'n':
          *(va_arg(ap, int *)) = outpos;
          goto skip_output;

          /*
           * Always extract the argument as a "char *" pointer. We
           * should be using "void *" but there are still machines
           * that don't understand it.
           * If the pointer size is equal to the size of an unsigned
           * integer we convert the pointer to a hex number, otherwise
           * we print "%p" to indicate that we don't handle "%p".
           */
        case 'p':
          if (sizeof(char *) <= sizeof(u_wide_int)) {
            ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
            s = ap_php_conv_p2(ui_num, 4, 'x',
                &num_buf[NUM_BUF_SIZE], &s_len);
            if (ui_num != 0) {
              *--s = 'x';
              *--s = '0';
              s_len += 2;
            }
          } else {
            s = const_cast<char*>("%p");
            s_len = 2;
          }
          pad_char = ' ';
          break;

        case NUL:
          /*
           * The last character of the format string was %.
           * We ignore it.
           */
          continue;

fmt_error:
        throw Exception("Illegal length modifier specified '%c'", *fmt);

          /*
           * The default case is for unrecognized %'s.
           * We print %<char> to help the user identify what
           * option is not understood.
           * This is also useful in case the user wants to pass
           * the output of format_converter to another function
           * that understands some other %<char> (like syslog).
           * Note that we can't point s inside fmt because the
           * unknown <char> could be preceded by width etc.
           */
        default:
          char_buf[0] = '%';
          char_buf[1] = *fmt;
          s = char_buf;
          s_len = 2;
          pad_char = ' ';
          break;
      }

      if (prefix_char != NUL) {
        *--s = prefix_char;
        s_len++;
      }
      if (adjust_width && adjust == RIGHT && min_width > s_len) {
        if (pad_char == '0' && prefix_char != NUL) {
          appendchar(&result, &outpos, &size, *s);
          s++;
          s_len--;
          min_width--;
        }
        for (int i = 0; i < min_width - s_len; i++) {
          appendchar(&result, &outpos, &size, pad_char);
        }
      }
      /*
       * Print the (for now) non-null terminated string s.
       */
      appendsimplestring(&result, &outpos, &size, s, s_len);

      if (adjust_width && adjust == LEFT && min_width > s_len) {
        for (int i = 0; i < min_width - s_len; i++) {
          appendchar(&result, &outpos, &size, pad_char);
        }
      }
    }
skip_output:
    fmt++;
  }
  /*
   * Add the terminating null here since it wasn't added incrementally above
   * once the whole string has been composed.
   */
  appendchar(&result, &outpos, &size, NUL);
  *outbuf = result;
  return outpos - 1;
}


static struct gfxinfo *php_handle_xbm(const req::ptr<File>& stream) {
  struct gfxinfo *result;
  php_get_xbm(stream, &result);
  return result;
}


  void moduleInit() override {
    HHVM_RC_INT_SAME(AF_UNIX);
    HHVM_RC_INT_SAME(AF_INET);
    HHVM_RC_INT_SAME(AF_INET6);
    HHVM_RC_INT_SAME(SOCK_STREAM);
    HHVM_RC_INT_SAME(SOCK_DGRAM);
    HHVM_RC_INT_SAME(SOCK_RAW);
    HHVM_RC_INT_SAME(SOCK_SEQPACKET);
    HHVM_RC_INT_SAME(SOCK_RDM);

    HHVM_RC_INT_SAME(MSG_OOB);
    HHVM_RC_INT_SAME(MSG_WAITALL);
    HHVM_RC_INT_SAME(MSG_CTRUNC);
    HHVM_RC_INT_SAME(MSG_TRUNC);
    HHVM_RC_INT_SAME(MSG_PEEK);
    HHVM_RC_INT_SAME(MSG_DONTROUTE);
#ifdef MSG_EOR
    HHVM_RC_INT_SAME(MSG_EOR);
#endif
#ifdef MSG_EOF
    HHVM_RC_INT_SAME(MSG_EOF);
#endif
#ifdef MSG_CONFIRM
    HHVM_RC_INT_SAME(MSG_CONFIRM);
#endif
#ifdef MSG_ERRQUEUE
    HHVM_RC_INT_SAME(MSG_ERRQUEUE);
#endif
#ifdef MSG_NOSIGNAL
    HHVM_RC_INT_SAME(MSG_NOSIGNAL);
#endif
#ifdef MSG_DONTWAIT
    HHVM_RC_INT_SAME(MSG_DONTWAIT);
#endif
#ifdef MSG_MORE
    HHVM_RC_INT_SAME(MSG_MORE);
#endif
#ifdef MSG_WAITFORONE
    HHVM_RC_INT_SAME(MSG_WAITFORONE);
#endif
#ifdef MSG_CMSG_CLOEXEC
    HHVM_RC_INT_SAME(MSG_CMSG_CLOEXEC);
#endif

    HHVM_RC_INT_SAME(SO_DEBUG);
    HHVM_RC_INT_SAME(SO_REUSEADDR);
#ifdef SO_REUSEPORT
    HHVM_RC_INT_SAME(SO_REUSEPORT);
#endif
    HHVM_RC_INT_SAME(SO_KEEPALIVE);
    HHVM_RC_INT_SAME(SO_DONTROUTE);
    HHVM_RC_INT_SAME(SO_LINGER);
    HHVM_RC_INT_SAME(SO_BROADCAST);
    HHVM_RC_INT_SAME(SO_OOBINLINE);
    HHVM_RC_INT_SAME(SO_SNDBUF);
    HHVM_RC_INT_SAME(SO_RCVBUF);
    HHVM_RC_INT_SAME(SO_SNDLOWAT);
    HHVM_RC_INT_SAME(SO_RCVLOWAT);
    HHVM_RC_INT_SAME(SO_SNDTIMEO);
    HHVM_RC_INT_SAME(SO_RCVTIMEO);
    HHVM_RC_INT_SAME(SO_TYPE);
#ifdef SO_FAMILY
    HHVM_RC_INT_SAME(SO_FAMILY);
#endif
    HHVM_RC_INT_SAME(SO_ERROR);
#ifdef SO_BINDTODEVICE
    HHVM_RC_INT_SAME(SO_BINDTODEVICE);
#endif
    HHVM_RC_INT_SAME(SOL_SOCKET);
    HHVM_RC_INT_SAME(SOMAXCONN);
#ifdef TCP_NODELAY
    HHVM_RC_INT_SAME(TCP_NODELAY);
#endif
    HHVM_RC_INT_SAME(PHP_NORMAL_READ);
    HHVM_RC_INT_SAME(PHP_BINARY_READ);

    /* TODO: MCAST_* constants and logic to handle them */

    HHVM_RC_INT_SAME(IP_MULTICAST_IF);
    HHVM_RC_INT_SAME(IP_MULTICAST_TTL);
    HHVM_RC_INT_SAME(IP_MULTICAST_LOOP);
    HHVM_RC_INT_SAME(IPV6_MULTICAST_IF);
    HHVM_RC_INT_SAME(IPV6_MULTICAST_HOPS);
    HHVM_RC_INT_SAME(IPV6_MULTICAST_LOOP);
    HHVM_RC_INT_SAME(IPPROTO_IP);
    HHVM_RC_INT_SAME(IPPROTO_IPV6);

    HHVM_RC_INT(SOL_TCP, IPPROTO_TCP);
    HHVM_RC_INT(SOL_UDP, IPPROTO_UDP);

    HHVM_RC_INT_SAME(IPV6_UNICAST_HOPS);

#define REGISTER_LONG_CONSTANT(name, val, flags) \
    Native::registerConstant<KindOfInt64>(makeStaticString(name), val)
#include "hphp/runtime/ext/sockets/unix_socket_constants.h"
#undef REGISTER_LONG_CONSTANT

    HHVM_FE(socket_create);
    HHVM_FE(socket_create_listen);
    HHVM_FE(socket_create_pair);
    HHVM_FE(socket_get_option);
    HHVM_FE(socket_getpeername);
    HHVM_FE(socket_getsockname);
    HHVM_FE(socket_set_block);
    HHVM_FE(socket_set_nonblock);
    HHVM_FE(socket_set_option);
    HHVM_FE(socket_connect);
    HHVM_FE(socket_bind);
    HHVM_FE(socket_listen);
    HHVM_FE(socket_select);
    HHVM_FE(socket_server);
    HHVM_FE(socket_accept);
    HHVM_FE(socket_read);
    HHVM_FE(socket_write);
    HHVM_FE(socket_send);
    HHVM_FE(socket_sendto);
    HHVM_FE(socket_recv);
    HHVM_FE(socket_recvfrom);
    HHVM_FE(socket_shutdown);
    HHVM_FE(socket_close);
    HHVM_FE(socket_strerror);
    HHVM_FE(socket_last_error);
    HHVM_FE(socket_clear_error);
    HHVM_FE(getaddrinfo);

    loadSystemlib();
  }


Variant strpbrk_char_list_has_nulls_slow(const String& haystack,
                                         const String& char_list) {

  auto const charListSz = char_list.size();
  auto const charListData = char_list.c_str();
  assertx(memchr(charListData, '\0', charListSz) != nullptr);

  // in order to use strcspn, remove all null byte(s) from char_list
  auto charListWithoutNull = (char*) req::malloc_noptrs(charListSz);
  SCOPE_EXIT { req::free(charListWithoutNull); };

  auto copy_ptr = charListWithoutNull;
  auto const charListStop = charListData + char_list.size();
  for (auto ptr = charListData; ptr != charListStop; ++ptr) {
    if (*ptr != '\0') { *copy_ptr++ = *ptr; }
  }
  assertx((copy_ptr - charListWithoutNull) < charListSz);
  // at least one of charListData chars was null, so there must be room:
  *copy_ptr = '\0';

  // Use strcspn instead of strpbrk because the latter doesn't report when
  // its terminated due to a null byte in haystack in any manageable way.
  auto haySize = haystack.size();
  auto hayData = haystack.c_str();

  size_t idx = strcspn(hayData, charListWithoutNull);
  if (idx < haySize) {
    // we know that char_list contains null bytes, being terminated because
    // haystack has null bytes is just dandy
    if (existence_only) { return true; }
    return String(hayData + idx, haySize - idx, CopyString);
  }
  return false;
}


Variant HHVM_FUNCTION(metaphone, const String& str, int /*phones*/ /* = 0 */) {
  return string_metaphone(str.data(), str.size(), 0, 1);
}


char operator[](int pos) const { return charAt(pos);}