static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo,
                                    size_t dir_offset, int section_index) {
  int i, sn, num_entries, sub_section_index = 0;
  unsigned char *dir_entry;
  char tagname[64];
  size_t ifd_size, dir_size, entry_offset, next_offset,
         entry_length, entry_value=0, fgot;
  int entry_tag , entry_type;
  tag_table_type tag_table = exif_get_tag_table(section_index);

  if (ImageInfo->ifd_nesting_level > MAX_IFD_NESTING_LEVEL) {
    return 0;
  }

  if (ImageInfo->FileSize >= dir_offset+2) {
    sn = exif_file_sections_add(ImageInfo, M_PSEUDO, 2, nullptr);
    if (sn == -1) return 0;
    /* we do not know the order of sections */
    ImageInfo->infile->seek(dir_offset, SEEK_SET);
    String snData = ImageInfo->infile->read(2);
    memcpy(ImageInfo->file.list[sn].data, snData.c_str(), 2);
    num_entries = php_ifd_get16u(ImageInfo->file.list[sn].data,
                                 ImageInfo->motorola_intel);
    dir_size = 2/*num dir entries*/ +
               12/*length of entry*/*num_entries +
               4/* offset to next ifd (points to thumbnail or NULL)*/;
    if (ImageInfo->FileSize >= dir_offset+dir_size) {
      if (exif_file_sections_realloc(ImageInfo, sn, dir_size)) {
        return 0;
      }
      snData = ImageInfo->infile->read(dir_size-2);
      memcpy(ImageInfo->file.list[sn].data+2, snData.c_str(), dir_size-2);
      next_offset =
        php_ifd_get32u(ImageInfo->file.list[sn].data + dir_size - 4,
                       ImageInfo->motorola_intel);
      /* now we have the directory we can look how long it should be */
      ifd_size = dir_size;
      char *end = (char*)ImageInfo->file.list[sn].data + dir_size;
      for(i=0;i<num_entries;i++) {
        dir_entry = ImageInfo->file.list[sn].data+2+i*12;
        CHECK_BUFFER_R(dir_entry+4, end, 4, 0);
        entry_tag = php_ifd_get16u(dir_entry+0, ImageInfo->motorola_intel);
        entry_type = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
        if (entry_type > NUM_FORMATS) {
          raise_notice("Read from TIFF: tag(0x%04X,%12s): "
                       "Illegal format code 0x%04X, switching to BYTE",
                       entry_tag,
                       exif_get_tagname(entry_tag, tagname, -12, tag_table),
                       entry_type);
          /* Since this is repeated in exif_process_IFD_TAG make it a
             notice here and make it a warning in the exif_process_IFD_TAG
             which is called elsewhere. */
          entry_type = TAG_FMT_BYTE;
        }
        entry_length =
          php_ifd_get32u(dir_entry+4, ImageInfo->motorola_intel) *
          get_php_tiff_bytes_per_format(entry_type);
        if (entry_length <= 4) {
          switch(entry_type) {
          case TAG_FMT_USHORT:
            CHECK_BUFFER_R(dir_entry+8, end, 2, 0);
            entry_value  = php_ifd_get16u(dir_entry+8,
                                          ImageInfo->motorola_intel);
            break;
          case TAG_FMT_SSHORT:
            CHECK_BUFFER_R(dir_entry+8, end, 2, 0);
            entry_value  = php_ifd_get16s(dir_entry+8,
                                          ImageInfo->motorola_intel);
            break;
          case TAG_FMT_ULONG:
            CHECK_BUFFER_R(dir_entry+8, end, 4, 0);
            entry_value  = php_ifd_get32u(dir_entry+8,
                                          ImageInfo->motorola_intel);
            break;
          case TAG_FMT_SLONG:
            CHECK_BUFFER_R(dir_entry+8, end, 4, 0);
            entry_value  = php_ifd_get32s(dir_entry+8,
                                          ImageInfo->motorola_intel);
            break;
          }
          switch(entry_tag) {
          case TAG_IMAGEWIDTH:
          case TAG_COMP_IMAGE_WIDTH:
            ImageInfo->Width  = entry_value;
            break;
          case TAG_IMAGEHEIGHT:
          case TAG_COMP_IMAGE_HEIGHT:
            ImageInfo->Height = entry_value;
            break;
          case TAG_PHOTOMETRIC_INTERPRETATION:
            switch (entry_value) {
            case PMI_BLACK_IS_ZERO:
            case PMI_WHITE_IS_ZERO:
            case PMI_TRANSPARENCY_MASK:
              ImageInfo->IsColor = 0;
              break;
            case PMI_RGB:
            case PMI_PALETTE_COLOR:
            case PMI_SEPARATED:
            case PMI_YCBCR:
            case PMI_CIELAB:
              ImageInfo->IsColor = 1;
              break;
            }
            break;
          }
        } else {
          CHECK_BUFFER_R(dir_entry+8, end, 4, 0);
          entry_offset =
            php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
          /* if entry needs expading ifd cache and entry is at end of
             current ifd cache. */
          /* otherwise there may be huge holes between two entries */
          if (entry_offset + entry_length > dir_offset + ifd_size &&
              entry_offset == dir_offset + ifd_size) {
            ifd_size = entry_offset + entry_length - dir_offset;
          }
        }
      }
      if (ImageInfo->FileSize >=
          dir_offset + ImageInfo->file.list[sn].size) {
        if (ifd_size > dir_size) {
          if (dir_offset + ifd_size > ImageInfo->FileSize) {
            raise_warning("Error in TIFF: filesize(x%04lX) less than "
                            "size of IFD(x%04lX + x%04lX)",
                            ImageInfo->FileSize, dir_offset, ifd_size);
            return 0;
          }
          if (exif_file_sections_realloc(ImageInfo, sn, ifd_size)) {
            return 0;
          } else {
            end = (char*)ImageInfo->file.list[sn].data + dir_size;
          }
          /* read values not stored in directory itself */
          snData = ImageInfo->infile->read(ifd_size-dir_size);
          memcpy(ImageInfo->file.list[sn].data+dir_size, snData.c_str(),
                 ifd_size-dir_size);
        }
        /* now process the tags */
        for(i=0;i<num_entries;i++) {
          dir_entry = ImageInfo->file.list[sn].data+2+i*12;
          CHECK_BUFFER_R(dir_entry+2, end, 2, 0);
          entry_tag = php_ifd_get16u(dir_entry+0, ImageInfo->motorola_intel);
          entry_type = php_ifd_get16u(dir_entry+2, ImageInfo->motorola_intel);
          if (entry_tag == TAG_EXIF_IFD_POINTER ||
              entry_tag == TAG_INTEROP_IFD_POINTER ||
              entry_tag == TAG_GPS_IFD_POINTER ||
              entry_tag == TAG_SUB_IFD) {
            switch(entry_tag) {
            case TAG_EXIF_IFD_POINTER:
              ImageInfo->sections_found |= FOUND_EXIF;
              sub_section_index = SECTION_EXIF;
              break;
            case TAG_GPS_IFD_POINTER:
              ImageInfo->sections_found |= FOUND_GPS;
              sub_section_index = SECTION_GPS;
              break;
            case TAG_INTEROP_IFD_POINTER:
              ImageInfo->sections_found |= FOUND_INTEROP;
              sub_section_index = SECTION_INTEROP;
              break;
            case TAG_SUB_IFD:
              ImageInfo->sections_found |= FOUND_THUMBNAIL;
              sub_section_index = SECTION_THUMBNAIL;
              break;
            }
            CHECK_BUFFER_R(dir_entry+8, end, 4, 0);
            entry_offset =
              php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel);
            ImageInfo->ifd_nesting_level++;
            exif_process_IFD_in_TIFF(ImageInfo, entry_offset,
                                     sub_section_index);
            if (section_index!=SECTION_THUMBNAIL && entry_tag==TAG_SUB_IFD) {
              if (ImageInfo->Thumbnail.filetype != IMAGE_FILETYPE_UNKNOWN &&
                  ImageInfo->Thumbnail.size &&
                  ImageInfo->Thumbnail.offset &&
                  ImageInfo->read_thumbnail) {
                if (!ImageInfo->Thumbnail.data) {
                  ImageInfo->Thumbnail.data =
                    (char *)IM_MALLOC(ImageInfo->Thumbnail.size);
                  CHECK_ALLOC_R(ImageInfo->Thumbnail.data,
                                ImageInfo->Thumbnail.size, 0);
                  ImageInfo->infile->seek(ImageInfo->Thumbnail.offset,
                                          SEEK_SET);
                  String str =
                    ImageInfo->infile->read(ImageInfo->Thumbnail.size);
                  fgot = str.length();
                  if (fgot < ImageInfo->Thumbnail.size) {
                    raise_warning("Thumbnail goes IFD boundary or "
                                    "end of file reached");
                    IM_FREE(ImageInfo->Thumbnail.data);
                    ImageInfo->Thumbnail.data = nullptr;
                  } else {
                    memcpy(ImageInfo->Thumbnail.data, str.c_str(), fgot);
                    exif_thumbnail_build(ImageInfo);
                  }
                }
              }
            }
          } else {
            if (!exif_process_IFD_TAG(ImageInfo, (char*)dir_entry,
                          (char*)(ImageInfo->file.list[sn].data-dir_offset),
                          (char*)(ImageInfo->file.list[sn].data + ifd_size),
                          ifd_size, 0, section_index, 0, tag_table)) {
              return 0;
            }
          }
        }
        /* If we had a thumbnail in a SUB_IFD we have ANOTHER image in
           NEXT IFD */
        if (next_offset && section_index != SECTION_THUMBNAIL) {
          /* this should be a thumbnail IFD */
          /* the thumbnail itself is stored at Tag=StripOffsets */
          ImageInfo->ifd_nesting_level++;
          exif_process_IFD_in_TIFF(ImageInfo, next_offset,
                                   SECTION_THUMBNAIL);
          if (!ImageInfo->Thumbnail.data && ImageInfo->Thumbnail.offset &&
              ImageInfo->Thumbnail.size && ImageInfo->read_thumbnail) {
            ImageInfo->Thumbnail.data =
              (char *)IM_MALLOC(ImageInfo->Thumbnail.size);
            CHECK_ALLOC_R(ImageInfo->Thumbnail.data,
                          ImageInfo->Thumbnail.size, 0);
            ImageInfo->infile->seek(ImageInfo->Thumbnail.offset, SEEK_SET);
            String str = ImageInfo->infile->read(ImageInfo->Thumbnail.size);
            fgot = str.length();
            if (fgot < ImageInfo->Thumbnail.size) {
              raise_warning("Thumbnail goes IFD boundary or "
                              "end of file reached");
              IM_FREE(ImageInfo->Thumbnail.data);
              ImageInfo->Thumbnail.data = nullptr;
            } else {
              memcpy(ImageInfo->Thumbnail.data, str.c_str(), fgot);
              exif_thumbnail_build(ImageInfo);
            }
          }
        }
        return 1;
      } else {
        raise_warning("Error in TIFF: filesize(x%04lX) less than "
                        "size of IFD(x%04lX)",
                        ImageInfo->FileSize,
                        dir_offset+ImageInfo->file.list[sn].size);
        return 0;
      }
    } else {
      raise_warning("Error in TIFF: filesize(x%04lX) less than size "
                      "of IFD dir(x%04lX)",
                      ImageInfo->FileSize, dir_offset+dir_size);
      return 0;
    }
  } else {
    raise_warning("Error in TIFF: filesize(x%04lX) less than "
                    "start of IFD dir(x%04lX)",
                    ImageInfo->FileSize, dir_offset+2);
    return 0;
  }
}


bool HHVM_FUNCTION(mcrypt_module_is_block_algorithm_mode, const String& mode,
                                  const String& lib_dir /* = null_string */) {
  String dir = lib_dir.empty() ? String(MCG(modes_dir)) : lib_dir;
  return mcrypt_module_is_block_algorithm_mode((char*)mode.data(),
                                               (char*)dir.data()) == 1;
}

security fixes

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


Variant HHVM_FUNCTION(imagescale, const Resource& image, int64_t newwidth,
  int64_t newheight /* =-1 */, int64_t method /*=GD_BILINEAR_FIXED*/) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImagePtr imscaled = nullptr;
  gdInterpolationMethod old_method;
  if (method == -1) method = GD_BILINEAR_FIXED;

  if (newheight < 0) {
    /* preserve ratio */
    long src_x, src_y;
    src_x = gdImageSX(im);
    src_y = gdImageSY(im);
    if (src_x) {
      newheight = newwidth * src_y / src_x;
    }
  }
  if (newheight <= 0 || newheight > INT_MAX || newwidth <= 0 || newwidth > INT_MAX) {
    return false;
  }

  old_method = im->interpolation_id;
  if (gdImageSetInterpolationMethod(im, (gdInterpolationMethod) method)) {
    imscaled = gdImageScale(im, newwidth, newheight);
  }
  gdImageSetInterpolationMethod(im, old_method);

  if (imscaled == nullptr) {
    return false;
  }
  return Variant(req::make<Image>(imscaled));
}


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


Variant HHVM_FUNCTION(imagepalettecopy,
    const Resource& dst,
    const Resource& src) {
  gdImagePtr dstim = cast<Image>(dst)->get();
  gdImagePtr srcim = cast<Image>(src)->get();
  if (!dstim || !srcim)
    return false;
  gdImagePaletteCopy(dstim, srcim);
  return true;
}


bool HHVM_FUNCTION(boolval, const Variant& v) {
  return v.toBoolean();
}


ALWAYS_INLINE String serialize_impl(const Variant& value,
                                    const SerializeOptions& opts) {
  switch (value.getType()) {
    case KindOfClass:
    case KindOfLazyClass:
    case KindOfPersistentString:
    case KindOfString: {
      auto const str =
        isStringType(value.getType()) ? value.getStringData() :
        isClassType(value.getType()) ? classToStringHelper(value.toClassVal()) :
        lazyClassToStringHelper(value.toLazyClassVal());
      auto const size = str->size();
      if (size >= RuntimeOption::MaxSerializedStringSize) {
        throw Exception("Size of serialized string (%ld) exceeds max", size);
      }
      StringBuffer sb;
      sb.append("s:");
      sb.append(size);
      sb.append(":\"");
      sb.append(str->data(), size);
      sb.append("\";");
      return sb.detach();
    }
    case KindOfResource:
      return s_Res;

    case KindOfUninit:
    case KindOfNull:
    case KindOfBoolean:
    case KindOfInt64:
    case KindOfFunc:
    case KindOfPersistentVec:
    case KindOfVec:
    case KindOfPersistentDict:
    case KindOfDict:
    case KindOfPersistentKeyset:
    case KindOfKeyset:
    case KindOfPersistentDArray:
    case KindOfDArray:
    case KindOfPersistentVArray:
    case KindOfVArray:
    case KindOfDouble:
    case KindOfObject:
    case KindOfClsMeth:
    case KindOfRClsMeth:
    case KindOfRFunc:
    case KindOfRecord:
      break;
  }
  VariableSerializer vs(VariableSerializer::Type::Serialize);
  if (opts.keepDVArrays)        vs.keepDVArrays();
  if (opts.forcePHPArrays)      vs.setForcePHPArrays();
  if (opts.warnOnHackArrays)    vs.setHackWarn();
  if (opts.warnOnPHPArrays)     vs.setPHPWarn();
  if (opts.ignoreLateInit)      vs.setIgnoreLateInit();
  if (opts.serializeProvenanceAndLegacy) vs.setSerializeProvenanceAndLegacy();
  // Keep the count so recursive calls to serialize() embed references properly.
  return vs.serialize(value, true, true);
}


    Accessor()
      : m_kind(Kind::Empty)
    {}


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


/* implicit */ String(String&& str) noexcept : m_str(std::move(str.m_str)) {}


  MachineInfo() {
    m_cpu_num = sysconf(_SC_NPROCESSORS_CONF);
    m_cpu_frequencies = get_cpu_frequency_from_file("/proc/cpuinfo", m_cpu_num);

    if (m_cpu_frequencies)
      return;

    m_cpu_frequencies = new int64_t[m_cpu_num];
    for (int i = 0; i < m_cpu_num; i++) {
      cpu_set_t prev_mask;
      GET_AFFINITY(0, sizeof(cpu_set_t), &prev_mask);
      BindToCPU(i);
      // Make sure the current process gets scheduled to the target cpu. This
      // might not be necessary though.
      usleep(0);
      m_cpu_frequencies[i] = get_cpu_frequency();
      SET_AFFINITY(0, sizeof(cpu_set_t), &prev_mask);
    }
  }


String HHVM_FUNCTION(crypt,
                     const String& str,
                     const String& salt /* = "" */) {
  return StringUtil::Crypt(str, salt.c_str());
}


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


  String& operator=(StringData *data) {
    m_str = data;
    return *this;
  }


bool HHVM_FUNCTION(socket_set_option,
                   const Resource& socket,
                   int level,
                   int optname,
                   const Variant& optval) {
  auto sock = cast<Socket>(socket);

  struct linger lv;
  struct timeval tv;
  int ov;
  int optlen;
  void *opt_ptr;

  switch (optname) {
  case SO_LINGER:
    {
      Array value = optval.toArray();
      if (!value.exists(s_l_onoff)) {
        raise_warning("no key \"l_onoff\" passed in optval");
        return false;
      }
      if (!value.exists(s_l_linger)) {
        raise_warning("no key \"l_linger\" passed in optval");
        return false;
      }

      lv.l_onoff = (unsigned short)value[s_l_onoff].toInt32();
      lv.l_linger = (unsigned short)value[s_l_linger].toInt32();
      optlen = sizeof(lv);
      opt_ptr = &lv;
    }
    break;

  case SO_RCVTIMEO:
  case SO_SNDTIMEO:
    {
      Array value = optval.toArray();
      if (!value.exists(s_sec)) {
        raise_warning("no key \"sec\" passed in optval");
        return false;
      }
      if (!value.exists(s_usec)) {
        raise_warning("no key \"usec\" passed in optval");
        return false;
      }

      tv.tv_sec = value[s_sec].toInt32();
      tv.tv_usec = value[s_usec].toInt32();
      if (tv.tv_usec >= 1000000) {
        tv.tv_sec += tv.tv_usec / 1000000;
        tv.tv_usec %= 1000000;
      }
      if (tv.tv_sec < 0) {
        tv.tv_sec = RequestInfo::s_requestInfo.getNoCheck()->
        m_reqInjectionData.getSocketDefaultTimeout();
      }
      optlen = sizeof(tv);
      opt_ptr = &tv;
      sock->internalSetTimeout(tv);
    }
    break;

  default:
    ov = optval.toInt32();
    optlen = sizeof(ov);
    opt_ptr = &ov;
    break;
  }

  if (setsockopt(sock->fd(), level, optname, opt_ptr, optlen) != 0) {
    SOCKET_ERROR(sock, "unable to set socket option", errno);
    return false;
  }
  return true;
}


Array HHVM_FUNCTION(localeconv) {
  struct lconv currlocdata;
  {
    Lock lock(s_mutex);
    struct lconv *res = localeconv();
    currlocdata = *res;
  }

  Array ret = Array::CreateDArray();
#define SET_LOCALE_STRING(x) ret.set(s_ ## x, String(currlocdata.x, CopyString))
  SET_LOCALE_STRING(decimal_point);
  SET_LOCALE_STRING(thousands_sep);
  SET_LOCALE_STRING(int_curr_symbol);
  SET_LOCALE_STRING(currency_symbol);
  SET_LOCALE_STRING(mon_decimal_point);
  SET_LOCALE_STRING(mon_thousands_sep);
  SET_LOCALE_STRING(positive_sign);
  SET_LOCALE_STRING(negative_sign);
#define SET_LOCALE_INTEGER(x) ret.set(s_ ## x, currlocdata.x)
  SET_LOCALE_INTEGER(int_frac_digits);
  SET_LOCALE_INTEGER(frac_digits);
  SET_LOCALE_INTEGER(p_cs_precedes);
  SET_LOCALE_INTEGER(p_sep_by_space);
  SET_LOCALE_INTEGER(n_cs_precedes);
  SET_LOCALE_INTEGER(n_sep_by_space);
  SET_LOCALE_INTEGER(p_sign_posn);
  SET_LOCALE_INTEGER(n_sign_posn);

  Array grouping, mon_grouping;

  /* Grab the grouping data out of the array */
  int len = strlen(currlocdata.grouping);
  for (int i = 0; i < len; i++) {
    grouping.set(i, currlocdata.grouping[i]);
  }
  ret.set(s_grouping, grouping);

  /* Grab the monetary grouping data out of the array */
  len = strlen(currlocdata.mon_grouping);
  for (int i = 0; i < len; i++) {
    mon_grouping.set(i, currlocdata.mon_grouping[i]);
  }
  ret.set(s_mon_grouping, mon_grouping);

  return ret;
}


  void beginFrameEx(const char* /*symbol*/) override {
    HierarchicalProfilerFrame *frame =
      dynamic_cast<HierarchicalProfilerFrame *>(m_stack);
    frame->m_tsc_start = cpuCycles();

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

    if (m_flags & TrackMemory) {
      auto const stats = tl_heap->getStats();
      frame->m_mu_start  = stats.usage();
      frame->m_pmu_start = stats.peakUsage;
    } else if (m_flags & TrackMalloc) {
      frame->m_mu_start = get_allocs();
      frame->m_pmu_start = get_frees();
    }
  }


ExifExtension() : Extension("exif", NO_EXTENSION_VERSION_YET) {}


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

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


Variant HHVM_FUNCTION(iptcembed, const String& iptcdata,
    const String& jpeg_file_name, int64_t spool /* = 0 */) {
  char psheader[] = "\xFF\xED\0\0Photoshop 3.0\08BIM\x04\x04\0\0\0";
  static_assert(sizeof(psheader) == 28, "psheader must be 28 bytes");
  unsigned int iptcdata_len = iptcdata.length();
  unsigned int marker, inx;
  unsigned char *spoolbuf = nullptr, *poi = nullptr;
  bool done = false;
  bool written = false;

  auto file = File::Open(jpeg_file_name, "rb");
  if (!file) {
    raise_warning("failed to open file: %s", jpeg_file_name.c_str());
    return false;
  }

  if (spool < 2) {
    auto stat = HHVM_FN(fstat)(Resource(file));
    // TODO(t7561579) until we can properly handle non-file streams here, don't
    // pretend we can and crash.
    if (!stat.isArray()) {
      raise_warning("unable to stat input");
      return false;
    }

    auto& stat_arr = stat.asCArrRef();
    auto st_size = stat_arr[s_size].toInt64();
    if (st_size < 0) {
      raise_warning("unsupported stream type");
      return false;
    }

    if (iptcdata_len >= (INT64_MAX - sizeof(psheader) - st_size - 1024 - 1)) {
      raise_warning("iptcdata too long");
      return false;
    }

    auto malloc_size = iptcdata_len + sizeof(psheader) + st_size + 1024 + 1;
    poi = spoolbuf = (unsigned char *)IM_MALLOC(malloc_size);
    CHECK_ALLOC_R(poi, malloc_size, false);
    memset(poi, 0, malloc_size);
  }
  if (php_iptc_get1(file, spool, poi?&poi:0) != 0xFF) {
    file->close();
    if (spoolbuf) {
      IM_FREE(spoolbuf);
    }
    return false;
  }

  if (php_iptc_get1(file, spool, poi?&poi:0) != 0xD8) {
    file->close();
    if (spoolbuf) {
      IM_FREE(spoolbuf);
    }
    return false;
  }

  while (!done) {
    marker = php_iptc_next_marker(file, spool, poi?&poi:0);
    if (marker == M_EOI) { /* EOF */
      break;
    } else if (marker != M_APP13) {
      php_iptc_put1(file, spool, (unsigned char)marker, poi?&poi:0);
    }
    switch (marker) {
    case M_APP13:
      /* we are going to write a new APP13 marker, so don't
         output the old one */
      php_iptc_skip_variable(file, 0, 0);
      php_iptc_read_remaining(file, spool, poi?&poi:0);
      done = true;
      break;

    case M_APP0:
      /* APP0 is in each and every JPEG, so when we hit APP0
         we insert our new APP13! */
    case M_APP1:
      if (written) {
        /* don't try to write the data twice */
        break;
      }
      written = true;

      php_iptc_skip_variable(file, spool, poi?&poi:0);

      if (iptcdata_len & 1) {
        iptcdata_len++; /* make the length even */
      }

      psheader[2] = (iptcdata_len + sizeof(psheader)) >> 8;
      psheader[3] = (iptcdata_len + sizeof(psheader)) & 0xff;

      for (inx = 0; inx < sizeof(psheader); inx++) {
        php_iptc_put1(file, spool, psheader[inx], poi ? &poi : 0);
      }

      php_iptc_put1(file, spool, (unsigned char)(iptcdata_len>>8),
                    poi?&poi:0);
      php_iptc_put1(file, spool, (unsigned char)(iptcdata_len&0xff),
                    poi?&poi:0);

      for (inx = 0; inx < iptcdata_len; inx++) {
        php_iptc_put1(file, spool, iptcdata.c_str()[inx], poi?&poi:0);
      }
      break;

    case M_SOS:
      /* we hit data, no more marker-inserting can be done! */
      php_iptc_read_remaining(file, spool, poi?&poi:0);
      done = true;
      break;

    default:
      php_iptc_skip_variable(file, spool, poi?&poi:0);
      break;
    }
  }

  file->close();

  if (spool < 2) {
    return String((char *)spoolbuf, poi - spoolbuf, AttachString);
  }
  return true;
}


explicit StrNR(StringData *sd) : m_px(sd) {}


  WuManberReplacement(const Array &arr, size_t minLen)
  : m(minLen), B(MIN(m,2)), Bp(MIN(m,2)),
    valid(initPatterns(arr)) { }


    void clear() {
      wall_time = cpu = memory = peak_memory = 0;
    }


  String& operator=(const String& v) {
    m_str = v.m_str;
    return *this;
  }


static int64_t get_cpu_frequency() {
  struct timeval start;
  struct timeval end;

  if (gettimeofday(&start, 0)) {
    perror("gettimeofday");
    return 0.0;
  }
  uint64_t tsc_start = cpuCycles();
  uint64_t tsc_end;
  volatile int i;
  // Busy loop for 5 miliseconds. Don't use usleep() here since it causes the
  // CPU to halt which will generate meaningless results.
  do {
    for (i = 0; i < 1000000; i++);
    if (gettimeofday(&end, 0)) {
      perror("gettimeofday");
      return 0.0;
    }
    tsc_end = cpuCycles();
  } while (get_us_interval(&start, &end) < 5000);

  return nearbyint((tsc_end - tsc_start) * 1.0
                                   / (get_us_interval(&start, &end)));
}


ALWAYS_INLINE Variant preg_return_no_error(Variant&& return_value) {
  *rl_last_error_code = PHP_PCRE_NO_ERROR;
  return std::move(return_value);
}


static bool php_string_to_if_index(const char *val, unsigned *out)
{
#if HAVE_IF_NAMETOINDEX
  unsigned int ind = if_nametoindex(val);
  if (ind == 0) {
    raise_warning("no interface with name \"%s\" could be found", val);
    return false;
  } else {
    *out = ind;
    return true;
  }
#else
  raise_warning("this platform does not support looking up an interface by "
                "name, an integer interface index must be supplied instead");
  return false;
#endif
}


ALWAYS_INLINE TypedValue empty_string_tv() {
  return make_tv<KindOfPersistentString>(staticEmptyString());
}


bool HHVM_FUNCTION(HH_is_varray, const Variant& val) {
  return is_varray(val.asTypedValue());
}


Variant HHVM_FUNCTION(mcrypt_create_iv, int size, int source /* = 0 */) {
  if (size <= 0 || size >= INT_MAX) {
    raise_warning("Can not create an IV with a size of less than 1 or "
                    "greater than %d", INT_MAX);
    return false;
  }

  int n = 0;
  char *iv = (char*)calloc(size + 1, 1);
  if (source == RANDOM || source == URANDOM) {
    int fd = open(source == RANDOM ? "/dev/random" : "/dev/urandom", O_RDONLY);
    if (fd < 0) {
      free(iv);
      raise_warning("Cannot open source device");
      return false;
    }
    int read_bytes;
    for (read_bytes = 0; read_bytes < size && n >= 0; read_bytes += n) {
      n = read(fd, iv + read_bytes, size - read_bytes);
    }
    n = read_bytes;
    close(fd);
    if (n < size) {
      free(iv);
      raise_warning("Could not gather sufficient random data");
      return false;
    }
  } else {
    n = size;
    while (size) {
      // Use userspace rand() function because it handles auto-seeding
      iv[--size] = (char)HHVM_FN(rand)(0, 255);
    }
  }
  return String(iv, n, AttachString);
}


Variant str_replace(const Variant& search, const Variant& replace,
                    const String& subject, int64_t& count, bool caseSensitive) {
  count = 0;
  if (search.isArray()) {
    String ret = subject;
    int c = 0;

    Array searchArr = search.toArray();
    if (replace.isArray()) {
      Array replArr = replace.toArray();
      ArrayIter replIter(replArr);
      for (ArrayIter iter(searchArr); iter; ++iter) {
        if (replIter) {
          ret = string_replace(ret, iter.second().toString(),
                               replIter.second().toString(),
                               c, caseSensitive);
          ++replIter;
        } else {
          ret = string_replace(ret, iter.second().toString(),
                               "", c, caseSensitive);
        }
        count +=c;
      }
      return ret;
    }

    String repl = replace.toString();
    for (ArrayIter iter(searchArr); iter; ++iter) {
      ret = string_replace(ret, iter.second().toString(), repl, c,
                           caseSensitive);
      count += c;
    }
    return ret;
  }

  int icount;
  auto ret = string_replace(subject, search.toString(), replace.toString(),
                            icount, caseSensitive);
  count = icount;
  return ret;
}


static void exif_iif_add_value(image_info_type *image_info, int section_index,
                               char *name, int tag, int format, int length,
                               void* value, int motorola_intel) {
  size_t idex;
  void *vptr;
  image_info_value *info_value;
  image_info_data  *info_data;
  image_info_data  *list;

  if (length < 0) {
    return;
  }

  size_t realloc_size = (image_info->info_list[section_index].count+1) *
                        sizeof(image_info_data);
  list = (image_info_data*)
    IM_REALLOC(image_info->info_list[section_index].list, realloc_size);
  CHECK_ALLOC(list, realloc_size);
  image_info->info_list[section_index].list = list;

  info_data  = &image_info->info_list[section_index].
                list[image_info->info_list[section_index].count];
  memset(info_data, 0, sizeof(image_info_data));
  info_data->tag = tag;
  info_data->format = format;
  info_data->length = length;
  PHP_STRDUP(info_data->name, name);
  info_value = &info_data->value;

  switch (format) {
  case TAG_FMT_STRING:
    if (value) {
      length = php_strnlen((char*)value, length);
      // TODO
      // if (PG(magic_quotes_runtime)) {
      //   info_value->s = php_addslashes(value, length, &length, 0);
      // } else {
      PHP_STRNDUP(info_value->s, (const char *)value, length);
      // }
      info_data->length = (info_value->s ? length : 0);
    } else {
      info_data->length = 0;
      PHP_STRDUP(info_value->s, "");
    }
    break;

  default:
    /* Standard says more types possible but skip them...
     * but allow users to handle data if they know how to
     * So not return but use type UNDEFINED
     * return;
     */
    info_data->tag = TAG_FMT_UNDEFINED;/* otherwise not freed from memory */
  case TAG_FMT_SBYTE:
  case TAG_FMT_BYTE:
    /* in contrast to strings bytes do not need to allocate buffer for
       nullptr if length==0 */
    if (!length)
      break;
  case TAG_FMT_UNDEFINED:
    if (value) {
      /* do not recompute length here */
      // TODO
      // if (PG(magic_quotes_runtime)) {
      //   info_value->s = php_addslashes(value, length, &length, 0);
      // } else {
      PHP_STRNDUP(info_value->s, (const char *)value, length);
      // }
      info_data->length = (info_value->s ? length : 0);
    } else {
      info_data->length = 0;
      PHP_STRDUP(info_value->s, "");
    }
    break;

  case TAG_FMT_USHORT:
  case TAG_FMT_ULONG:
  case TAG_FMT_URATIONAL:
  case TAG_FMT_SSHORT:
  case TAG_FMT_SLONG:
  case TAG_FMT_SRATIONAL:
  case TAG_FMT_SINGLE:
  case TAG_FMT_DOUBLE:
    if (length==0) {
      break;
    } else if (length>1) {
      info_value->list =
        (image_info_value*)IM_CALLOC(length, sizeof(image_info_value));
      CHECK_ALLOC(info_value->list, sizeof(image_info_value));
    } else {
      info_value = &info_data->value;
    }
    for (idex=0,vptr=value; idex<(size_t)length;
         idex++,vptr=(char *) vptr + get_php_tiff_bytes_per_format(format)) {
      if (length>1) {
        info_value = &info_data->value.list[idex];
      }
      switch (format) {
      case TAG_FMT_USHORT:
        info_value->u = php_ifd_get16u(vptr, motorola_intel);
        break;

      case TAG_FMT_ULONG:
        info_value->u = php_ifd_get32u(vptr, motorola_intel);
        break;

      case TAG_FMT_URATIONAL:
        info_value->ur.num = php_ifd_get32u(vptr, motorola_intel);
        info_value->ur.den = php_ifd_get32u(4+(char *)vptr, motorola_intel);
        break;

      case TAG_FMT_SSHORT:
        info_value->i = php_ifd_get16s(vptr, motorola_intel);
        break;

      case TAG_FMT_SLONG:
        info_value->i = php_ifd_get32s(vptr, motorola_intel);
        break;

      case TAG_FMT_SRATIONAL:
        info_value->sr.num = php_ifd_get32u(vptr, motorola_intel);
        info_value->sr.den = php_ifd_get32u(4+(char *)vptr, motorola_intel);
        break;

      case TAG_FMT_SINGLE:
        info_value->f = *(float *)value;

      case TAG_FMT_DOUBLE:
        info_value->d = *(double *)value;
        break;
      }
    }
  }
  image_info->sections_found |= 1<<section_index;
  image_info->info_list[section_index].count++;
}


TypedValue HHVM_FUNCTION(strspn,
                         const String& str1,
                         const String& str2,
                         int start /* = 0 */,
                         int length /* = 0x7FFFFFFF */) {
  const char *s1 = str1.data();
  const char *s2 = str2.data();
  int s1_len = str1.size();
  int s2_len = str2.size();

  if (!string_strspn_check(s1_len, start, length)) {
    return make_tv<KindOfBoolean>(false);
  }

  s1 += start;
  for (int pos = 0; pos < length; ++pos) {
    if (memchr(s2, *(s1++), s2_len) == nullptr) {
      return make_tv<KindOfInt64>(pos);
    }
  }

  return make_tv<KindOfInt64>(length);
}


Variant HHVM_FUNCTION(mcrypt_encrypt, const String& cipher, const String& key,
                                      const String& data, const String& mode,
                                      const Variant& viv /* = null_string */) {
  String iv = viv.toString();
  return php_mcrypt_do_crypt(cipher, key, data, mode, iv, false,
                             "mcrypt_encrypt");
}


inline bool StringData::same(const StringData* s) const {
  assertx(s);
  if (m_len != s->m_len) return false;
  // The underlying buffer and its length are 8-byte aligned, ensured by
  // StringData layout, req::malloc, or malloc. So compare words.
  assertx(uintptr_t(data()) % 8 == 0);
  assertx(uintptr_t(s->data()) % 8 == 0);
  return wordsame(data(), s->data(), m_len);
}


Variant HHVM_FUNCTION(imagettfbbox, double size, double angle,
                     const String& fontfile, const String& text) {
  return php_imagettftext_common(TTFTEXT_BBOX, 0,
                                 size, angle, fontfile, text);
}


TypedValue HHVM_FUNCTION(strstr,
                         const String& haystack,
                         const Variant& needle,
                         bool before_needle /* = false */) {
  auto const tv = HHVM_FN(strpos)(haystack, needle);
  auto const& ret = tvAsCVarRef(&tv);
  assertx(!isRefcountedType(tv.m_type));

  if (same(ret, false)) {
    return make_tv<KindOfBoolean>(false);
  }
  if (before_needle) {
    return tvReturn(haystack.substr(0, ret.toInt32()));
  } else {
    return tvReturn(haystack.substr(ret.toInt32()));
  }
}


static int php_read(req::ptr<Socket> sock, void *buf, int64_t maxlen,
                    int flags) {
  int m = fcntl(sock->fd(), F_GETFL);
  if (m < 0) {
    return m;
  }
  int nonblock = (m & O_NONBLOCK);
  m = 0;

  char *t = (char *)buf;
  *t = '\0';
  int n = 0;
  int no_read = 0;
  while (*t != '\n' && *t != '\r' && n < maxlen) {
    if (m > 0) {
      t++;
      n++;
    } else if (m == 0) {
      no_read++;
      if (nonblock && no_read >= 2) {
        return n;
        /* The first pass, m always is 0, so no_read becomes 1
         * in the first pass. no_read becomes 2 in the second pass,
         * and if this is nonblocking, we should return.. */
      }

      if (no_read > 200) {
        errno = ECONNRESET;
        return -1;
      }
    }

    if (n < maxlen) {
      m = recv(sock->fd(), (void *)t, 1, flags);
    }

    if (errno != 0 && errno != ESPIPE && errno != EAGAIN) {
      return -1;
    }
    errno = 0;
  }

  if (n < maxlen) {
    n++;
    /* The only reasons it makes it to here is
     * if '\n' or '\r' are encountered. So, increase
     * the return by 1 to make up for the lack of the
     * '\n' or '\r' in the count (since read() takes
     * place at the end of the loop..) */
  }

  return n;
}


  ~FreeHelperImpl() {
    useSmartFree ? req::free(p) : free(p);
  }


bool HHVM_FUNCTION(imagecolordeallocate, const Resource& image,
                                         int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  /* We can return right away for a truecolor image as deallocating colours
     is meaningless here */
  if (gdImageTrueColor(im)) return true;

  if (color >= 0 && color < gdImageColorsTotal(im)) {
    gdImageColorDeallocate(im, color);
    return true;
  } else {
    raise_warning("Color index %" PRId64 " out of range", color);
    return false;
  }
}


int php_iptc_skip_variable(const req::ptr<File>& file,
                           int spool,
                           unsigned char **spoolbuf) {
  unsigned int length;
  int c1, c2;

  if ((c1 = php_iptc_get1(file, spool, spoolbuf)) == EOF) return M_EOI;

  if ((c2 = php_iptc_get1(file, spool, spoolbuf)) == EOF) return M_EOI;

  length = (((unsigned char) c1) << 8) + ((unsigned char) c2);

  length -= 2;

  while (length--) {
    if (php_iptc_get1(file, spool, spoolbuf) == EOF) return M_EOI;
  }
  return 0;
}


  void walkTrace(TraceIterator begin, TraceIterator end, TraceIterator final,
                 Stats& stats) {
    TraceWalker<TraceIterator, Stats> walker;
    walker.walk(begin, end, final, stats);
  }


int64_t HHVM_FUNCTION(intval, const Variant& v, int64_t base /* = 10 */) {
  return v.toInt64(base);
}


ALWAYS_INLINE void decRefStr(StringData* s) {
  s->decRefAndRelease();
}


String encode_as_utf8(int code_point) {
  auto res = folly::codePointToUtf8(code_point);
  return String(res);
}


pcre_cache_entry::~pcre_cache_entry() {
  if (extra) {
#if PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20)
    free(extra);
#else
    pcre_free_study(extra);
#endif
  }
  free(subpat_names);
  pcre_free(re);
}


static void sock_array_from_fd_set(Variant &sockets,
                                   const std::vector<pollfd>& fds,
                                   int &nfds, int &count, const short flag) {
  Array ret = Array::CreateDArray();
  assertx(sockets.isArray());
  const auto& sock_array = sockets.asCArrRef();
  IterateKVNoInc(
    sock_array.get(),
    [&](TypedValue k, TypedValue v) {
      const pollfd &fd = fds.at(nfds++);
      assertx(v.m_type == KindOfResource);
      assertx(fd.fd == static_cast<File*>(v.m_data.pres->data())->fd());
      if (fd.revents & flag) {
        ret.set(k, v);
        count++;
      }
    }
  );
  sockets = ret;
}


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


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


explicit MCrypt(MCRYPT td) : m_td(td), m_init(false) {}


String HHVM_FUNCTION(hebrevc,
                     const String& hebrew_text,
                     int max_chars_per_line /* = 0 */) {
  if (hebrew_text.empty()) return hebrew_text;
  return string_convert_hebrew_string(hebrew_text, max_chars_per_line, true);
}


  void beginFrame(const char *symbol) override {
    VMRegAnchor _;
    ActRec *ar = vmfp();
    Frame f(symbol);
    if (ar->func()->cls() && ar->hasThis()) {
      auto& memo = m_memos[symbol];
      if (!memo.m_ignore) {
        ARRPROV_USE_RUNTIME_LOCATION();
        auto args = hhvm_get_frame_args(ar);
        args.append((int64_t)(ar->getThis())); // Use the pointer not the obj
        VariableSerializer vs(VariableSerializer::Type::DebuggerSerialize);
        String sdata;
        try {
          sdata = vs.serialize(VarNR{args}, true);
          f.m_args = sdata;
        } catch (...) {
          fprintf(stderr, "Args Serialization failure: %s\n", symbol);
        }
      }
    }
    m_stack.push_back(f);
  }


bool HHVM_FUNCTION(imagegd, const Resource& image,
    const String& filename /* = null_string */) {
  return _php_image_output(image, filename, -1, -1, PHP_GDIMG_TYPE_GD, "GD",
                           (void (*)())gdImageGd);
}


int    toInt32  () const { return m_str ? m_str->toInt32  () : 0;}


static int compare_left(char const **a, char const *aend,
                        char const **b, char const *bend) {
  /* Compare two left-aligned numbers: the first to have a
     different value wins. */
  for(;; (*a)++, (*b)++) {
    if ((*a == aend || !isdigit((int)(unsigned char)**a)) &&
        (*b == bend || !isdigit((int)(unsigned char)**b)))
      return 0;
    else if (*a == aend || !isdigit((int)(unsigned char)**a))
      return -1;
    else if (*b == bend || !isdigit((int)(unsigned char)**b))
      return +1;
    else if (**a < **b)
      return -1;
    else if (**a > **b)
      return +1;
  }

  return 0;
}


  const String& setSize(int64_t len) {
    assertx(m_str);
    m_str->setSize(len);
    return *this;
  }


bool HHVM_FUNCTION(imagestringup, const Resource& image,
    int64_t font, int64_t x, int64_t y,
    const String& str, int64_t color) {
  return php_imagechar(image, font, x, y, str, color, 3);
}


static size_t php_strnlen(char* str, size_t maxlen) {
  size_t len = 0;

  if (str && maxlen && *str) {
    do {
      len++;
    } while (--maxlen && *(++str));
  }
  return len;
}


  void sample_check() {
    if (m_stack) {
      // While loop is to handle a single function taking a long time
      // and passing several sampling intervals
      while ((cpuCycles() - m_last_sample_tsc) > m_sampling_interval_tsc) {
        m_last_sample_tsc += m_sampling_interval_tsc;
        // HAS TO BE UPDATED BEFORE calling sample_stack
        incr_us_interval(&m_last_sample_time, SAMPLING_INTERVAL);
        sample_stack();
      }
    }
  }


static bool _php_image_convert(const String& f_org, const String& f_dest,
                               int dest_height, int dest_width,
                               int threshold, int image_type) {
  gdImagePtr im_org, im_dest, im_tmp;
  req::ptr<File> org_file, dest_file;
  FILE *org, *dest;
  int org_height, org_width;
  int white, black;
  int color, color_org, median;
  int x, y;
  float x_ratio, y_ratio;
#ifdef HAVE_GD_JPG
  // long ignore_warning;
#endif

  /* Check threshold value */
  if (threshold < 0 || threshold > 8) {
    raise_warning("Invalid threshold value '%d'", threshold);
    return false;
  }

  /* Open origin file */
  org_file = php_open_plain_file(f_org, "rb", &org);
  if (!org_file) {
    return false;
  }

  /* Open destination file */
  dest_file = php_open_plain_file(f_dest, "wb", &dest);
  if (!dest_file) {
    return false;
  }

  switch (image_type) {
  case PHP_GDIMG_TYPE_GIF:
    im_org = gdImageCreateFromGif(org);
    if (im_org == nullptr) {
      raise_warning("Unable to open '%s' Not a valid GIF file",
                      f_org.c_str());
      return false;
    }
    break;

#ifdef HAVE_GD_JPG
  case PHP_GDIMG_TYPE_JPG:
    im_org = gdImageCreateFromJpeg(org);
    if (im_org == nullptr) {
      raise_warning("Unable to open '%s' Not a valid JPEG file",
                      f_org.c_str());
      return false;
    }
    break;
#endif /* HAVE_GD_JPG */

#ifdef HAVE_GD_PNG
  case PHP_GDIMG_TYPE_PNG:
    im_org = gdImageCreateFromPng(org);
    if (im_org == nullptr) {
      raise_warning("Unable to open '%s' Not a valid PNG file",
                      f_org.c_str());
      return false;
    }
    break;
#endif /* HAVE_GD_PNG */

#ifdef HAVE_LIBVPX
  case PHP_GDIMG_TYPE_WEBP:
    im_org = gdImageCreateFromWebp(org);
    if (im_org == nullptr) {
      raise_warning("Unable to open '%s' Not a valid webp file",
                      f_org.c_str());
      return false;
    }
    break;
#endif /* HAVE_LIBVPX */

  default:
    raise_warning("Format not supported");
    return false;
  }

  org_width  = gdImageSX (im_org);
  org_height = gdImageSY (im_org);

  x_ratio = (float) org_width / (float) dest_width;
  y_ratio = (float) org_height / (float) dest_height;

  if (x_ratio > 1 && y_ratio > 1) {
    if (y_ratio > x_ratio) {
      x_ratio = y_ratio;
    } else {
      y_ratio = x_ratio;
    }
    dest_width = (int) (org_width / x_ratio);
    dest_height = (int) (org_height / y_ratio);
  } else {
    x_ratio = (float) dest_width / (float) org_width;
    y_ratio = (float) dest_height / (float) org_height;

    if (y_ratio < x_ratio) {
      x_ratio = y_ratio;
    } else {
      y_ratio = x_ratio;
    }
    dest_width = (int) (org_width * x_ratio);
    dest_height = (int) (org_height * y_ratio);
  }

  im_tmp = gdImageCreate (dest_width, dest_height);
  if (im_tmp == nullptr) {
    raise_warning("Unable to allocate temporary buffer");
    return false;
  }

  gdImageCopyResized (im_tmp, im_org, 0, 0, 0, 0,
                      dest_width, dest_height, org_width, org_height);

  gdImageDestroy(im_org);

  org_file->close();

  im_dest = gdImageCreate(dest_width, dest_height);
  if (im_dest == nullptr) {
    raise_warning("Unable to allocate destination buffer");
    return false;
  }

  white = gdImageColorAllocate(im_dest, 255, 255, 255);
  if (white == -1) {
    raise_warning("Unable to allocate the colors for "
                    "the destination buffer");
    return false;
  }

  black = gdImageColorAllocate(im_dest, 0, 0, 0);
  if (black == -1) {
    raise_warning("Unable to allocate the colors for "
                    "the destination buffer");
    return false;
  }

  threshold = threshold * 32;

  for (y = 0; y < dest_height; y++) {
    for (x = 0; x < dest_width; x++) {
      color_org = gdImageGetPixel (im_tmp, x, y);
      median = (im_tmp->red[color_org] +
                im_tmp->green[color_org] +
                im_tmp->blue[color_org]) / 3;
      if (median < threshold) {
        color = black;
      } else {
        color = white;
      }
      gdImageSetPixel(im_dest, x, y, color);
    }
  }

  gdImageDestroy(im_tmp);

  gdImageWBMP(im_dest, black , dest);

  fflush(dest);
  dest_file->close();

  gdImageDestroy(im_dest);

  return true;
}


Variant preg_match(const String& pattern, const String& subject,
                   Variant* matches /* = nullptr */, int flags /* = 0 */,
                   int offset /* = 0 */) {
  return preg_match(pattern.get(), subject.get(), matches, flags, offset);
}


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


static void pcre_log_error(const char* func, int line, int pcre_code,
                           const char* pattern, int pattern_size,
                           const char* subject, int subject_size,
                           const char* repl, int repl_size,
                           int arg1 = 0, int arg2 = 0,
                           int arg3 = 0, int arg4 = 0) {
  const char* escapedPattern;
  const char* escapedSubject;
  const char* escapedRepl;
  std::string p(pattern, pattern_size);
  std::string s(subject, subject_size);
  std::string r(repl, repl_size);
  escapedPattern = Logger::EscapeString(p);
  escapedSubject = Logger::EscapeString(s);
  escapedRepl = Logger::EscapeString(r);
  const char* errString =
    (pcre_code == PCRE_ERROR_MATCHLIMIT) ? "PCRE_ERROR_MATCHLIMIT" :
    (pcre_code == PCRE_ERROR_RECURSIONLIMIT) ? "PCRE_ERROR_RECURSIONLIMIT" :
    "UNKNOWN";
  raise_warning_unsampled(
    "REGEXERR: %s/%d: err=%d(%s), pattern='%s', subject='%s', repl='%s', "
    "limits=(%" PRId64 ", %" PRId64 "), extra=(%d, %d, %d, %d)",
    func, line, pcre_code, errString,
    escapedPattern, escapedSubject, escapedRepl,
    tl_pcre_globals->preg_backtrace_limit,
    tl_pcre_globals->preg_recursion_limit,
    arg1, arg2, arg3, arg4);
  free((void *)escapedPattern);
  free((void *)escapedSubject);
  free((void *)escapedRepl);
}


TypedValue HHVM_FUNCTION(substr, StringArg str, int start, int length) {
  auto const size = str.get()->size();
  if (!string_substr_check(size, start, length)) {
    if (RuntimeOption::PHP7_Substr && size == start) {
      return make_tv<KindOfPersistentString>(empty_string_ref.get());
    } else {
      return make_tv<KindOfBoolean>(false);
    }
  }
  return tvReturn(String::attach(str.get()->substr(start, length)));
}


Variant HHVM_FUNCTION(money_format,
                      const String& format,
                      double number) {
  String s = StringUtil::MoneyFormat(format.c_str(), number);
  if (s.isNull()) return false;
  return s;
}


static int exif_file_sections_realloc(image_info_type *ImageInfo,
                                      int section_index, size_t size) {
  void *tmp;

  /* This is not a malloc/realloc check. It is a plausibility check for the
   * function parameters (requirements engineering).
   */
  if (section_index >= ImageInfo->file.count) {
    raise_warning("Illegal reallocating of undefined file section");
    return -1;
  }
  tmp = IM_REALLOC(ImageInfo->file.list[section_index].data, size);
  CHECK_ALLOC_R(tmp, size, -1);
  ImageInfo->file.list[section_index].data = (unsigned char *)tmp;
  ImageInfo->file.list[section_index].size = size;
  return 0;
}


  static bool HasConverted(int64_t n) {
    return MinPrecomputedInteger <= n && n <= MaxPrecomputedInteger;
  }


int64_t HHVM_FUNCTION(imagefontwidth, int64_t font) {
  return php_imagefontsize(font, 0);
}


Variant HHVM_FUNCTION(unserialize, const String& str,
                                   const Array& options) {
  return unserialize_from_string(
    str,
    VariableUnserializer::Type::Serialize,
    options
  );
}


bool HHVM_FUNCTION(imagesetstyle, const Resource& image, const Array& style) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  int *stylearr;
  int index;
  size_t malloc_size = sizeof(int) * style.size();
  stylearr = (int *)IM_MALLOC(malloc_size);
  CHECK_ALLOC_R(stylearr, malloc_size, false);
  index = 0;
  for (ArrayIter iter(style); iter; ++iter) {
    stylearr[index++] = tvToInt(iter.secondVal());
  }
  gdImageSetStyle(im, stylearr, index);
  IM_FREE(stylearr);
  return true;
}


bool HHVM_FUNCTION(imagesetthickness,
    const Resource& image, int64_t thickness) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImageSetThickness(im, thickness);
  return true;
}


String(StringData* sd, NoIncRef) : m_str(sd, NoIncRef{}) {}


static int php_jpg_get16(void *value) {
  return (((unsigned char *)value)[0] << 8) | ((unsigned char *)value)[1];
}


Variant ProfilerFactory::stop() {
  if (m_profiler) {
    m_profiler->endAllFrames();

    Array ret;
    m_profiler->writeStats(ret);
    req::destroy_raw(m_profiler);
    m_profiler = nullptr;
    RequestInfo::s_requestInfo->m_profiler = nullptr;

    return ret;
  }
  return init_null();
}


static ALWAYS_INLINE void do_var_dump(VariableSerializer& vs,
                                      const Variant& expression) {
  // manipulate maxCount to match PHP behavior
  if (!expression.isObject()) {
    vs.incMaxCount();
  }
  vs.serialize(expression, false);
}


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

  if (!stream->seek(11, SEEK_CUR)) return nullptr;

  String dim = stream->read(8);
  if (dim.length() != 8) return nullptr;
  s = (unsigned char *)dim.c_str();
  result = (struct gfxinfo *)IM_CALLOC(1, sizeof(struct gfxinfo));
  CHECK_ALLOC_R(result, (sizeof(struct gfxinfo)), nullptr);
  result->height = (((unsigned int)s[0]) << 24) +
                   (((unsigned int)s[1]) << 16) +
                   (((unsigned int)s[2]) << 8) +
                   ((unsigned int)s[3]);
  result->width =  (((unsigned int)s[4]) << 24) +
                   (((unsigned int)s[5]) << 16) +
                   (((unsigned int)s[6]) << 8) +
                   ((unsigned int)s[7]);
  return result;
}


bool MemFile::flush() {
  raise_fatal_error((std::string("cannot flush a mem stream: ") +
                             getName()).c_str());
}


static void php_gdimagecharup(gdImagePtr im, gdFontPtr f, int x, int y,
                              int c, int color) {
  int cx, cy, px, py, fline;
  cx = 0;
  cy = 0;

  if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
    return;
  }

  fline = (c - f->offset) * f->h * f->w;
  for (py = y; (py > (y - f->w)); py--) {
    for (px = x; (px < (x + f->h)); px++) {
      if (f->data[fline + cy * f->w + cx]) {
        gdImageSetPixel(im, px, py, color);
      }
      cy++;
    }
    cy = 0;
    cx++;
  }
}


bool HHVM_FUNCTION(image2wbmp, const Resource& image,
                   const String& filename /* = null_string */,
                   int64_t threshold /* = -1 */) {
  return _php_image_output(image, filename, threshold, -1,
                           PHP_GDIMG_CONVERT_WBM, "WBMP",
                           (void (*)())_php_image_bw_convert);
}


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


static struct gfxinfo *php_handle_png(const req::ptr<File>& stream) {
  struct gfxinfo *result = nullptr;
  const unsigned char *s;
  /* Width:              4 bytes
   * Height:             4 bytes
   * Bit depth:          1 byte
   * Color type:         1 byte
   * Compression method: 1 byte
   * Filter method:      1 byte
   * Interlace method:   1 byte
   */

  if (!stream->seek(8, SEEK_CUR)) return nullptr;

  String dim = stream->read(9);
  if (dim.length() < 9) return nullptr;

  s = (unsigned char *)dim.c_str();
  result = (struct gfxinfo *)IM_CALLOC(1, sizeof(struct gfxinfo));
  CHECK_ALLOC_R(result, sizeof (struct gfxinfo), nullptr);
  result->width = (((unsigned int)s[0]) << 24) +
                   (((unsigned int)s[1]) << 16) +
                   (((unsigned int)s[2]) << 8) +
                   ((unsigned int)s[3]);
  result->height = (((unsigned int)s[4]) << 24) +
                   (((unsigned int)s[5]) << 16) +
                   (((unsigned int)s[6]) << 8) +
                   ((unsigned int)s[7]);
  result->bits = (unsigned int)s[8];
  return result;
}


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


TypedValue HHVM_FUNCTION(chunk_split, const String& body,
                      int chunklen, const String& end) {
  return tvReturn(StringUtil::ChunkSplit(body, chunklen, end));
}


  StringData* get() const {
    assertx(valid());
    return m_str.get();
  }


Variant HHVM_FUNCTION(fsockopen,
                      const String& hostname,
                      int port,
                      Variant& errnum,
                      Variant& errstr,
                      double timeout /* = -1.0 */) {
  HostURL hosturl(static_cast<const std::string>(hostname), port);
  return sockopen_impl(hosturl, errnum, errstr, timeout, false, uninit_variant);
}


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


inline int php_iptc_put1(req::ptr<File> /*file*/, int spool, unsigned char c,
                         unsigned char** spoolbuf) {
  if (spool > 0) {
    g_context->write((const char *)&c, 1);
  }

  if (spoolbuf) *(*spoolbuf)++ = c;

  return c;
}


bool HHVM_FUNCTION(imagealphablending, const Resource& image,
                                       bool blendmode) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImageAlphaBlending(im, blendmode);
  return true;
}


Variant HHVM_FUNCTION(imagecreate, int64_t width, int64_t height) {
  gdImagePtr im;
  if (width <= 0 || height <= 0 || width >= INT_MAX || height >= INT_MAX) {
    raise_warning("Invalid image dimensions");
    return false;
  }
  im = gdImageCreate(width, height);
  if (!im) {
    return false;
  }
  return Variant(req::make<Image>(im));
}


bool pcre_literal_data::isLiteral() const {
  return literal_str.has_value();
}


  String rval(const StringData *key) const {
    not_reached();
    return rvalImpl(key ? key->toInt32() : 0);
  }


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


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

  return stringForEachBuffered(str.size(), str,
    [&] (StringBuffer& ret, const char*& src, const char* end) {
      char c;
      const char* p;

      if (*src != '\\' || src + 1 == end) {
        ret.append(*src);
        return;
      }

      switch (*++src) {
        case 'n': ret.append('\n'); break;
        case 'r': ret.append('\r'); break;
        case 'a': ret.append('\a'); break;
        case 't': ret.append('\t'); break;
        case 'v': ret.append('\v'); break;
        case 'b': ret.append('\b'); break;
        case 'f': ret.append('\f'); break;
        case '\\': ret.append('\\'); break;
        case 'x':
          if (src + 1 == end || !isxdigit(src[1])) {
            ret.append(*src);
            break;
          }

          for (c = 0, ++src, p = src + 2; src < p && src < end &&
               isxdigit(*src); ++src) {
            c *= 16;
            c += (*src < 'A' ? *src - '0' :
                  *src < 'a' ? *src - 'A' + 10 :
                               *src - 'a' + 10);
          }
          ret.append(c);
          src--;
          break;

        default:
          if (*src < '0' || '7' < *src) {
            // The character after the slash is nothing special. Append it
            // unchanged to the result.
            ret.append(*src);
            break;
          }

          // Decode a base 8 number up to 3 characters long.
          for (c = 0, p = src + 3; src < p && src < end && '0' <= *src &&
               *src < '8'; ++src) {
            c *= 8;
            c += (*src - '0');
          }
          ret.append(c);
          src--;
      }
    });
}


const String& o_getClassNameHook() const override { return classnameof(); }


exif_process_SOFn(unsigned char* Data, int /*marker*/, jpeg_sof_info* result) {
  result->bits_per_sample = Data[2];
  result->height          = php_jpg_get16(Data+3);
  result->width           = php_jpg_get16(Data+5);
  result->num_components  = Data[7];
}


  TraceEntry* nextTraceEntry() {
    if (!isTraceSpaceAvailable() && !ensureTraceSpace()) {
      return 0;
    }
    return &m_traceBuffer[m_nextTraceEntry++];
  }


  TypedValue asTypedValue() const {
    assertx(m_str);
    return make_tv<KindOfString>(m_str.get());
  }


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


Variant HHVM_FUNCTION(print_r, const Variant& expression,
                               bool ret /* = false */) {
  Variant res;
  try {
    VariableSerializer vs(VariableSerializer::Type::PrintR);
    if (ret) {
      res = vs.serialize(expression, ret);
    } else {
      vs.serialize(expression, ret);
      res = true;
    }
  } catch (StringBufferLimitException& e) {
    raise_notice("print_r() exceeded max bytes limit");
    res = e.m_result;
  }
  return res;
}


String HHVM_FUNCTION(chop,
                      const String& str,
                      const String& charlist /* = k_HPHP_TRIM_CHARLIST */) {
  return stringTrim<false,true>(str, charlist);
}


Variant HHVM_FUNCTION(socket_get_option,
                      const Resource& socket,
                      int level,
                      int optname) {
  auto sock = cast<Socket>(socket);
  socklen_t optlen;

  switch (optname) {
  case SO_LINGER:
    {
      struct linger linger_val;
      optlen = sizeof(linger_val);
      if (getsockopt(sock->fd(), level, optname, (char*)&linger_val,
                     &optlen) != 0) {
        SOCKET_ERROR(sock, "unable to retrieve socket option", errno);
        return false;
      }

      return make_darray(
        s_l_onoff, linger_val.l_onoff,
        s_l_linger, linger_val.l_linger
      );
    }
    break;

  case SO_RCVTIMEO:
  case SO_SNDTIMEO:
    {
      struct timeval tv;
      optlen = sizeof(tv);
      if (getsockopt(sock->fd(), level, optname, (char*)&tv, &optlen) != 0) {
        SOCKET_ERROR(sock, "unable to retrieve socket option", errno);
        return false;
      }
      return make_darray(
        s_sec,  (int)tv.tv_sec,
        s_usec, (int)tv.tv_usec
      );
    }
    break;

  default:
    {
      int other_val;
      optlen = sizeof(other_val);
      if (getsockopt(sock->fd(), level, optname, (char*)&other_val, &optlen)) {
        SOCKET_ERROR(sock, "unable to retrieve socket option", errno);
        return false;
      }
      return other_val;
    }
  }
  not_reached();
}


static bool string_substr_check(int len, int& f, int& l) {
  assertx(len >= 0);

  if (l < 0 && -l > len) {
    return false;
  }
  if (f >= len) {
    return false;
  }

  if (l > len) {
    l = len;
  }

  if (f < 0 && -f > len) {
    f = 0;
    if (len == 0) {
      return false;
    }
  }

  if (l < 0 && l + len < f) {
    return false;
  }

  // If "from" position is negative, count start position from the end.
  if (f < 0) {
    f += len;
  }
  assertx(f >= 0);

  // If "length" position is negative, set it to the length needed to stop that
  // many chars from the end of the string.
  if (l < 0) {
    l += len - f;
    if (l < 0) {
      l = 0;
    }
  }
  assertx(l >= 0);

  return true;
}


static bool php_set_inet_addr(struct sockaddr_in *sin,
                              const char *address,
                              req::ptr<Socket> sock) {
  struct in_addr tmp;

  if (inet_aton(address, &tmp)) {
    sin->sin_addr.s_addr = tmp.s_addr;
  } else {
    HostEnt result;
    if (!safe_gethostbyname(address, result)) {
      /* Note: < -10000 indicates a host lookup error */
      SOCKET_ERROR(sock, "Host lookup failed", (-10000 - result.herr));
      return false;
    }
    if (result.hostbuf.h_addrtype != AF_INET) {
      raise_warning("Host lookup failed: Non AF_INET domain "
                      "returned on AF_INET socket");
      return false;
    }
    memcpy(&(sin->sin_addr.s_addr), result.hostbuf.h_addr_list[0],
           result.hostbuf.h_length);
  }

  return true;
}


static void incr_us_interval(struct timeval *start, uint64_t incr) {
  incr += (start->tv_sec * 1000000 + start->tv_usec);
  start->tv_sec  = incr/1000000;
  start->tv_usec = incr%1000000;
  return;
}


bool HHVM_FUNCTION(imagefilter, const Resource& res,
    int64_t filtertype,
    const Variant& arg1 /*=0*/, const Variant& arg2 /*=0*/,
    const Variant& arg3 /*=0*/, const Variant& arg4 /*=0*/) {
  gdImagePtr im = get_valid_image_resource(res);
  if (!im) return false;

/* Exists purely to mirror PHP5's invalid arg logic for this function */
#define IMFILT_TYPECHK(n) \
  if (!arg##n.isBoolean() && !arg##n.isNumeric(true)) { \
    raise_warning("imagefilter() expected boolean/numeric for argument %d", \
                  (n+2)); \
    return false; \
  }
  IMFILT_TYPECHK(1)
  IMFILT_TYPECHK(2)
  IMFILT_TYPECHK(3)
  IMFILT_TYPECHK(4)
#undef IMFILT_TYPECHECK

  using image_filter = bool (*)(gdImagePtr, int, int, int, int);
  image_filter filters[] = {
    php_image_filter_negate,
    php_image_filter_grayscale,
    php_image_filter_brightness,
    php_image_filter_contrast,
    php_image_filter_colorize,
    php_image_filter_edgedetect,
    php_image_filter_emboss,
    php_image_filter_gaussian_blur,
    php_image_filter_selective_blur,
    php_image_filter_mean_removal,
    php_image_filter_smooth,
    php_image_filter_pixelate,
  };
  auto const num_filters = sizeof(filters) / sizeof(image_filter);
  if (filtertype >= 0 && filtertype < num_filters) {
    return filters[filtertype](im, arg1.toInt64(), arg2.toInt64(),
                                          arg3.toInt64(), arg4.toInt64());
  }
  return false;
}


static int exif_scan_thumbnail(image_info_type *ImageInfo) {
  unsigned char c, *data = (unsigned char*)ImageInfo->Thumbnail.data;
  int n, marker;
  size_t length=2, pos=0;
  jpeg_sof_info sof_info;

  if (!data || ImageInfo->Thumbnail.size < 4) {
    return 0; /* nothing to do here */
  }
  if (memcmp(data, "\xFF\xD8\xFF", 3)) {
    if (!ImageInfo->Thumbnail.width && !ImageInfo->Thumbnail.height) {
      raise_warning("Thumbnail is not a JPEG image");
    }
    return 0;
  }
  for (;;) {
    pos += length;
    if (pos>=ImageInfo->Thumbnail.size)
      return 0;
    c = data[pos++];
    if (pos>=ImageInfo->Thumbnail.size)
      return 0;
    if (c != 0xFF) {
      return 0;
    }
    n = 8;
    while ((c = data[pos++]) == 0xFF && n--) {
      if (pos+3>=ImageInfo->Thumbnail.size)
        return 0;
      /* +3 = pos++ of next check when reaching marker + 2 bytes for length */
    }
    if (c == 0xFF)
      return 0;
    marker = c;
    if (ImageInfo->Thumbnail.size - 2 < pos) return 0;
    length = php_jpg_get16(data+pos);
    if (length > ImageInfo->Thumbnail.size || pos >= ImageInfo->Thumbnail.size - length) {
      return 0;
    }
    switch (marker) {
      case M_SOF0:
      case M_SOF1:
      case M_SOF2:
      case M_SOF3:
      case M_SOF5:
      case M_SOF6:
      case M_SOF7:
      case M_SOF9:
      case M_SOF10:
      case M_SOF11:
      case M_SOF13:
      case M_SOF14:
      case M_SOF15:
        /* handle SOFn block */
        if (length < 8 || ImageInfo->Thumbnail.size - 8 < pos) {
          /* exif_process_SOFn needs 8 bytes */
          return 0;
        }
        exif_process_SOFn(data+pos, marker, &sof_info);
        ImageInfo->Thumbnail.height   = sof_info.height;
        ImageInfo->Thumbnail.width    = sof_info.width;
        return 1;

      case M_SOS:
      case M_EOI:
        raise_warning("Could not compute size of thumbnail");
        return 0;
        break;

      default:
        /* just skip */
        break;
    }
  }

  raise_warning("Could not compute size of thumbnail");
  return 0;
}


tv_to_cycles(const struct timeval& tv, int64_t MHz)
{
  return (((uint64_t)tv.tv_sec * 1000000) + tv.tv_usec) * MHz;
}


void PCRECache::reinit(CacheKind kind) {
  switch (m_kind) {
    case CacheKind::Static:
      if (m_staticCache.load()) {
        DestroyStatic(m_staticCache);
        m_staticCache = nullptr;
      }
      break;
    case CacheKind::Lru:
      m_lruCache.reset();
      break;
    case CacheKind::Scalable:
      m_scalableCache.reset();
      break;
  }
  m_kind = kind;

  switch (kind) {
    case CacheKind::Static:
      m_staticCache = CreateStatic();
      m_expire = time(nullptr) + RuntimeOption::EvalPCREExpireInterval;
      break;
    case CacheKind::Lru:
      m_lruCache.reset(new LRUCache(RuntimeOption::EvalPCRETableSize));
      break;
    case CacheKind::Scalable:
      m_scalableCache.reset(
        new ScalableCache(RuntimeOption::EvalPCRETableSize));
      break;
  }
}


static int php_vspprintf_ap(char **pbuf, size_t max_len,
                            const char *fmt, va_list ap) {
  char *buf;
  int len = vspprintf_ap(&buf, max_len, fmt, ap);
  if (buf) {
#ifdef IM_MEMORY_CHECK
    *pbuf = php_strndup_impl(buf, len, __LINE__);
#else
    *pbuf = php_strndup_impl(buf, len);
#endif
    free(buf);
  }
  return len;
}


Variant HHVM_FUNCTION(imagecreatefromwebp, const String& filename) {
  gdImagePtr im =
    _php_image_create_from(filename, -1, -1, -1, -1,
                           PHP_GDIMG_TYPE_WEBP, "WEBP",
                           (gdImagePtr(*)())gdImageCreateFromWebp,
                           (gdImagePtr(*)())gdImageCreateFromWebpCtx);
  if (im == nullptr) {
    return false;
  }
  return Variant(req::make<Image>(im));
}


void WuManberReplacement::initTables() {
  size_t max_shift = m - B + 1;
  hash.fill(-1);
  shift.fill(max_shift);
  prefix.reserve(patterns.size());

  strtr_compare_hash_suffix comparator(m, B);
  std::sort(patterns.begin(), patterns.end(), comparator);

  {
    uint16_t last_h = -1; // assumes not all bits are used
    // patterns is already ordered by hash.
    // Make hash[h] de index of the first pattern in
    // patterns that has hash
    int size = patterns.size();
    for(int i = 0; i != size; ++i) {
      // init hash tab
      uint16_t h = patterns[i].hash(m - B, B) & HASH_TAB_MASK;
      if (h != last_h) {
        hash[h] = i;
        last_h = h;
      }
      // init shift tab
      for (int j = 0; j < max_shift; j++) {
        uint16_t h2 = patterns[i].hash( j, B ) & SHIFT_TAB_MASK;
        assertx((long long) m - (long long) j - B >= 0);
        shift[h2] = MIN(shift[h2], m - j - B);
      }
      // init prefix
      prefix.push_back(patterns[i].hash(0, Bp));
    }
  }

  hash[HASH_TAB_SIZE] = patterns.size();  // OK, we allocated SIZE+1
  for (int i = HASH_TAB_SIZE - 1; i >= 0; i--) {
    if (hash[i] == -1) {
      hash[i] = hash[i + 1];
    }
  }
}


inline bool StringData::isZero() const  {
  return size() == 1 && data()[0] == '0';
}


int size() const { return m_px ? m_px->size() : 0; }


bool HHVM_FUNCTION(imagewebp, const Resource& image,
    const String& filename /* = null_string */,
    int64_t quality /* = 80 */) {
  return _php_image_output_ctx(image, filename, quality, -1,
                               PHP_GDIMG_TYPE_WEBP, "WEBP",
                               (void (*)())gdImageWebpCtx);
}


void PCRECache::clearStatic() {
  std::unique_lock<std::mutex> lock(m_clearMutex, std::try_to_lock);
  if (!lock) return;

  auto newExpire = time(nullptr) + RuntimeOption::EvalPCREExpireInterval;
  m_expire.store(newExpire, std::memory_order_relaxed);

  auto tmpMap = CreateStatic();
  tmpMap = m_staticCache.exchange(tmpMap, std::memory_order_acq_rel);

  Treadmill::enqueue([tmpMap]() {
      DestroyStatic(tmpMap);
   });
}


ALWAYS_INLINE const String& asCStrRef(tv_rval tv) {
  assertx(tvIsPlausible(*tv));
  assertx(isStringType(type(tv)));
  return reinterpret_cast<const String&>(val(tv).pstr);
}


String HHVM_FUNCTION(serialize, const Variant& value) {
  return serialize_impl(value, SerializeOptions());
}


Variant HHVM_FUNCTION(imagetruecolortopalette, const Resource& image,
    bool dither, int64_t ncolors) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;

  if (ncolors <= 0 || ncolors >= INT_MAX) {
    raise_warning("Number of colors has to be greater than zero");
    return false;
  }
  gdImageTrueColorToPalette(im, dither, ncolors);
  return true;
}


Variant HHVM_FUNCTION(imagecropauto,
                      const Resource& image,
                      int64_t mode /* = -1 */,
                      double threshold /* = 0.5f */,
                      int64_t color /* = -1 */) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImagePtr imcropped = nullptr;
  switch (mode) {
    case -1:
      mode = GD_CROP_DEFAULT;
    case GD_CROP_DEFAULT:
    case GD_CROP_TRANSPARENT:
    case GD_CROP_BLACK:
    case GD_CROP_WHITE:
    case GD_CROP_SIDES:
      imcropped = gdImageCropAuto(im, mode);
      break;

    case GD_CROP_THRESHOLD:
      if (color < 0) {
        raise_warning("imagecropauto(): Color argument missing "
                      "with threshold mode");
        return false;
      }
      imcropped = gdImageCropThreshold(im, color, (float) threshold);
      break;

    default:
      raise_warning("imagecropauto(): Unknown crop mode");
      return false;
  }
  if (!imcropped) {
    return false;
  }
  return Variant(req::make<Image>(imcropped));
}


bool HHVM_FUNCTION(imagejpeg, const Resource& image,
    const String& filename /* = null_string */, int64_t quality /* = -1 */) {
  return _php_image_output_ctx(image, filename, quality, -1,
                               PHP_GDIMG_TYPE_JPG, "JPEG",
                               (void (*)())gdImageJpegCtx);
}


String stringTrim(const String& str, const String& charlist) {
  char flags[256];
  string_charmask(charlist.c_str(), charlist.size(), flags);

  auto len = str.size();
  int start = 0, end = len - 1;

  if (left) {
    for (; start < len && flags[(unsigned char)str[start]]; ++start)
      /* do nothing */;
  }

  if (right) {
    for (; end >= start && flags[(unsigned char)str[end]]; --end) {}
  }

  return str.substr(start, end - start + 1);
}


void onStrobelightSignal(int signo) {
  if (!RuntimeOption::StrobelightEnabled) {
    // Handle the signal so we don't crash, but do nothing.
    return;
  }

  if (signo == strobelight::kSignumCurrent) {
    // sets on only current thread
    if (rds::isFullyInitialized()) {
      // Ignore threads that are not serving requests, otherwise this segfaults
      if (!Strobelight::isXenonActive()) {
        // Do not set the flag if Xenon is actively profiling this request
        setSurpriseFlag(XenonSignalFlag);
      }
    }
  }

  // surpriseAll currently has an issue where the isXenonActive() check will
  // try to access s_xenonData->getIsProfiledRequest() to check if the current
  // request is profiling. The problem is that you really want to check if the
  // request t is profiling. The current thread may not even be a request thread.
  // If we ever want to start using this signal for profiling,
  // we will need to figure out how to work around that problem.
  // if (signo == strobelight::kSignumAll) {
  //  // sets on ALL threads
  //  Strobelight::getInstance().surpriseAll();
  // }
}


int string_ncmp(const char *s1, const char *s2, int len) {
  for (int i = 0; i < len; i++) {
    char c1 = s1[i];
    char c2 = s2[i];
    if (c1 > c2) return 1;
    if (c1 < c2) return -1;
  }
  return 0;
}


  String rval(const char* key) const {
    return rvalImpl(String(key).toInt32());
  }


  bool shouldSkipBuiltins() const override {
    return m_flags & NoTrackBuiltins;
  }


  bool isTraceSpaceAvailable() {
    // the two slots are reserved for internal use
    return m_nextTraceEntry < m_traceBufferSize - 3;
  }


  String(char* s, size_t length, AttachStringMode mode)
  : m_str(LIKELY((bool)s) ? StringData::Make(s, length, mode)
                          : nullptr, NoIncRef{}) { }


Variant HHVM_FUNCTION(imagecreatefromgd2part,
    const String& filename,
    int64_t srcx, int64_t srcy, int64_t width, int64_t height) {
  gdImagePtr im =
    _php_image_create_from(filename, srcx, srcy, width, height,
                           PHP_GDIMG_TYPE_GD2PART, "GD2",
                           (gdImagePtr(*)())gdImageCreateFromGd2Part,
                           (gdImagePtr(*)())gdImageCreateFromGd2PartCtx);
  if (im == nullptr) {
    return false;
  }
  return Variant(req::make<Image>(im));
}


TypedValue HHVM_FUNCTION(stripos,
                         const String& haystack,
                         const Variant& needle,
                         int offset /* = 0 */) {
  if (offset < 0 || offset > haystack.size()) {
    raise_warning("Offset not contained in string");
    return make_tv<KindOfBoolean>(false);
  }
  int pos;
  if (needle.isString()) {
    pos = haystack.find(needle.toString(), offset, false);
  } else {
    pos = haystack.find(needle.toByte(), offset, false);
  }
  if (pos >= 0) return make_tv<KindOfInt64>(pos);
  return make_tv<KindOfBoolean>(false);
}


String HHVM_FUNCTION(image_type_to_mime_type, int64_t imagetype) {
  switch (imagetype) {
    case IMAGE_FILETYPE_GIF:
      return "image/gif";
    case IMAGE_FILETYPE_JPEG:
      return "image/jpeg";
    case IMAGE_FILETYPE_PNG:
      return "image/png";
    case IMAGE_FILETYPE_SWF:
    case IMAGE_FILETYPE_SWC:
      return "application/x-shockwave-flash";
    case IMAGE_FILETYPE_PSD:
      return "image/psd";
    case IMAGE_FILETYPE_BMP:
      return "image/x-ms-bmp";
    case IMAGE_FILETYPE_TIFF_II:
    case IMAGE_FILETYPE_TIFF_MM:
      return "image/tiff";
    case IMAGE_FILETYPE_IFF:
      return "image/iff";
    case IMAGE_FILETYPE_WBMP:
      return "image/vnd.wap.wbmp";
    case IMAGE_FILETYPE_JPC:
      return "application/octet-stream";
    case IMAGE_FILETYPE_JP2:
      return "image/jp2";
    case IMAGE_FILETYPE_XBM:
      return "image/xbm";
    case IMAGE_FILETYPE_ICO:
      return "image/vnd.microsoft.icon";
    default:
    case IMAGE_FILETYPE_UNKNOWN:
      return "application/octet-stream"; /* suppose binary format */
  }
}


  String& asString() {
    return *reinterpret_cast<String*>(this);
  }


void PCRECache::dump(folly::File& file) {
  switch (m_kind) {
    case CacheKind::Static:
      for (auto& it : *m_staticCache) {
        folly::writeFull(file.fd(), it.first->data(), it.first->size());
        folly::writeFull(file.fd(), "\n", 1);
      }
      break;
    case CacheKind::Lru:
    case CacheKind::Scalable:
      {
        std::vector<LRUCacheKey> keys;
        if (m_kind == CacheKind::Lru) {
          m_lruCache->snapshotKeys(keys);
        } else {
          m_scalableCache->snapshotKeys(keys);
        }
        for (auto& key: keys) {
          folly::writeFull(file.fd(), key.data(), key.size());
          folly::writeFull(file.fd(), "\n", 1);
        }
      }
      break;
  }
}


bool HHVM_FUNCTION(mcrypt_generic_end, const Resource& td) {
  return HHVM_FUNCTION(mcrypt_generic_deinit, td);
}


static Variant php_replace_in_subject(const Variant& regex, const Variant& replace,
                                      String subject, int limit, bool callable,
                                      int* replace_count) {
  if (!regex.isArray()) {
    return php_pcre_replace(regex.toString(), subject, replace, callable,
                            limit, replace_count);
  }

  if (callable || !replace.isArray()) {
    Array arr = regex.toDArray();
    for (ArrayIter iterRegex(arr); iterRegex; ++iterRegex) {
      String regex_entry = iterRegex.second().toString();
      auto ret = php_pcre_replace(regex_entry, subject, replace, callable,
                                  limit, replace_count);
      if (!ret.isString()) {
        assertx(ret.isNull());
        return ret; // php_pcre_replace already set error
      }
      subject = ret.asStrRef();
      assertx(!subject.isNull());
    }
    return preg_return_no_error(std::move(subject));
  }

  Array arrReplace = replace.toDArray();
  Array arrRegex = regex.toDArray();
  ArrayIter iterReplace(arrReplace);
  for (ArrayIter iterRegex(arrRegex); iterRegex; ++iterRegex) {
    String regex_entry = iterRegex.second().toString();
    Variant replace_value;
    if (iterReplace) {
      replace_value = iterReplace.second();
      ++iterReplace;
    }

    auto ret = php_pcre_replace(regex_entry, subject, replace_value, callable,
                                limit, replace_count);
    if (!ret.isString()) {
      assertx(ret.isNull());
      return ret; // php_pcre_replace already set error
    }
    subject = ret.asStrRef();
    assertx(!subject.isNull());
  }
  return preg_return_no_error(std::move(subject));
}


bool f_imageistruecolor(const Resource& image) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  return im->trueColor;
}


static bool php_set_inet6_addr(struct sockaddr_in6 *sin6,
                               const char *address,
                               req::ptr<Socket> sock) {
  struct in6_addr tmp;
  struct addrinfo hints;
  struct addrinfo *addrinfo = NULL;

  if (inet_pton(AF_INET6, address, &tmp)) {
    memcpy(&(sin6->sin6_addr.s6_addr), &(tmp.s6_addr),
           sizeof(struct in6_addr));
  } else {
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = PF_INET6;
    getaddrinfo(address, NULL, &hints, &addrinfo);
    if (!addrinfo) {
      // 10000 is a magic value to indicate a host error.
      SOCKET_ERROR(sock, "Host lookup failed", (-10000 - h_errno));
      return false;
    }
    if (addrinfo->ai_family != PF_INET6 ||
        addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) {
      raise_warning("Host lookup failed: Non AF_INET6 domain "
                      "returned on AF_INET6 socket");
      freeaddrinfo(addrinfo);
      return false;
    }

    memcpy(&(sin6->sin6_addr.s6_addr),
           ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr,
           sizeof(struct in6_addr));
    freeaddrinfo(addrinfo);
  }

  const char *scope = strchr(address, '%');
  if (scope++) {
    int64_t lval = 0;
    double dval = 0;
    unsigned scope_id = 0;
    if (KindOfInt64 == is_numeric_string(scope, strlen(scope), &lval, &dval,
                                         0)) {
      if (lval > 0 && lval <= UINT_MAX) {
        scope_id = lval;
      }
    } else {
      php_string_to_if_index(scope, &scope_id);
    }

    sin6->sin6_scope_id = scope_id;
  }

  return true;
}


Variant HHVM_FUNCTION(mdecrypt_generic, const Resource& td,
                                        const String& data) {
  return mcrypt_generic(td, data, true);
}


Variant HHVM_FUNCTION(pfsockopen,
                      const String& hostname,
                      int port,
                      Variant& errnum,
                      Variant& errstr,
                      double timeout /* = -1.0 */) {
  HostURL hosturl(static_cast<const std::string>(hostname), port);
  return sockopen_impl(hosturl, errnum, errstr, timeout, true, uninit_variant);
}


void Profiler::beginFrame(const char *symbol) {
  Frame *current = createFrame(symbol);

  // NOTE(cjiang): use hash code to fend off most of call-stack traversal
  int recursion_level = 0;
  if (m_func_hash_counters[current->m_hash_code] > 0) {
    // Find this symbols recurse level
    for (Frame *p = current->m_parent; p; p = p->m_parent) {
      if (strcmp(current->m_name, p->m_name) == 0) {
        recursion_level = p->m_recursion + 1;
        break;
      }
    }
  }
  current->m_recursion = recursion_level;

  m_func_hash_counters[current->m_hash_code]++;
  beginFrameEx(symbol);
}


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


static bool php_imagepolygon(const Resource& image,
                             const Array& points, int num_points,
                             int color, int filled) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdPointPtr pts;
  int nelem, i;

  nelem = points.size();
  if (nelem < 6) {
    raise_warning("You must have at least 3 points in your array");
    return false;
  }

  if (nelem < num_points * 2) {
    raise_warning("Trying to use %d points in array with only %d points",
                    num_points, nelem/2);
    return false;
  }

  pts = (gdPointPtr)IM_MALLOC(num_points * sizeof(gdPoint));
  CHECK_ALLOC_R(pts, (num_points * sizeof(gdPoint)), false);

  for (i = 0; i < num_points; i++) {
    if (points.exists(i * 2)) {
      pts[i].x = points[i * 2].toInt32();
    }
    if (points.exists(i * 2 + 1)) {
      pts[i].y = points[i * 2 + 1].toInt32();
    }
  }

  if (filled) {
    gdImageFilledPolygon(im, pts, num_points, color);
  } else {
    color = SetupAntiAliasedColor(im, color);
    gdImagePolygon(im, pts, num_points, color);
  }

  IM_FREE(pts);
  return true;
}


inline bool StringData::isProxy() const { return m_data != payload(); }


  void close() {
    if (m_td != MCRYPT_FAILED) {
      mcrypt_generic_deinit(m_td);
      mcrypt_module_close(m_td);
      m_td = MCRYPT_FAILED;
    }
  }


Variant HHVM_FUNCTION(socket_recv,
                      const Resource& socket,
                      Variant& buf,
                      int64_t len,
                      int flags) {
  if (len <= 0) {
    return false;
  }
  auto sock = cast<Socket>(socket);

  char *recv_buf = (char *)malloc(len + 1);
  int retval;
  if ((retval = recv(sock->fd(), recv_buf, (size_t)len, flags)) < 1) {
    free(recv_buf);
    buf = init_null();
  } else {
    recv_buf[retval] = '\0';
    buf = String(recv_buf, retval, AttachString);
  }

  if (retval == -1) {
    SOCKET_ERROR(sock, "unable to read from socket", errno);
    return false;
  }
  return retval;
}


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

  c = file->getc();

  if (c == EOF) return EOF;

  if (spool > 0) {
    cc = c;
    g_context->write((const char *)&cc, 1);
  }

  if (spoolbuf) *(*spoolbuf)++ = c;

  return c;
}


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

  return stringForEachBuffered(
    str.size(), str,
    [&](StringBuffer& ret, const char* src, const char* /*end*/) {
      static char hexconvtab[] = "0123456789abcdef";
      ret.append(hexconvtab[(unsigned char)*src >> 4]);
      ret.append(hexconvtab[(unsigned char)*src & 15]);
    });
}


String HHVM_FUNCTION(html_entity_decode,
                     const String& str,
                     int flags /* = k_ENT_HTML_QUOTE_DOUBLE */,
                     const String& charset /* = "UTF-8" */) {
  const char *scharset = charset.data();
  if (!*scharset) scharset = "ISO-8859-1";
  return StringUtil::HtmlDecode(str, StringUtil::toQuoteStyle(flags),
                                scharset, true);
}


  void requestShutdown() override {
#ifdef IM_MEMORY_CHECK
    void *ptrs[1000];
    int n = 1000;
    if (m_mallocSize) imDump(ptrs, n);
    assertx(m_mallocSize == 0);
#endif
    m_mallocSize = 0;
  }


Variant HHVM_FUNCTION(imagecreatefromgd, const String& filename) {
  gdImagePtr im =
    _php_image_create_from(filename, -1, -1, -1, -1,
                           PHP_GDIMG_TYPE_GD, "GD",
                           (gdImagePtr(*)())gdImageCreateFromGd,
                           (gdImagePtr(*)())gdImageCreateFromGdCtx);
  if (im == nullptr) {
    return false;
  }
  return Variant(req::make<Image>(im));
}


  void checkArcBuff(int len) {
    len = 2*len + HP_STACK_DELIM_LEN + 2;
    if (len >= m_arcBuffLen) {
      m_arcBuffLen *= 2;
      m_arcBuff = (char *)realloc(m_arcBuff, m_arcBuffLen);
      if (m_arcBuff == nullptr) {
        throw std::bad_alloc();
      }
    }
  }


bool HHVM_FUNCTION(jpeg2wbmp, const String& jpegname, const String& wbmpname,
    int64_t dest_height, int64_t dest_width, int64_t threshold) {
  return _php_image_convert(jpegname, wbmpname, dest_height, dest_width,
                            threshold, PHP_GDIMG_TYPE_JPG);
}


static bool _php_image_output_ctx(const Resource& image, const String& filename,
                                  int quality, int basefilter, int image_type,
                                  char* /*tn*/, void (*func_p)()) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  req::ptr<File> file;
  FILE *fp = nullptr;
  int q = quality, i;
  int f = basefilter;
  gdIOCtx *ctx;

  /* The third (quality) parameter for Wbmp stands for the threshold
     when called from image2wbmp(). The third (quality) parameter for
     Wbmp and Xbm stands for the foreground color index when called
     from imagey<type>().
   */

  if (!filename.empty()) {
    file = php_open_plain_file(filename, "wb", &fp);
    if (!file) {
      raise_warning("Unable to open '%s' for writing", filename.c_str());
      return false;
    }
    ctx = gdNewFileCtx(fp);
  } else {
    ctx = (gdIOCtx *)IM_MALLOC(sizeof(gdIOCtx));
    CHECK_ALLOC_R(ctx, sizeof(gdIOCtx), false);
    ctx->putC = _php_image_output_putc;
    ctx->putBuf = _php_image_output_putbuf;
    ctx->gd_free = _php_image_output_ctxfree;
  }

  switch(image_type) {
  case PHP_GDIMG_CONVERT_WBM:
    if (q<0||q>255) {
      raise_warning("Invalid threshold value '%d'. "
                      "It must be between 0 and 255", q);
    }
  case PHP_GDIMG_TYPE_JPG:
    ((void(*)(gdImagePtr, gdIOCtx *, int))(func_p))(im, ctx, q);
    break;
  case PHP_GDIMG_TYPE_PNG:
    ((void(*)(gdImagePtr, gdIOCtx *, int, int))(func_p))(im, ctx, q, f);
    break;
  case PHP_GDIMG_TYPE_WEBP:
    ((void(*)(gdImagePtr, gdIOCtx *, int64_t, int))(func_p))(im, ctx, q, f);
    break;
  case PHP_GDIMG_TYPE_XBM:
  case PHP_GDIMG_TYPE_WBM:
    if (q == -1) { // argc < 3
      for(i=0; i < gdImageColorsTotal(im); i++) {
        if (!gdImageRed(im, i) &&
            !gdImageGreen(im, i) &&
            !gdImageBlue(im, i)) break;
      }
      q = i;
    }
    if (image_type == PHP_GDIMG_TYPE_XBM) {
      ((void(*)(gdImagePtr, char *, int, gdIOCtx *))(func_p))
        (im, (char*)filename.c_str(), q, ctx);
    } else {
      ((void(*)(gdImagePtr, int, gdIOCtx *))(func_p))(im, q, ctx);
    }
    break;
  default:
    ((void(*)(gdImagePtr, gdIOCtx *))(func_p))(im, ctx);
    break;
  }

  ctx->gd_free(ctx);

  if (fp) {
    fflush(fp);
    file->close();
  }

  return true;
}


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

  if (mcrypt_generic_deinit(pm->m_td) < 0) {
    raise_warning("Could not terminate encryption specifier");
    return false;
  }
  pm->m_init = false;
  return true;
}


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


Variant HHVM_FUNCTION(nl_langinfo, int item) {
#ifdef _MSC_VER
  raise_warning("nl_langinfo is not yet implemented on Windows!");
  return empty_string();
#else
  switch(item) {
#ifdef ABDAY_1
    case ABDAY_1:
    case ABDAY_2:
    case ABDAY_3:
    case ABDAY_4:
    case ABDAY_5:
    case ABDAY_6:
    case ABDAY_7:
#endif
#ifdef DAY_1
    case DAY_1:
    case DAY_2:
    case DAY_3:
    case DAY_4:
    case DAY_5:
    case DAY_6:
    case DAY_7:
#endif
#ifdef ABMON_1
    case ABMON_1:
    case ABMON_2:
    case ABMON_3:
    case ABMON_4:
    case ABMON_5:
    case ABMON_6:
    case ABMON_7:
    case ABMON_8:
    case ABMON_9:
    case ABMON_10:
    case ABMON_11:
    case ABMON_12:
#endif
#ifdef MON_1
    case MON_1:
    case MON_2:
    case MON_3:
    case MON_4:
    case MON_5:
    case MON_6:
    case MON_7:
    case MON_8:
    case MON_9:
    case MON_10:
    case MON_11:
    case MON_12:
#endif
#ifdef AM_STR
    case AM_STR:
#endif
#ifdef PM_STR
    case PM_STR:
#endif
#ifdef D_T_FMT
    case D_T_FMT:
#endif
#ifdef D_FMT
    case D_FMT:
#endif
#ifdef T_FMT
    case T_FMT:
#endif
#ifdef T_FMT_AMPM
    case T_FMT_AMPM:
#endif
#ifdef ERA
    case ERA:
#endif
#ifdef ERA_YEAR
    case ERA_YEAR:
#endif
#ifdef ERA_D_T_FMT
    case ERA_D_T_FMT:
#endif
#ifdef ERA_D_FMT
    case ERA_D_FMT:
#endif
#ifdef ERA_T_FMT
    case ERA_T_FMT:
#endif
#ifdef ALT_DIGITS
    case ALT_DIGITS:
#endif
#ifdef INT_CURR_SYMBOL
    case INT_CURR_SYMBOL:
#endif
#ifdef CURRENCY_SYMBOL
    case CURRENCY_SYMBOL:
#endif
#ifdef CRNCYSTR
    case CRNCYSTR:
#endif
#ifdef MON_DECIMAL_POINT
    case MON_DECIMAL_POINT:
#endif
#ifdef MON_THOUSANDS_SEP
    case MON_THOUSANDS_SEP:
#endif
#ifdef MON_GROUPING
    case MON_GROUPING:
#endif
#ifdef POSITIVE_SIGN
    case POSITIVE_SIGN:
#endif
#ifdef NEGATIVE_SIGN
    case NEGATIVE_SIGN:
#endif
#ifdef INT_FRAC_DIGITS
    case INT_FRAC_DIGITS:
#endif
#ifdef FRAC_DIGITS
    case FRAC_DIGITS:
#endif
#ifdef P_CS_PRECEDES
    case P_CS_PRECEDES:
#endif
#ifdef P_SEP_BY_SPACE
    case P_SEP_BY_SPACE:
#endif
#ifdef N_CS_PRECEDES
    case N_CS_PRECEDES:
#endif
#ifdef N_SEP_BY_SPACE
    case N_SEP_BY_SPACE:
#endif
#ifdef P_SIGN_POSN
    case P_SIGN_POSN:
#endif
#ifdef N_SIGN_POSN
    case N_SIGN_POSN:
#endif
#ifdef DECIMAL_POINT
    case DECIMAL_POINT:
#elif defined(RADIXCHAR)
    case RADIXCHAR:
#endif
#ifdef THOUSANDS_SEP
    case THOUSANDS_SEP:
#elif defined(THOUSEP)
    case THOUSEP:
#endif
#ifdef GROUPING
    case GROUPING:
#endif
#ifdef YESEXPR
    case YESEXPR:
#endif
#ifdef NOEXPR
    case NOEXPR:
#endif
#ifdef YESSTR
    case YESSTR:
#endif
#ifdef NOSTR
    case NOSTR:
#endif
#ifdef CODESET
    case CODESET:
#endif
      break;
    default:
      raise_warning("Item '%d' is not valid", item);
      return false;
  }

  auto const ret = nl_langinfo(item);
  if (ret == nullptr) {
    return false;
  }
  return String(ret);
#endif
}


Variant HHVM_FUNCTION(imagecrop, const Resource& image, const Array& rect) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImagePtr imcropped = nullptr;
  gdRect gdrect;
  if (rect.exists(s_x)) {
    gdrect.x = rect[s_x].toInt64();
  } else {
    raise_warning("imagecrop(): Missing x position");
    return false;
  }
  if (rect.exists(s_y)) {
    gdrect.y = rect[s_y].toInt64();
  } else {
    raise_warning("imagecrop(): Missing y position");
    return false;
  }
  if (rect.exists(s_width)) {
    gdrect.width = rect[s_width].toInt64();
  } else {
    raise_warning("imagecrop(): Missing width position");
    return false;
  }
  if (rect.exists(s_height)) {
    gdrect.height = rect[s_height].toInt64();
  } else {
    raise_warning("imagecrop(): Missing height position");
    return false;
  }

  imcropped = gdImageCrop(im, &gdrect);

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


void StandardExtension::initVariable() {
  HHVM_FE(is_null);
  HHVM_FE(is_bool);
  HHVM_FE(is_int);
  HHVM_FALIAS(is_integer, is_int);
  HHVM_FALIAS(is_long, is_int);
  HHVM_FE(is_float);
  HHVM_FALIAS(is_double, is_float);
  HHVM_FALIAS(is_real, is_float);
  HHVM_FE(is_numeric);
  HHVM_FE(is_string);
  HHVM_FE(is_scalar);
  HHVM_FE(is_array);
  HHVM_FALIAS(HH\\is_vec, HH_is_vec);
  HHVM_FALIAS(HH\\is_dict, HH_is_dict);
  HHVM_FALIAS(HH\\is_keyset, HH_is_keyset);
  HHVM_FALIAS(HH\\is_varray, HH_is_varray);
  HHVM_FALIAS(HH\\is_darray, HH_is_darray);
  HHVM_FALIAS(HH\\is_vec_or_varray, HH_is_vec_or_varray);
  HHVM_FALIAS(HH\\is_dict_or_darray, HH_is_dict_or_darray);
  HHVM_FALIAS(HH\\is_any_array, HH_is_any_array);
  HHVM_FALIAS(HH\\is_php_array, HH_is_php_array);
  HHVM_FALIAS(HH\\is_list_like, HH_is_list_like);
  HHVM_FALIAS(HH\\is_meth_caller, HH_is_meth_caller);
  HHVM_FE(is_object);
  HHVM_FE(is_resource);
  HHVM_FE(boolval);
  HHVM_FE(intval);
  HHVM_FE(floatval);
  HHVM_FALIAS(doubleval, floatval);
  HHVM_FE(strval);
  HHVM_FE(gettype);
  HHVM_FE(get_resource_type);
  HHVM_FE(print_r);
  HHVM_FE(var_export);
  HHVM_FE(debug_zval_dump);
  HHVM_FE(var_dump);
  HHVM_FE(serialize);
  HHVM_FE(unserialize);
  HHVM_FE(parse_str);
  HHVM_FALIAS(HH\\object_prop_array, HH_object_prop_array);
  HHVM_FALIAS(HH\\serialize_with_options, HH_serialize_with_options);
  HHVM_FALIAS(HH\\Lib\\_Private\\Native\\first, HH_first);
  HHVM_FALIAS(HH\\Lib\\_Private\\Native\\last, HH_last);
  HHVM_FALIAS(HH\\Lib\\_Private\\Native\\first_key, HH_first_key);
  HHVM_FALIAS(HH\\Lib\\_Private\\Native\\last_key, HH_last_key);
  HHVM_FALIAS(HH\\is_late_init_prop_init, HH_is_late_init_prop_init);
  HHVM_FALIAS(HH\\is_late_init_sprop_init, HH_is_late_init_sprop_init);
  HHVM_FALIAS(HH\\global_key_exists, HH_global_key_exists);

  if (RuntimeOption::EnableIntrinsicsExtension) {
    HHVM_FALIAS(__hhvm_intrinsics\\serialize_keep_dvarrays,
                hhvm_intrinsics_serialize_keep_dvarrays);
  }

  loadSystemlib("std_variable");
}


bool HHVM_FUNCTION(imagegammacorrect, const Resource& image,
    double inputgamma, double outputgamma) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  if (inputgamma <= 0.0 || outputgamma <= 0.0) {
    raise_warning("Gamma values should be positive");
    return false;
  }
  if (gdImageTrueColor(im))   {
    int x, y, c;

    for (y = 0; y < gdImageSY(im); y++) {
      for (x = 0; x < gdImageSX(im); x++) {
        c = gdImageGetPixel(im, x, y);
        gdImageSetPixel(im, x, y,
          gdTrueColor((int)((pow((pow((gdTrueColorGetRed(c)/255.0),
                      inputgamma)),1.0/outputgamma)*255) + .5),
                      (int)((pow((pow((gdTrueColorGetGreen(c)/255.0),
                      inputgamma)),1.0/outputgamma) * 255) + .5),
                      (int)((pow((pow((gdTrueColorGetBlue(c)/255.0),
                      inputgamma)),1.0/outputgamma) * 255) + .5)));
      }
    }
    return true;
  }
  for (int i = 0; i < gdImageColorsTotal(im); i++) {
    im->red[i] = (int)((pow((pow((im->red[i]/255.0), inputgamma)),
                        1.0/outputgamma)*255) + .5);
    im->green[i] = (int)((pow((pow((im->green[i]/255.0), inputgamma)),
                          1.0/outputgamma)*255) + .5);
    im->blue[i] = (int)((pow((pow((im->blue[i]/255.0), inputgamma)),
                         1.0/outputgamma)*255) + .5);
  }

  return true;
}


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

  return mcrypt_enc_get_iv_size(pm->m_td);
}