static const char *read_string(const Array& args, const String& key, const char *def,
                               std::vector<String> &strings) {
  if (args.exists(key)) {
    String value = args[key].toString();
    strings.push_back(value);
    return (char*)value.data();
  }
  return def;
}


int64_t HHVM_FUNCTION(strcoll,
                      const String& str1,
                      const String& str2) {
  return strcoll(str1.c_str(), str2.c_str());
}

security fixes

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

/* No explicit global definitions found. */


uint16_t inline PatAndRepl::hash(int start, int len) const {
  assertx(pat.size() >= start + len);
  return strtr_hash(pat.data() + start, len);
};


static struct gfxinfo *php_handle_swf(const req::ptr<File>& stream) {
  struct gfxinfo *result = nullptr;
  long bits;
  unsigned char *a;

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

  String str = stream->read(32);
  if (str.length() != 32) return nullptr;
  a = (unsigned char *)str.c_str();
  result = (struct gfxinfo *)IM_CALLOC(1, sizeof (struct gfxinfo));
  CHECK_ALLOC_R(result, sizeof (struct gfxinfo), nullptr);
  bits = php_swf_get_bits (a, 0, 5);
  result->width = (php_swf_get_bits (a, 5 + bits, bits) -
    php_swf_get_bits (a, 5, bits)) / 20;
  result->height = (php_swf_get_bits (a, 5 + (3 * bits), bits) -
    php_swf_get_bits (a, 5 + (2 * bits), bits)) / 20;
  result->bits = 0;
  result->channels = 0;
  return result;
}

#define IMAGE_TYPE_GIF 1

#define IMAGE_TYPE_JPEG 2

#define IMAGE_TYPE_PNG 4

#define IMAGE_FILTER_NEGATE         0

#define IMAGE_FILTER_GRAYSCALE      1

#define IMAGE_FILTER_COLORIZE       4

#define IMAGE_FILTER_MAX            11

#define IMAGE_FILTER_MAX_ARGS       6

#define IMAGE_TYPE_WBMP 8

#define IMAGE_TYPE_XPM 16

#define IMAGE_FILTER_BRIGHTNESS     2

#define IMAGE_FILTER_CONTRAST       3

#define IMAGE_FILTER_EDGEDETECT     5

#define IMAGE_FILTER_EMBOSS         6

#define IMAGE_FILTER_SELECTIVE_BLUR 8

#define IMAGE_FILTER_SMOOTH         10

#define IMAGE_FILTER_PIXELATE       11

#define IMAGE_FILTER_MEAN_REMOVAL   9

#define IMAGE_FILTER_GAUSSIAN_BLUR  7


bool MemFile::closeImpl() {
  *s_pcloseRet = 0;
  setIsClosed(true);
  if (m_malloced && m_data) {
    free(m_data);
    m_data = nullptr;
  }
  File::closeImpl();
  return true;
}


void Profiler::endFrameEx(const TypedValue* /*retval*/,
                          const char* /*_symbol*/) {}

#define HP_STACK_DELIM        "==>"

#define HP_STACK_DELIM_LEN    (sizeof(HP_STACK_DELIM) - 1)


bool HHVM_FUNCTION(HH_is_late_init_prop_init,
                   const Object& obj,
                   const String& name) {
  auto const ctx = fromCaller(
    [] (const ActRec* fp, Offset) { return fp->func()->cls(); }
  );
  auto const val = obj->getPropIgnoreLateInit(ctx, name.get());
  if (!val) {
    SystemLib::throwInvalidArgumentExceptionObject(
      folly::sformat(
       "Unknown or inaccessible property '{}' on object of class {}",
       name.get(),
       obj->getVMClass()->name()
      )
    );
  }
  return type(val) != KindOfUninit;
}


static void add_assoc_image_info(Array &value, bool sub_array,
                                 image_info_type *image_info,
                                 int section_index) {
  char buffer[64], *val, *name, uname[64];
  int i, ap, l, b, idx=0, unknown=0;
  image_info_value *info_value;
  image_info_data  *info_data;
  Array tmp;
  Array *tmpi = &tmp;
  Array array;

  if (image_info->info_list[section_index].count) {
    if (!sub_array) {
      tmpi = &value;
    }

    for(i=0; i<image_info->info_list[section_index].count; i++) {
      info_data  = &image_info->info_list[section_index].list[i];
      info_value = &info_data->value;
      if (!(name = info_data->name)) {
        snprintf(uname, sizeof(uname), "%d", unknown++);
        name = uname;
      }
      if (info_data->length==0) {
        tmpi->set(String(name, CopyString), uninit_null());
      } else {
        switch (info_data->format) {
        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;
           */
        case TAG_FMT_BYTE:
        case TAG_FMT_SBYTE:
        case TAG_FMT_UNDEFINED:
          if (!info_value->s) {
            tmpi->set(String(name, CopyString), "");
          } else {
            tmpi->set(String(name, CopyString),
                      String(info_value->s, info_data->length, CopyString));
          }
          break;

        case TAG_FMT_STRING:
          if (!(val = info_value->s)) {
            val = "";
          }
          if (section_index==SECTION_COMMENT) {
            tmpi->set(idx++, String(val, CopyString));
          } else {
            tmpi->set(String(name, CopyString), String(val, CopyString));
          }
          break;

        case TAG_FMT_URATIONAL:
        case TAG_FMT_SRATIONAL:
        /*case TAG_FMT_BYTE:
        case TAG_FMT_SBYTE:*/
        case TAG_FMT_USHORT:
        case TAG_FMT_SSHORT:
        case TAG_FMT_SINGLE:
        case TAG_FMT_DOUBLE:
        case TAG_FMT_ULONG:
        case TAG_FMT_SLONG:
          /* now the rest, first see if it becomes an array */
          if ((l = info_data->length) > 1) {
            array.clear();
          }
          for(ap=0; ap<l; ap++) {
            if (l>1) {
              info_value = &info_data->value.list[ap];
            }
            switch (info_data->format) {
            case TAG_FMT_BYTE:
              if (l>1) {
                info_value = &info_data->value;
                for (b=0;b<l;b++) {
                  array.set(b, (int)(info_value->s[b]));
                }
                break;
              }
            case TAG_FMT_USHORT:
            case TAG_FMT_ULONG:
              if (l==1) {
                tmpi->set(String(name, CopyString), (int)info_value->u);
              } else {
                array.set(ap, (int)info_value->u);
              }
              break;

            case TAG_FMT_URATIONAL:
              snprintf(buffer, sizeof(buffer), "%u/%u",
                       info_value->ur.num, info_value->ur.den);
              if (l==1) {
                tmpi->set(String(name, CopyString),
                          String(buffer, CopyString));
              } else {
                array.set(ap, String(buffer, CopyString));
              }
              break;

            case TAG_FMT_SBYTE:
              if (l>1) {
                info_value = &info_data->value;
                for (b=0;b<l;b++) {
                  array.set(ap, (int)info_value->s[b]);
                }
                break;
              }
            case TAG_FMT_SSHORT:
            case TAG_FMT_SLONG:
              if (l==1) {
                tmpi->set(String(name, CopyString), info_value->i);
              } else {
                array.set(ap, info_value->i);
              }
              break;

            case TAG_FMT_SRATIONAL:
              snprintf(buffer, sizeof(buffer), "%i/%i",
                       info_value->sr.num, info_value->sr.den);
              if (l==1) {
                tmpi->set(String(name, CopyString),
                          String(buffer, CopyString));
              } else {
                array.set(ap, String(buffer, CopyString));
              }
              break;

            case TAG_FMT_SINGLE:
              if (l==1) {
                tmpi->set(String(name, CopyString), info_value->f);
              } else {
                array.set(ap, info_value->f);
              }
              break;

            case TAG_FMT_DOUBLE:
              if (l==1) {
                tmpi->set(String(name, CopyString), info_value->d);
              } else {
                array.set(ap, info_value->d);
              }
              break;
            }
            info_value = &info_data->value.list[ap];
          }
          if (l>1) {
            tmpi->set(String(name, CopyString), array);
          }
          break;
        }
      }
    }
    if (sub_array) {
      value.set(exif_get_sectionname(section_index), tmp);
    }
  }
}


String ipaddr_convert(struct sockaddr *addr, int addrlen) {
  char buffer[NI_MAXHOST];
  int error = getnameinfo(addr, addrlen, buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST);

  if (error) {
    raise_warning("%s", gai_strerror(error));
    return empty_string();
  }
  return String(buffer, CopyString);
}

#define PHP_NORMAL_READ 0x0001

#define PHP_BINARY_READ 0x0002


/* implicit */ operator const String&() const { return asString(); }

String(const StringData*) = delete;

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

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

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

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

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

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

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

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

static const int npos = -1;

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

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

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

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

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

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

friend String&& operator

friend String

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

extern const String null_string;


void php_ifd_set16u(char *data, unsigned int value, int motorola_intel) {
  if (motorola_intel) {
    data[0] = (value & 0xFF00) >> 8;
    data[1] = (value & 0x00FF);
  } else {
    data[1] = (value & 0xFF00) >> 8;
    data[0] = (value & 0x00FF);
  }
}


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

  pm->close();
  return true;
}


static void exif_iif_add_int(image_info_type *image_info, int section_index,
                             char *name, int value) {
  image_info_data *info_data;
  image_info_data *list;
  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 = (unsigned short)TAG_NONE;
  info_data->format = TAG_FMT_SLONG;
  info_data->length = 1;
  PHP_STRDUP(info_data->name, name);
  info_data->value.i = value;
  image_info->sections_found |= 1<<section_index;
  image_info->info_list[section_index].count++;
}


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


pcre_get_compiled_regex_cache(PCRECache::Accessor& accessor,
                              const StringData* regex) {
  PCRECache::TempKeyCache tkc;

  /* Try to lookup the cached regex entry, and if successful, just pass
     back the compiled pattern, otherwise go on and compile it. */
  if (s_pcreCache.find(accessor, regex, tkc)) {
    return true;
  }

  /* Parse through the leading whitespace, and display a warning if we
     get to the end without encountering a delimiter. */
  const char *p = regex->data();
  while (isspace((int)*(unsigned char *)p)) p++;
  if (*p == 0) {
    raise_warning("Empty regular expression");
    return false;
  }

  /* Get the delimiter and display a warning if it is alphanumeric
     or a backslash. */
  char delimiter = *p++;
  if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {
    raise_warning("Delimiter must not be alphanumeric or backslash");
    return false;
  }

  char start_delimiter = delimiter;
  const char *pp = strchr("([{< )]}> )]}>", delimiter);
  if (pp) {
    delimiter = pp[5];
  }
  char end_delimiter = delimiter;

  if (start_delimiter == end_delimiter) {
    /* We need to iterate through the pattern, searching for the ending
     * delimiter, but skipping the backslashed delimiters. If the ending
     * delimiter is not found, display a warning. */
    pp = p;
    while (*pp != 0) {
      if (*pp == '\\' && pp[1] != 0) pp++;
      else if (*pp == delimiter)
        break;
      pp++;
    }
    if (*pp == 0) {
      raise_warning("No ending delimiter '%c' found: [%s]", delimiter,
                      regex->data());
      return false;
    }
  } else {
    /* We iterate through the pattern, searching for the matching ending
     * delimiter. For each matching starting delimiter, we increment nesting
     * level, and decrement it for each matching ending delimiter. If we
     * reach the end of the pattern without matching, display a warning.
     */
    int brackets = 1; // brackets nesting level
    pp = p;
    while (*pp != 0) {
      if (*pp == '\\' && pp[1] != 0) pp++;
      else if (*pp == end_delimiter && --brackets <= 0)
        break;
      else if (*pp == start_delimiter)
        brackets++;
      pp++;
    }
    if (*pp == 0) {
      raise_warning("No ending matching delimiter '%c' found: [%s]",
                      end_delimiter, regex->data());
      return false;
    }
  }

  /* Make a copy of the actual pattern. */
  String spattern(p, pp-p, CopyString);
  const char *pattern = spattern.data();

  /* Move on to the options */
  pp++;

  /* Parse through the options, setting appropriate flags.  Display
     a warning if we encounter an unknown modifier. */
  int coptions = 0;
  int poptions = 0;
  bool do_study = false;
  while (*pp != 0) {
    switch (*pp++) {
      /* Perl compatible options */
    case 'i':  coptions |= PCRE_CASELESS;       break;
    case 'm':  coptions |= PCRE_MULTILINE;      break;
    case 's':  coptions |= PCRE_DOTALL;         break;
    case 'x':  coptions |= PCRE_EXTENDED;       break;

      /* PCRE specific options */
    case 'A':  coptions |= PCRE_ANCHORED;       break;
    case 'D':  coptions |= PCRE_DOLLAR_ENDONLY; break;
    case 'S':  do_study = true;                 break;
    case 'U':  coptions |= PCRE_UNGREEDY;       break;
    case 'X':  coptions |= PCRE_EXTRA;          break;
    case 'u':  coptions |= PCRE_UTF8;
  /* In  PCRE,  by  default, \d, \D, \s, \S, \w, and \W recognize only ASCII
       characters, even in UTF-8 mode. However, this can be changed by setting
       the PCRE_UCP option. */
#ifdef PCRE_UCP
            coptions |= PCRE_UCP;
#endif
      break;

      /* Custom preg options */
    case 'e':  poptions |= PREG_REPLACE_EVAL;   break;

    case ' ':
    case '\n':
    case '\r':
      break;

    default:
      raise_warning("Unknown modifier '%c': [%s]", pp[-1], regex->data());
      return false;
    }
  }

  /* We've reached a null byte, now check if we're actually at the end of the
     string.  If not this is a bad expression, and a potential security hole. */
  if (regex->size() != (pp - regex->data())) {
    raise_error("Error: Null byte found in pattern");
  }

  /* Compile pattern and display a warning if compilation failed. */
  const char  *error;
  int erroffset;
  pcre *re = pcre_compile(pattern, coptions, &error, &erroffset, 0);
  if (re == nullptr) {
    raise_warning("Compilation failed: %s at offset %d", error, erroffset);
    return false;
  }

  // Careful: from here 're' needs to be freed if something throws.

  // TODO(t14969501): enable literal_data everywhere and skip the
  // pcre_compile above.
  auto const literal_data = pcre_literal_data(pattern, coptions);

  /* If study option was specified, study the pattern and
     store the result in extra for passing to pcre_exec. */
  pcre_extra *extra = nullptr;
  if (!literal_data.isLiteral()) {
    if (do_study || PCRE_STUDY_JIT_COMPILE) {
      int soptions = PCRE_STUDY_JIT_COMPILE;
      extra = pcre_study(re, soptions, &error);
      if (extra) {
        extra->flags |= PCRE_EXTRA_MATCH_LIMIT |
          PCRE_EXTRA_MATCH_LIMIT_RECURSION;
        pcre_assign_jit_stack(extra, alloc_jit_stack, nullptr);
      }
      if (error != nullptr) {
        try {
          raise_warning("Error while studying pattern");
        } catch (...) {
          pcre_free(re);
          throw;
        }
      }
      if ((!RuntimeOption::EvalJitNoGdb ||
           RuntimeOption::EvalJitUseVtuneAPI ||
           RuntimeOption::EvalPerfPidMap) &&
          extra &&
          extra->executable_jit != nullptr) {
        size_t size;
        pcre_fullinfo(re, extra, PCRE_INFO_JITSIZE, &size);

        TCA start = *(TCA *)(extra->executable_jit);
        TCA end = start + size;
        std::string name = folly::sformat("HHVM::pcre_jit::{}", pattern);

        if (!RuntimeOption::EvalJitNoGdb && jit::mcgen::initialized()) {
          Debug::DebugInfo::Get()->recordStub(Debug::TCRange(start, end, false),
                                              name);
        }
        if (RuntimeOption::EvalJitUseVtuneAPI) {
          HPHP::jit::reportHelperToVtune(name.c_str(), start, end);
        }
        if (RuntimeOption::EvalPerfPidMap && jit::mcgen::initialized()) {
          std::string escaped_name;
          folly::json::escapeString(name, escaped_name,
                                    folly::json::serialization_opts());
          Debug::DebugInfo::Get()->recordPerfMap(
            Debug::TCRange(start, end, false),
            SrcKey{}, escaped_name
          );
        }
      }
    }
  }

  /* Store the compiled pattern and extra info in the cache. */
  pcre_cache_entry* new_entry = new pcre_cache_entry();
  new_entry->re = re;
  new_entry->extra = extra;
  if (literal_data.isLiteral()) {
    new_entry->literal_data =
      std::make_unique<pcre_literal_data>(std::move(literal_data));
  }

  assertx((poptions & ~0x1) == 0);
  new_entry->preg_options = poptions;

  assertx((coptions & 0x80000000) == 0);
  new_entry->compile_options = coptions;

  /* Get pcre full info */
  if (!get_pcre_fullinfo(new_entry)) {
    delete new_entry;
    return false;
  }

  s_pcreCache.insert(accessor, regex, tkc, new_entry);
  return true;
}


void beginFrameEx(const char* /*symbol*/) override { sample_check(); }


  bool isInvalid() const override {
    return m_td == MCRYPT_FAILED;
  }


  String rvalImpl(int key) const {
    if (m_str) {
      return String{m_str->getChar(key)};
    }
    return String();
  }


void pcre_dump_cache(folly::File& file) {
  s_pcreCache.dump(file);
}


HHVM_FUNCTION(imagerotate, const Resource& source_image, double angle,
              int64_t bgd_color, int64_t /*ignore_transparent*/ /* = 0 */) {
  gdImagePtr im_src = get_valid_image_resource(source_image);
  if (!im_src) return false;
  gdImagePtr im_dst = gdImageRotateInterpolated(im_src, angle, bgd_color);
  if (!im_dst) return false;
  return Variant(req::make<Image>(im_dst));
}


get_allocs()
{
#ifdef USE_JEMALLOC
  return tl_heap->getAllocated();
#endif
#ifdef USE_TCMALLOC
  if (MallocExtensionInstance) {
    size_t stat;
    MallocExtensionInstance()->GetNumericProperty(
           "generic.thread_bytes_allocated", &stat);
    return stat;
  }
#endif
  return 0;
}


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

  int max_key_size = mcrypt_enc_get_key_size(pm->m_td);
  int iv_size = mcrypt_enc_get_iv_size(pm->m_td);

  if (key.empty()) {
    raise_warning("Key size is 0");
  }

  unsigned char *key_s = (unsigned char *)malloc(key.size());
  memset(key_s, 0, key.size());

  unsigned char *iv_s = (unsigned char *)malloc(iv_size + 1);
  memset(iv_s, 0, iv_size + 1);

  int key_size;
  if (key.size() > max_key_size) {
    raise_warning("Key size too large; supplied length: %ld, max: %d",
                    key.size(), max_key_size);
    key_size = max_key_size;
  } else {
    key_size = key.size();
  }
  memcpy(key_s, key.data(), key.size());

  if (iv.size() != iv_size) {
    raise_warning("Iv size incorrect; supplied length: %ld, needed: %d",
                    iv.size(), iv_size);
  }
  memcpy(iv_s, iv.data(), std::min<int64_t>(iv_size, iv.size()));

  mcrypt_generic_deinit(pm->m_td);
  int result = mcrypt_generic_init(pm->m_td, key_s, key_size, iv_s);

  /* If this function fails, close the mcrypt module to prevent crashes
   * when further functions want to access this resource */
  if (result < 0) {
    pm->close();
    switch (result) {
    case -3:
      raise_warning("Key length incorrect");
      break;
    case -4:
      raise_warning("Memory allocation error");
      break;
    case -1:
    default:
      raise_warning("Unknown error");
      break;
    }
  } else {
    pm->m_init = true;
  }

  free(iv_s);
  free(key_s);
  return result;
}


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

    *egdsocket = 0;
    *seeded = 0;
    if (file == nullptr) {
      file = RAND_file_name(buffer, sizeof(buffer));
#if !defined(OPENSSL_NO_RAND_EGD) && !defined(OPENSSL_NO_EGD)
    } else if (RAND_egd(file) > 0) {
      /* if the given filename is an EGD socket, don't
       * write anything back to it */
      *egdsocket = 1;
      return true;
#endif
    }

    if (file == nullptr || !RAND_load_file(file, -1)) {
      if (RAND_status() == 0) {
        raise_warning("unable to load random state; not enough data!");
        return false;
      }
      return false;
    }
    *seeded = 1;
    return true;
  }

const int64_t k_OPENSSL_RAW_DATA = 1;

const int64_t k_OPENSSL_ZERO_PADDING = 2;

const int64_t k_OPENSSL_NO_PADDING = 3;

const int64_t k_OPENSSL_PKCS1_OAEP_PADDING = 4;

const int64_t k_OPENSSL_SSLV23_PADDING = 2;

const int64_t k_OPENSSL_PKCS1_PADDING = 1;

static char default_ssl_conf_filename[PATH_MAX];

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

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

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

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

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

  ~OpenSSLInitializer() {
    EVP_cleanup();
  }
}

static OpenSSLInitializer s_openssl_initializer;


char *string_bin2hex(const char *input, int &len) {
  if (!len) return nullptr;
  int inLen = len;
  int outLen = inLen * 2;
  char* result = (char*)malloc(outLen + 1);
  len = outLen;
  return string_bin2hex(input, inLen, result);
}


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

String(const StringData*) = delete;

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

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

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

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

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

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

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

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

static const int npos = -1;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

extern const String null_string;


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


  void requestInit() override {
    str.reset();
    pos = 0;
    memset(&mask, 0, sizeof(mask));
  }


TypedValue HHVM_FUNCTION(strchr,
                         const String& haystack,
                         const Variant& needle) {
  return HHVM_FN(strstr)(haystack, needle);
}


Variant HHVM_FUNCTION(exif_tagname, int64_t index) {
  char *szTemp;

  szTemp = exif_get_tagname(index, nullptr, 0, tag_table_IFD);
  if (index <0 || !szTemp || !szTemp[0]) {
    return false;
  } else {
    return String(szTemp, CopyString);
  }
}


  folly::StringPiece slice() const {
    return m_str ? m_str->slice() : folly::StringPiece();
  }


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


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

  return mcrypt_enc_get_key_size(pm->m_td);
}


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

  if (!stream->seek(3, SEEK_CUR)) return nullptr;
  String dim = stream->read(5);
  if (dim.length() != 5) 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] | (((unsigned int)s[1])<<8);
  result->height = (unsigned int)s[2] | (((unsigned int)s[3])<<8);
  result->bits = s[4]&0x80 ? ((((unsigned int)s[4])&0x07) + 1) : 0;
  result->channels = 3; /* always */
  return result;
}


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


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


void Strobelight::shutdown() {
  RuntimeOption::StrobelightEnabled = false;
}


static bool php_imagechar(const Resource& image, int size, int x, int y,
                          const String& c, int color, int mode) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  int ch = 0;
  gdFontPtr font;

  if (mode < 2) {
    ch = (int)((unsigned char)(c.charAt(0)));
  }

  font = php_find_gd_font(size);

  switch (mode) {
  case 0:
    gdImageChar(im, font, x, y, ch, color);
    break;
  case 1:
    php_gdimagecharup(im, font, x, y, ch, color);
    break;
  case 2:
    for (int i = 0; (i < c.length()); i++) {
      gdImageChar(im, font, x, y, (int)((unsigned char)c.charAt(i)), color);
      x += font->w;
    }
    break;
  case 3:
    for (int i = 0; (i < c.length()); i++) {
      gdImageCharUp(im, font, x, y, (int)c.charAt(i), color);
      y -= font->w;
    }
    break;
  }
  return true;
}


php_open_plain_file(const String& filename, const char *mode, FILE **fpp) {
  auto file = File::Open(filename, mode);
  auto plain_file = dyn_cast_or_null<PlainFile>(file);
  if (!plain_file) return nullptr;
  if (FILE* fp = plain_file->getStream()) {
  if (fpp) *fpp = fp;
    return file;
  }
  file->close();
  return nullptr;
}


Variant strpbrk_impl(const String& haystack, const String& char_list) {
  if (char_list.empty()) {
    raise_invalid_argument_warning("char_list: (empty)");
    return false;
  }
  if (haystack.empty()) {
    return false;
  }

  auto charListData = char_list.c_str();

  // equivalent to rawmemchr(charListData, '\0') ... charListData must be
  // null-terminated
  auto firstNull = charListData;
  while (*firstNull != '\0') { ++firstNull; }

  auto const hasNullByte = (firstNull - charListData) < char_list.size();

  if (UNLIKELY(hasNullByte)) {
    if ((firstNull - charListData) == (char_list.size() - 1)) {
      // the first null is the last character in char_list
    } else if (firstNull == charListData) {
      // the first null is the first character in char_list
      auto secondNull = firstNull + 1;
      while (*secondNull != '\0') { ++secondNull; }

      if ((secondNull - charListData) != char_list.size()) {
        return
          strpbrk_char_list_has_nulls_slow<existence_only>(haystack, char_list);
      }
      ++charListData; // we can remember the null byte
    } else {
      return
        strpbrk_char_list_has_nulls_slow<existence_only>(haystack, char_list);
    }
  }

  // Use strcspn instead of strpbrk because the latter doesn't report when
  // it's terminated due to a null byte in haystack in any manageable way.
  auto haySize = haystack.size();
  auto hayData = haystack.c_str();
retry:
  size_t idx = strcspn(hayData, charListData);
  if (idx < haySize) {
    if (UNLIKELY(hayData[idx] == '\0' && !hasNullByte)) {
      hayData += idx + 1;
      haySize -= idx + 1;
      goto retry;
    }
    if (existence_only) { return true; }

    return String(hayData + idx, haySize - idx, CopyString);
  }
  return false;
}


static inline uint16_t strtr_hash(const char *str, int len) {
    uint16_t  res = 0;
    for (int i = 0; i < len; i++) {
        res = res * 33 + (unsigned char)str[i];
    }

    return res;
}


Variant HHVM_FUNCTION(socket_create_listen,
                      int port,
                      int backlog /* = 128 */) {
  HostEnt result;
  if (!safe_gethostbyname("0.0.0.0", result)) {
    return false;
  }

  struct sockaddr_in la;
  memcpy((char *) &la.sin_addr, result.hostbuf.h_addr,
         result.hostbuf.h_length);
  la.sin_family = result.hostbuf.h_addrtype;
  la.sin_port = htons((unsigned short)port);

  auto sock = req::make<ConcreteSocket>(
    socket(PF_INET, SOCK_STREAM, 0), PF_INET, "0.0.0.0", port);

  if (!sock->valid()) {
    SOCKET_ERROR(sock, "unable to create listening socket", errno);
    return false;
  }

  if (::bind(sock->fd(), (struct sockaddr *)&la, sizeof(la)) < 0) {
    SOCKET_ERROR(sock, "unable to bind to given address", errno);
    return false;
  }

  if (listen(sock->fd(), backlog) < 0) {
    SOCKET_ERROR(sock, "unable to listen on socket", errno);
    return false;
  }

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


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

  return mcrypt_enc_is_block_mode(pm->m_td) == 1;
}


inline String& String::operator=(const StaticString& v) {
  m_str = req::ptr<StringData>::attach(v.m_str.get());
  return *this;
}


inline String::String(const StaticString& str) :
  m_str(str.m_str.get(), NoIncRef{}) {
  assertx(str.m_str->isStatic());
}


String HHVM_FUNCTION(str_repeat,
                     const String& input,
                     int64_t multiplier) {
  if (input.empty()) {
    return input;
  }

  if (multiplier < 0) {
    SystemLib::throwRuntimeExceptionObject(
      "Second argument has to be greater than or equal to 0");
  }

  if (multiplier == 0) {
    return empty_string();
  }

  if (input.size() == 1) {
    String ret(multiplier, ReserveString);

    memset(ret.mutableData(), *input.data(), multiplier);
    ret.setSize(multiplier);
    return ret;
  }

  auto size = multiplier * size_t(input.size());
  if (multiplier >= StringData::MaxSize || size > StringData::MaxSize) {
    raiseStringLengthExceededError(size);
  }

  StringBuffer ret(input.size() * multiplier);

  while (multiplier--) {
    ret.append(input);
  }

  return ret.detach();
}


TypedValue HHVM_FUNCTION(substr_replace,
                         const Variant& str, const Variant& replacement,
                         const Variant& start, const Variant& length) {
  return tvReturn(substr_replace(str, replacement, start, length));
}


int vspprintf(char** pbuf, size_t /*max_len*/, const char* format, ...) {
  int len;
  va_list ap;
  va_start(ap, format);
  len = xbuf_format_converter(pbuf, format, ap);
  va_end(ap);
  return len;
}

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

#define INT_NULL        ((int *)0)

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


inline StringData* StringData::Make(const StringData* s1, const char* lit2) {
  return Make(s1->slice(), lit2);
}

inline folly::StringPiece

inline folly::MutableStringPiece

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

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

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

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

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

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


int64_t HHVM_FUNCTION(imagetypes) {
  int ret=0;
  ret = IMAGE_TYPE_GIF;
#ifdef HAVE_GD_JPG
  ret |= IMAGE_TYPE_JPEG;
#endif
#ifdef HAVE_GD_PNG
  ret |= IMAGE_TYPE_PNG;
#endif
  ret |= IMAGE_TYPE_WBMP;
#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED)
  ret |= IMAGE_TYPE_XPM;
#endif
  return ret;
}

#define IMAGE_TYPE_GIF 1

#define IMAGE_TYPE_JPEG 2

#define IMAGE_TYPE_PNG 4

#define IMAGE_TYPE_WBMP 8

#define IMAGE_TYPE_XPM 16


  bool operator() (const PatAndRepl &a, const PatAndRepl &b) {
    uint16_t  hash_a = a.hash(m - B, B) & HASH_TAB_MASK,
              hash_b = b.hash(m - B, B) & HASH_TAB_MASK;

    if (hash_a > hash_b) {
      return false;
    }
    if (hash_a < hash_b) {
      return true;
    }
    // longer patterns must be sorted first
    if (a.getPat().size() > b.getPat().size()) {
      return true;
    }
    if (a.getPat().size() < b.getPat().size()) {
      return false;
    }
    return false;
  }


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


bool HHVM_FUNCTION(imagefilledarc, const Resource& image,
    int64_t cx, int64_t cy, int64_t width, int64_t height,
    int64_t start, int64_t end, int64_t color, int64_t style) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  if (end < 0) end %= 360;
  if (start < 0) start %= 360;
  gdImageFilledArc(im, cx, cy, width, height, start, end, color, style);
  return true;
}


inline void StringData::setSize(int64_t len) {
  assertx(!isImmutable() && !hasMultipleRefs());
  assertx(len >= 0 && len <= capacity());
  mutableData()[len] = 0;
  m_lenAndHash = len;
  assertx(m_hash == 0);
  assertx(checkSane());
}


php_image_filter_smooth(gdImagePtr im, int arg1 /* = 0 */,
                        int /*arg2*/ /* = 0 */, int /*arg3*/ /* = 0 */,
                        int /*arg4*/ /* = 0 */) {
  int weight = arg1;
  return gdImageSmooth(im, weight) == 1;
}


Array HHVM_FUNCTION(mcrypt_module_get_supported_key_sizes,
                    const String& algorithm,
                    const String& lib_dir /* = null_string */) {
  String dir = lib_dir.empty() ? String(MCG(algorithms_dir)) : lib_dir;

  int count = 0;
  int *key_sizes = mcrypt_module_get_algo_supported_key_sizes
    ((char*)algorithm.data(), (char*)dir.data(), &count);

  VArrayInit ret(count);
  for (int i = 0; i < count; i++) {
    ret.append(key_sizes[i]);
  }
  mcrypt_free(key_sizes);
  return ret.toArray();
}


  void endAllFrames() override {
    // Nothing to do for this profiler since all work is done as we go.
  }


  strtr_compare_hash_suffix(size_t m, int B)
  : m(m), B(B) { }


bool HHVM_FUNCTION(imagestring, 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, 2);
}


String HHVM_FUNCTION(addcslashes,
                     const String& str,
                     const String& charlist) {
  if (str.empty() || (!charlist.isNull() && charlist.empty())) {
    return str;
  }

  int masklen = charlist.isNull() ? 10 : charlist.size();
  const char* list = charlist.isNull() ? "\\\x00\x01..\x1f\x7f..\xff"
                                       : charlist.c_str();

  char flags[256];
  string_charmask(list, masklen, flags);

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

      if (flags[c]) {
        ret.append('\\');
        if ((c < 32) || (c > 126)) {
          switch (c) {
            case '\n': ret.append('n'); break;
            case '\t': ret.append('t'); break;
            case '\r': ret.append('r'); break;
            case '\a': ret.append('a'); break;
            case '\v': ret.append('v'); break;
            case '\b': ret.append('b'); break;
            case '\f': ret.append('f'); break;
            default:
              ret.append((char)('0' + (c / 64)));
              c %= 64;
              ret.append((char)('0' + (c / 8)));
              c %= 8;
              ret.append((char)('0' + c));
          }
          return;
        }
      }
      ret.append((char)c);
    });
}


Variant HHVM_FUNCTION(exif_imagetype, const String& filename) {
  auto stream = File::Open(filename, "rb");
  if (!stream) {
    return false;
  }
  int itype = php_getimagetype(stream);
  stream->close();
  if (itype == IMAGE_FILETYPE_UNKNOWN) return false;
  return itype;
}


int vspprintf_ap(char** pbuf, size_t /*max_len*/, const char* format,
                 va_list ap) {
  int len;
  len = xbuf_format_converter(pbuf, format, ap);
  return len;
}


php_image_filter_brightness(gdImagePtr im, int arg1 /* = 0 */,
                            int /*arg2*/ /* = 0 */, int /*arg3*/ /* = 0 */,
                            int /*arg4*/ /* = 0 */) {
  int brightness = arg1;
  return gdImageBrightness(im, brightness) == 1;
}


  static String FromChar(char ch) {
    return String{makeStaticString(ch)};
  }

String(const StringData*) = delete;

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

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

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

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

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

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

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

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

static const int npos = -1;

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

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

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

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

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

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

friend String&& operator

friend String

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

extern const String null_string;


Variant HHVM_FUNCTION(imagecolortransparent, const Resource& image,
                                             int64_t color /* = -1 */) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  if (color != -1) {
    // has color argument
    gdImageColorTransparent(im, color);
  }
  return gdImageGetTransparent(im);
}


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

  if (ImageInfo->file.count) {
    for (i=0; i<ImageInfo->file.count; i++) {
       if (ImageInfo->file.list[i].data) {
         IM_FREE(ImageInfo->file.list[i].data);
       }
    }
  }
  if (ImageInfo->file.list) IM_FREE(ImageInfo->file.list);
  ImageInfo->file.count = 0;
  return 1;
}


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


String HHVM_FUNCTION(chr, const Variant& ascii) {
  switch (ascii.getType()) {
    case KindOfBoolean:
    case KindOfInt64:
    case KindOfDouble:
      return String::FromChar(ascii.toInt64() & 0xFF);
    case KindOfPersistentString:
    case KindOfString:
      return String::FromChar(
        ascii.toString().isNumeric() ? ascii.toInt64() & 0xFF : 0);
    case KindOfNull:
    case KindOfUninit:
    case KindOfPersistentVec:
    case KindOfPersistentDict:
    case KindOfPersistentKeyset:
    case KindOfPersistentDArray:
    case KindOfPersistentVArray:
    case KindOfVec:
    case KindOfDict:
    case KindOfKeyset:
    case KindOfDArray:
    case KindOfVArray:
    case KindOfObject:
    case KindOfResource:
    case KindOfRFunc:
    case KindOfFunc:
    case KindOfClass:
    case KindOfLazyClass:
    case KindOfClsMeth:
    case KindOfRClsMeth:
    case KindOfRecord:
      return String::FromChar(0);
  }

  not_reached();
}


String HHVM_FUNCTION(lcfirst,
                     const String& str) {
  return stringToCaseFirst(str, tolower, islower);
}


inline strhash_t StringData::hashStatic() const {
  assertx(isStatic());
  const strhash_t h = m_hash & STRHASH_MASK;
  assertx(h);
  return h;
}


Variant HHVM_FUNCTION(setlocale,
                      int category,
                      const Variant& locale,
                      const Array& _argv /* = null_array */) {
  Array argv = _argv;
  if (locale.isArray()) {
    if (!argv.empty()) raise_invalid_argument_warning("locale: not string)");
    argv = locale; // ignore _argv
  }

  for (int i = -1; i < argv.size(); i++) {
    String slocale;
    if (i == -1) {
      if (locale.isArray()) continue;
      slocale = locale.toString();
    } else {
      slocale = argv[i].toString();
    }

    const char *loc = slocale.c_str();
    if (slocale.size() >= 255) {
      raise_invalid_argument_warning("locale name is too long: %s", loc);
      return false;
    }
    if (strcmp("0", loc) == 0) {
      loc = nullptr;
    }
    {
      Lock lock(s_mutex);
      const char *retval = setlocale(category, loc);
      if (retval) {
        return String(retval, CopyString);
      }
    }
  }
  return false;
}


  static void BindToCPU(uint32_t cpu_id) {
    cpu_set_t new_mask;
    CPU_ZERO(&new_mask);
    CPU_SET(cpu_id, &new_mask);
    SET_AFFINITY(0, sizeof(cpu_set_t), &new_mask);
  }

#define HP_STACK_DELIM_LEN    (sizeof(HP_STACK_DELIM) - 1)


  void moduleInit() override {
    setlocale(LC_CTYPE, "");

    HHVM_FE(addcslashes);
    HHVM_FE(stripcslashes);
    HHVM_FE(addslashes);
    HHVM_FE(stripslashes);
    HHVM_FE(bin2hex);
    HHVM_FE(hex2bin);
    HHVM_FE(nl2br);
    HHVM_FE(quotemeta);
    HHVM_FE(str_shuffle);
    HHVM_FE(strrev);
    HHVM_FE(strtolower);
    HHVM_FE(strtoupper);
    HHVM_FE(ucfirst);
    HHVM_FE(lcfirst);
    HHVM_FE(ucwords);
    HHVM_FE(strip_tags);
    HHVM_FE(trim);
    HHVM_FE(ltrim);
    HHVM_FE(rtrim);
    HHVM_FE(chop);
    HHVM_FE(explode);
    HHVM_FE(implode);
    HHVM_FALIAS(join, implode);
    HHVM_FE(str_split);
    HHVM_FE(chunk_split);
    HHVM_FE(strtok);
    HHVM_FE(str_replace);
    HHVM_FE(str_replace_with_count);
    HHVM_FE(str_ireplace);
    HHVM_FE(str_ireplace_with_count);
    HHVM_FE(substr_replace);
    HHVM_FE(substr);
    HHVM_FE(str_pad);
    HHVM_FE(str_repeat);
    HHVM_FE(html_entity_decode);
    HHVM_FE(htmlentities);
    HHVM_FE(htmlspecialchars_decode);
    HHVM_FE(htmlspecialchars);
    HHVM_FE(fb_htmlspecialchars);
    HHVM_FE(quoted_printable_encode);
    HHVM_FE(quoted_printable_decode);
    HHVM_FE(convert_uudecode);
    HHVM_FE(convert_uuencode);
    HHVM_FE(str_rot13);
    HHVM_FE(crc32);
    HHVM_FE(crypt);
    HHVM_FE(md5);
    HHVM_FE(sha1);
    HHVM_FE(strtr);
    HHVM_FE(convert_cyr_string);
    HHVM_FE(get_html_translation_table);
    HHVM_FE(hebrev);
    HHVM_FE(hebrevc);
    HHVM_FE(setlocale);
    HHVM_FE(localeconv);
    HHVM_FE(nl_langinfo);
    HHVM_FE(sscanf);
    HHVM_FE(chr);
    HHVM_FE(ord);
    HHVM_FE(money_format);
    HHVM_FE(number_format);
    HHVM_FE(strcmp);
    HHVM_FE(strncmp);
    HHVM_FE(strnatcmp);
    HHVM_FE(strcasecmp);
    HHVM_FE(strncasecmp);
    HHVM_FE(strnatcasecmp);
    HHVM_FE(strcoll);
    HHVM_FE(substr_compare);
    HHVM_FE(strchr);
    HHVM_FE(strrchr);
    HHVM_FE(strstr);
    HHVM_FE(stristr);
    HHVM_FE(strpbrk);
    HHVM_FE(strpos);
    HHVM_FE(stripos);
    HHVM_FE(strrpos);
    HHVM_FE(strripos);
    HHVM_FE(substr_count);
    HHVM_FE(strspn);
    HHVM_FE(strcspn);
    HHVM_FE(strlen);
    HHVM_FE(str_getcsv);
    HHVM_FE(count_chars);
    HHVM_FE(str_word_count);
    HHVM_FE(levenshtein);
    HHVM_FE(similar_text);
    HHVM_FE(soundex);
    HHVM_FE(metaphone);

    HHVM_RC_INT(ENT_COMPAT, k_ENT_HTML_QUOTE_DOUBLE);
    HHVM_RC_INT(ENT_NOQUOTES, k_ENT_HTML_QUOTE_NONE);
    HHVM_RC_INT(ENT_QUOTES, k_ENT_QUOTES);
    HHVM_RC_INT(ENT_IGNORE, k_ENT_HTML_IGNORE_ERRORS);
    HHVM_RC_INT(ENT_SUBSTITUTE, k_ENT_HTML_SUBSTITUTE_ERRORS);
    HHVM_RC_INT(ENT_HTML401, k_ENT_HTML_DOC_HTML401);
    HHVM_RC_INT(ENT_XML1, k_ENT_HTML_DOC_XML1);
    HHVM_RC_INT(ENT_XHTML, k_ENT_HTML_DOC_XHTML);
    HHVM_RC_INT(ENT_HTML5, k_ENT_HTML_DOC_HTML5);
    HHVM_RC_INT(ENT_FB_UTF8, k_ENT_FB_UTF8);
    HHVM_RC_INT(ENT_FB_UTF8_ONLY, k_ENT_FB_UTF8_ONLY);

    HHVM_RC_INT(HTML_SPECIALCHARS, k_HTML_SPECIALCHARS);
    HHVM_RC_INT(HTML_ENTITIES, k_HTML_ENTITIES);

    HHVM_RC_INT(STR_PAD_LEFT, k_STR_PAD_LEFT);
    HHVM_RC_INT(STR_PAD_RIGHT, k_STR_PAD_RIGHT);
    HHVM_RC_INT(STR_PAD_BOTH, k_STR_PAD_BOTH);

    HHVM_RC_INT_SAME(LC_CTYPE);
    HHVM_RC_INT_SAME(LC_NUMERIC);
    HHVM_RC_INT_SAME(LC_TIME);
    HHVM_RC_INT_SAME(LC_COLLATE);
    HHVM_RC_INT_SAME(LC_MONETARY);
    HHVM_RC_INT_SAME(LC_ALL);
#ifdef LC_MESSAGES
    HHVM_RC_INT_SAME(LC_MESSAGES);
#endif

#ifdef YESEXPR
    HHVM_RC_INT_SAME(YESEXPR);
#endif
#ifdef NOEXPR
    HHVM_RC_INT_SAME(NOEXPR);
#endif
    HHVM_RC_INT(CHAR_MAX, std::numeric_limits<char>::max());

    HHVM_RC_STR(HPHP_TRIM_CHARLIST, k_HPHP_TRIM_CHARLIST);

#ifdef ABDAY_1
    HHVM_RC_INT_SAME(ABDAY_1);
    HHVM_RC_INT_SAME(ABDAY_2);
    HHVM_RC_INT_SAME(ABDAY_3);
    HHVM_RC_INT_SAME(ABDAY_4);
    HHVM_RC_INT_SAME(ABDAY_5);
    HHVM_RC_INT_SAME(ABDAY_6);
    HHVM_RC_INT_SAME(ABDAY_7);
#endif
#ifdef DAY_1
    HHVM_RC_INT_SAME(DAY_1);
    HHVM_RC_INT_SAME(DAY_2);
    HHVM_RC_INT_SAME(DAY_3);
    HHVM_RC_INT_SAME(DAY_4);
    HHVM_RC_INT_SAME(DAY_5);
    HHVM_RC_INT_SAME(DAY_6);
    HHVM_RC_INT_SAME(DAY_7);
#endif
#ifdef ABMON_1
    HHVM_RC_INT_SAME(ABMON_1);
    HHVM_RC_INT_SAME(ABMON_2);
    HHVM_RC_INT_SAME(ABMON_3);
    HHVM_RC_INT_SAME(ABMON_4);
    HHVM_RC_INT_SAME(ABMON_5);
    HHVM_RC_INT_SAME(ABMON_6);
    HHVM_RC_INT_SAME(ABMON_7);
    HHVM_RC_INT_SAME(ABMON_8);
    HHVM_RC_INT_SAME(ABMON_9);
    HHVM_RC_INT_SAME(ABMON_10);
    HHVM_RC_INT_SAME(ABMON_11);
    HHVM_RC_INT_SAME(ABMON_12);
#endif
#ifdef MON_1
    HHVM_RC_INT_SAME(MON_1);
    HHVM_RC_INT_SAME(MON_2);
    HHVM_RC_INT_SAME(MON_3);
    HHVM_RC_INT_SAME(MON_4);
    HHVM_RC_INT_SAME(MON_5);
    HHVM_RC_INT_SAME(MON_6);
    HHVM_RC_INT_SAME(MON_7);
    HHVM_RC_INT_SAME(MON_8);
    HHVM_RC_INT_SAME(MON_9);
    HHVM_RC_INT_SAME(MON_10);
    HHVM_RC_INT_SAME(MON_11);
    HHVM_RC_INT_SAME(MON_12);
#endif

    // These are ostensibly bools,
    // but for historical reasons are expressed as ints
    HHVM_RC_INT(CRYPT_BLOWFISH, 1);
    HHVM_RC_INT(CRYPT_EXT_DES, 0);
    HHVM_RC_INT(CRYPT_MD5, 1);
    HHVM_RC_INT(CRYPT_STD_DES, 1);

    HHVM_RC_INT(CRYPT_SALT_LENGTH, 12);

    loadSystemlib();
  }


static int exif_process_string(char **result, char *value,
                               size_t byte_count) {
  /* we cannot use strlcpy - here the problem is that we cannot use strlen to
   * determin length of string and we cannot use strlcpy with len=byte_count+1
   * because then we might get into an EXCEPTION if we exceed an allocated
   * memory page...so we use php_strnlen in conjunction with memcpy and add
   * the NUL char.
   */
    if ((byte_count=php_strnlen(value, byte_count)) > 0) {
      return exif_process_undefined(result, value, byte_count);
    }
    PHP_STRNDUP((*result), "", 1); /* force empty string */
    if (*result) return byte_count+1;
    return 0;
}


    static void compileTimeAssertions() {
      static_assert(sizeof(TraceData) == (sizeof(uint64_t) * 4), "");
    }


Array HHVM_FUNCTION(gd_info) {
  Array ret = Array::CreateDArray();

  ret.set(s_GD_Version, PHP_GD_VERSION_STRING);

#ifdef ENABLE_GD_TTF
  ret.set(s_FreeType_Support, true);
#if HAVE_LIBFREETYPE
  ret.set(s_FreeType_Linkage, s_with_freetype);
#elif HAVE_LIBTTF
  ret.set(s_FreeType_Linkage, s_with_TTF_library);
#else
  ret.set(s_FreeType_Linkage, s_with_unknown_library);
#endif
#else
  ret.set(s_FreeType_Support, false);
#endif

#ifdef HAVE_LIBT1
  ret.set(s_T1Lib_Support, true);
#else
  ret.set(s_T1Lib_Support, false);
#endif
  ret.set(s_GIF_Read_Support, true);
  ret.set(s_GIF_Create_Support, true);
#ifdef HAVE_GD_JPG
  ret.set(s_JPG_Support, true);
#else
  ret.set(s_JPG_Support, false);
#endif
#ifdef HAVE_GD_PNG
  ret.set(s_PNG_Support, true);
#else
  ret.set(s_PNG_Support, false);
#endif
  ret.set(s_WBMP_Support, true);
#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED)
  ret.set(s_XPM_Support, true);
#else
  ret.set(s_XPM_Support, false);
#endif
  ret.set(s_XBM_Support, true);
#if defined(USE_GD_JISX0208) && defined(HAVE_GD_BUNDLED)
  ret.set(s_JIS_mapped_Japanese_Font_Support, true);
#else
  ret.set(s_JIS_mapped_Japanese_Font_Support, false);
#endif
  return ret;
}


bool MemFile::close() {
  return closeImpl();
}


int string_copy(char *dst, const char *src, int siz) {
  register char *d = dst;
  register const char *s = src;
  register size_t n = siz;

  /* Copy as many bytes as will fit */
  if (n != 0 && --n != 0) {
    do {
      if ((*d++ = *s++) == 0)
        break;
    } while (--n != 0);
  }

  /* Not enough room in dst, add NUL and traverse rest of src */
  if (n == 0) {
    if (siz != 0)
      *d = '\0';    /* NUL-terminate dst */
    while (*s++)
      ;
  }

  return(s - src - 1);  /* count does not include NUL */
}


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


Variant HHVM_FUNCTION(socket_read,
                      const Resource& socket,
                      int64_t length,
                      int type /* = 0 */) {
  if (length <= 0) {
    return false;
  }
  auto sock = cast<Socket>(socket);

  char *tmpbuf = (char *)malloc(length + 1);
  int retval;
  if (type == PHP_NORMAL_READ) {
    retval = php_read(sock, tmpbuf, length, 0);
  } else {
    retval = recv(sock->fd(), tmpbuf, (size_t)length, 0);
  }

  if (retval == -1) {
    /* if the socket is in non-blocking mode and there's no data to read,
    don't output any error, as this is a normal situation, and not an error */
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
      sock->setError(errno);
    } else {
      SOCKET_ERROR(sock, "unable to read from socket", errno);
    }

    free(tmpbuf);
    return false;
  }

  tmpbuf[retval] = '\0' ;
  return String(tmpbuf, retval, AttachString);
}


bool PCRECache::find(Accessor& accessor,
                     const StringData* regex,
                     TempKeyCache& keyCache)
{
  switch (m_kind) {
    case CacheKind::Static:
      {
        assertx(m_staticCache.load());
        StaticCache::iterator it;
        auto cache = m_staticCache.load(std::memory_order_acquire);
        if ((it = cache->find(regex)) != cache->end()) {
          accessor = it->second;
          return true;
        }
        return false;
      }
    case CacheKind::Lru:
    case CacheKind::Scalable:
      {
        if (!keyCache) {
          keyCache.reset(new LRUCacheKey(regex->data(), regex->size()));
        }
        bool found;
        if (m_kind == CacheKind::Lru) {
          found = m_lruCache->find(accessor.resetToLRU(), *keyCache);
        } else {
          found = m_scalableCache->find(accessor.resetToLRU(), *keyCache);
        }
        return found;
      }
  }
  always_assert(false);
}


static struct gfxinfo *php_handle_jpc(const req::ptr<File>& file) {
  struct gfxinfo *result = nullptr;
  int highest_bit_depth, bit_depth;
  unsigned char first_marker_id;
  unsigned int i;

  /* JPEG 2000 components can be vastly different from one another.
     Each component can be sampled at a different resolution, use
     a different colour space, have a separate colour depth, and
     be compressed totally differently! This makes giving a single
     "bit depth" answer somewhat problematic. For this implementation
     we'll use the highest depth encountered. */

  /* Get the single byte that remains after the file type indentification */
  first_marker_id = file->getc();

  /* Ensure that this marker is SIZ (as is mandated by the standard) */
  if (first_marker_id != JPEG2000_MARKER_SIZ) {
    raise_warning("JPEG2000 codestream corrupt(Expected SIZ marker "
                    "not found after SOC)");
    return nullptr;
  }

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

  php_read2(file); /* Lsiz */
  php_read2(file); /* Rsiz */
  result->width = php_read4(file); /* Xsiz */
  result->height = php_read4(file); /* Ysiz */

#if MBO_0
  php_read4(file); /* XOsiz */
  php_read4(file); /* YOsiz */
  php_read4(file); /* XTsiz */
  php_read4(file); /* YTsiz */
  php_read4(file); /* XTOsiz */
  php_read4(file); /* YTOsiz */
#else
  if (!file->seek(24, SEEK_CUR)) {
    IM_FREE(result);
    return nullptr;
  }
#endif

  result->channels = php_read2(file); /* Csiz */
  if (result->channels > 256) {
    IM_FREE(result);
    return nullptr;
  }

  /* Collect bit depth info */
  highest_bit_depth = bit_depth = 0;
  for (i = 0; i < result->channels; i++) {
    bit_depth = file->getc(); /* Ssiz[i] */
    bit_depth++;
    if (bit_depth > highest_bit_depth) {
      highest_bit_depth = bit_depth;
    }

    file->getc(); /* XRsiz[i] */
    file->getc(); /* YRsiz[i] */
  }

  result->bits = highest_bit_depth;

  return result;
}


short  toInt16  () const { return m_str ? m_str->toInt16  () : 0;}


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

  long bits;
  unsigned long len=64, szlength;
  int factor=1,maxfactor=16;
  int slength, status=0;
  unsigned char *b, *buf=nullptr;
  String bufz;
  String tmp;

  b = (unsigned char *)IM_CALLOC(1, len + 1);
  CHECK_ALLOC_R(b, (len + 1), nullptr);

  if (!stream->seek(5, SEEK_CUR)) {
    IM_FREE(b);
    return nullptr;
  }

  String a = stream->read(64);
  if (a.length() != 64) {
    IM_FREE(b);
    return nullptr;
  }

  if (uncompress((Bytef*)b, &len, (const Bytef*)a.c_str(), 64) != Z_OK) {
    /* failed to decompress the file, will try reading the rest of the file */
    if (!stream->seek(8, SEEK_SET)) {
      IM_FREE(b);
      return nullptr;
    }

    while (!(tmp = stream->read(8192)).empty()) {
      bufz += tmp;
    }
    slength = bufz.length();
    /*
     * zlib::uncompress() wants to know the output data length
     * if none was given as a parameter
     * we try from input length * 2 up to input length * 2^8
     * doubling it whenever it wasn't big enough
     * that should be eneugh for all real life cases
    */

    do {
      szlength=slength*(1<<factor++);
      buf = (unsigned char *) IM_REALLOC(buf,szlength);
      if (!buf) IM_FREE(b);
      CHECK_ALLOC_R(buf, szlength, nullptr);
      status = uncompress((Bytef*)buf, &szlength,
                          (const Bytef*)bufz.c_str(), slength);
    } while ((status==Z_BUF_ERROR)&&(factor<maxfactor));

    if (status == Z_OK) {
       memcpy(b, buf, len);
    }

    if (buf) {
      IM_FREE(buf);
    }
  }

  if (!status) {
    result = (struct gfxinfo *)IM_CALLOC(1, sizeof (struct gfxinfo));
    if (!result) IM_FREE(b);
    CHECK_ALLOC_R(result, sizeof (struct gfxinfo), nullptr);
    bits = php_swf_get_bits (b, 0, 5);
    result->width = (php_swf_get_bits (b, 5 + bits, bits) -
      php_swf_get_bits (b, 5, bits)) / 20;
    result->height = (php_swf_get_bits (b, 5 + (3 * bits), bits) -
      php_swf_get_bits (b, 5 + (2 * bits), bits)) / 20;
  } else {
    result = nullptr;
  }

  IM_FREE(b);
  return result;
}


  ~CSRequest() override {
    // X509_REQ_free(nullptr) is a no-op
    X509_REQ_free(m_csr);
  }

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

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

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

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

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

  ~OpenSSLInitializer() {
    EVP_cleanup();
  }
}


bool HHVM_FUNCTION(socket_set_nonblock,
                   const Resource& socket) {
  return cast<Socket>(socket)->setBlocking(false);
}


String stringToCaseFirst(const String& str, OpTo tocase, OpIs iscase) {
  if (str.empty() || iscase(str[0])) {
    return str;
  }

  String ret(str, CopyString);
  char* first = ret.get()->mutableData();

  *first = tocase(*first);
  return ret;
}


  static void compileTimeAssertions() {
    static_assert(sizeof(String) == sizeof(req::ptr<StringData>), "");
  }

String(const StringData*) = delete;

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

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

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

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

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

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

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

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

static const int npos = -1;

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

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

friend String&& operator

friend String

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

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

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

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

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

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

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

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

extern const String null_string;

struct StrNR {
private:
  StringData *m_px;

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

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

  /* implicit */ operator

static uint32_t CreateAll();

static bool s_globalInit;

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

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

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


static int compare_right(char const **a, char const *aend,
                         char const **b, char const *bend) {
  int bias = 0;

  /* The longest run of digits wins.  That aside, the greatest
     value wins, but we can't know that it will until we've scanned
     both numbers to know that they have the same magnitude, so we
     remember it in BIAS. */
  for(;; (*a)++, (*b)++) {
    if ((*a == aend || !isdigit((int)(unsigned char)**a)) &&
        (*b == bend || !isdigit((int)(unsigned char)**b)))
      return bias;
    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) {
      if (!bias)
        bias = -1;
    } else if (**a > **b) {
      if (!bias)
        bias = +1;
    }
  }

  return 0;
}


bool CPlayListPLS::Load(const std::string &strFile)
{
  //read it from the file
  std::string strFileName(strFile);
  m_strPlayListName = URIUtils::GetFileName(strFileName);

  Clear();

  bool bShoutCast = false;
  if( StringUtils::StartsWithNoCase(strFileName, "shout://") )
  {
    strFileName.replace(0, 8, "http://");
    m_strBasePath = "";
    bShoutCast = true;
  }
  else
    URIUtils::GetParentPath(strFileName, m_strBasePath);

  CFile file;
  if (!file.Open(strFileName) )
  {
    file.Close();
    return false;
  }

  if (file.GetLength() > 1024*1024)
  {
    CLog::Log(LOGWARNING, "{} - File is larger than 1 MB, most likely not a playlist",
              __FUNCTION__);
    return false;
  }

  char szLine[4096];
  std::string strLine;

  // run through looking for the [playlist] marker.
  // if we find another http stream, then load it.
  while (true)
  {
    if ( !file.ReadString(szLine, sizeof(szLine) ) )
    {
      file.Close();
      return size() > 0;
    }
    strLine = szLine;
    StringUtils::Trim(strLine);
    if(StringUtils::EqualsNoCase(strLine, START_PLAYLIST_MARKER))
      break;

    // if there is something else before playlist marker, this isn't a pls file
    if(!strLine.empty())
      return false;
  }

  bool bFailed = false;
  while (file.ReadString(szLine, sizeof(szLine) ) )
  {
    strLine = szLine;
    StringUtils::RemoveCRLF(strLine);
    size_t iPosEqual = strLine.find('=');
    if (iPosEqual != std::string::npos)
    {
      std::string strLeft = strLine.substr(0, iPosEqual);
      iPosEqual++;
      std::string strValue = strLine.substr(iPosEqual);
      StringUtils::ToLower(strLeft);
      StringUtils::TrimLeft(strLeft);

      if (strLeft == "numberofentries")
      {
        m_vecItems.reserve(atoi(strValue.c_str()));
      }
      else if (StringUtils::StartsWith(strLeft, "file"))
      {
        std::vector <int>::size_type idx = atoi(strLeft.c_str() + 4);
        if (!Resize(idx))
        {
          bFailed = true;
          break;
        }

        // Skip self - do not load playlist recursively
        if (StringUtils::EqualsNoCase(URIUtils::GetFileName(strValue),
                                      URIUtils::GetFileName(strFileName)))
          continue;

        if (m_vecItems[idx - 1]->GetLabel().empty())
          m_vecItems[idx - 1]->SetLabel(URIUtils::GetFileName(strValue));
        CFileItem item(strValue, false);
        if (bShoutCast && !item.IsAudio())
          strValue.replace(0, 7, "shout://");

        strValue = URIUtils::SubstitutePath(strValue);
        CUtil::GetQualifiedFilename(m_strBasePath, strValue);
        g_charsetConverter.unknownToUTF8(strValue);
        m_vecItems[idx - 1]->SetPath(strValue);
      }
      else if (StringUtils::StartsWith(strLeft, "title"))
      {
        std::vector <int>::size_type idx = atoi(strLeft.c_str() + 5);
        if (!Resize(idx))
        {
          bFailed = true;
          break;
        }
        g_charsetConverter.unknownToUTF8(strValue);
        m_vecItems[idx - 1]->SetLabel(strValue);
      }
      else if (StringUtils::StartsWith(strLeft, "length"))
      {
        std::vector <int>::size_type idx = atoi(strLeft.c_str() + 6);
        if (!Resize(idx))
        {
          bFailed = true;
          break;
        }
        m_vecItems[idx - 1]->GetMusicInfoTag()->SetDuration(atol(strValue.c_str()));
      }
      else if (strLeft == "playlistname")
      {
        m_strPlayListName = strValue;
        g_charsetConverter.unknownToUTF8(m_strPlayListName);
      }
    }
  }
  file.Close();

  if (bFailed)
  {
    CLog::Log(LOGERROR,
              "File {} is not a valid PLS playlist. Location of first file,title or length is not "
              "permitted (eg. File0 should be File1)",
              URIUtils::GetFileName(strFileName));
    return false;
  }

  // check for missing entries
  ivecItems p = m_vecItems.begin();
  while ( p != m_vecItems.end())
  {
    if ((*p)->GetPath().empty())
    {
      p = m_vecItems.erase(p);
    }
    else
    {
      ++p;
    }
  }

  return true;
}

[Playlist] dont use istream directly to a tinyxml structure

Turn istream into a std::string to handle large buffers (#20305)

#define START_PLAYLIST_MARKER "[playlist]" // may be case-insensitive (equivalent to .ini file on win32)


bool CPlayListRAM::LoadData(std::istream& stream)
{
  CLog::Log(LOGINFO, "Parsing RAM");

  std::string strMMS;
  while( stream.peek() != '\n' && stream.peek() != '\r' )
    strMMS += stream.get();

  CLog::Log(LOGINFO, "Adding element {}", strMMS);
  CFileItemPtr newItem(new CFileItem(strMMS));
  newItem->SetPath(strMMS);
  Add(newItem);
  return true;
}

#define PLAYLIST_NAME     "PlaylistName"

#define START_PLAYLIST_MARKER "[playlist]" // may be case-insensitive (equivalent to .ini file on win32)


bool CPlayListASX::LoadData(std::istream& stream)
{
  CLog::Log(LOGINFO, "Parsing ASX");

  if(stream.peek() == '[')
  {
    return LoadAsxIniInfo(stream);
  }
  else
  {
    std::string asxstream(std::istreambuf_iterator<char>(stream), {});
    CXBMCTinyXML xmlDoc;
    xmlDoc.Parse(asxstream, TIXML_DEFAULT_ENCODING);

    if (xmlDoc.Error())
    {
      CLog::Log(LOGERROR, "Unable to parse ASX info Error: {}", xmlDoc.ErrorDesc());
      return false;
    }

    TiXmlElement *pRootElement = xmlDoc.RootElement();

    if (!pRootElement)
      return false;

    // lowercase every element
    TiXmlNode *pNode = pRootElement;
    TiXmlNode *pChild = NULL;
    std::string value;
    value = pNode->Value();
    StringUtils::ToLower(value);
    pNode->SetValue(value);
    while(pNode)
    {
      pChild = pNode->IterateChildren(pChild);
      if(pChild)
      {
        if (pChild->Type() == TiXmlNode::TINYXML_ELEMENT)
        {
          value = pChild->Value();
          StringUtils::ToLower(value);
          pChild->SetValue(value);

          TiXmlAttribute* pAttr = pChild->ToElement()->FirstAttribute();
          while(pAttr)
          {
            value = pAttr->Name();
            StringUtils::ToLower(value);
            pAttr->SetName(value);
            pAttr = pAttr->Next();
          }
        }

        pNode = pChild;
        pChild = NULL;
        continue;
      }

      pChild = pNode;
      pNode = pNode->Parent();
    }
    std::string roottitle;
    TiXmlElement *pElement = pRootElement->FirstChildElement();
    while (pElement)
    {
      value = pElement->Value();
      if (value == "title" && !pElement->NoChildren())
      {
        roottitle = pElement->FirstChild()->ValueStr();
      }
      else if (value == "entry")
      {
        std::string title(roottitle);

        TiXmlElement *pRef = pElement->FirstChildElement("ref");
        TiXmlElement *pTitle = pElement->FirstChildElement("title");

        if(pTitle && !pTitle->NoChildren())
          title = pTitle->FirstChild()->ValueStr();

        while (pRef)
        { // multiple references may appear for one entry
          // duration may exist on this level too
          value = XMLUtils::GetAttribute(pRef, "href");
          if (!value.empty())
          {
            if(title.empty())
              title = value;

            CLog::Log(LOGINFO, "Adding element {}, {}", title, value);
            CFileItemPtr newItem(new CFileItem(title));
            newItem->SetPath(value);
            Add(newItem);
          }
          pRef = pRef->NextSiblingElement("ref");
        }
      }
      else if (value == "entryref")
      {
        value = XMLUtils::GetAttribute(pElement, "href");
        if (!value.empty())
        { // found an entryref, let's try loading that url
          std::unique_ptr<CPlayList> playlist(CPlayListFactory::Create(value));
          if (nullptr != playlist)
            if (playlist->Load(value))
              Add(*playlist);
        }
      }
      pElement = pElement->NextSiblingElement();
    }
  }

  return true;
}


void CPlayListPLS::Save(const std::string& strFileName) const
{
  if (!m_vecItems.size()) return ;
  std::string strPlaylist = CUtil::MakeLegalPath(strFileName);
  CFile file;
  if (!file.OpenForWrite(strPlaylist, true))
  {
    CLog::Log(LOGERROR, "Could not save PLS playlist: [{}]", strPlaylist);
    return;
  }
  std::string write;
  write += StringUtils::Format("{}\n", START_PLAYLIST_MARKER);
  std::string strPlayListName=m_strPlayListName;
  g_charsetConverter.utf8ToStringCharset(strPlayListName);
  write += StringUtils::Format("PlaylistName={}\n", strPlayListName);

  for (int i = 0; i < (int)m_vecItems.size(); ++i)
  {
    CFileItemPtr item = m_vecItems[i];
    std::string strFileName=item->GetPath();
    g_charsetConverter.utf8ToStringCharset(strFileName);
    std::string strDescription=item->GetLabel();
    g_charsetConverter.utf8ToStringCharset(strDescription);
    write += StringUtils::Format("File{}={}\n", i + 1, strFileName);
    write += StringUtils::Format("Title{}={}\n", i + 1, strDescription.c_str());
    write +=
        StringUtils::Format("Length{}={}\n", i + 1, item->GetMusicInfoTag()->GetDuration() / 1000);
  }

  write += StringUtils::Format("NumberOfEntries={0}\n", m_vecItems.size());
  write += StringUtils::Format("Version=2\n");
  file.Write(write.c_str(), write.size());
  file.Close();
}

#define START_PLAYLIST_MARKER "[playlist]" // may be case-insensitive (equivalent to .ini file on win32)

#define PLAYLIST_NAME     "PlaylistName"


bool CPlayListPLS::Resize(std::vector <int>::size_type newSize)
{
  if (newSize == 0)
    return false;

  while (m_vecItems.size() < newSize)
  {
    CFileItemPtr fileItem(new CFileItem());
    m_vecItems.push_back(fileItem);
  }
  return true;
}


bool CPlayListASX::LoadAsxIniInfo(std::istream &stream)
{
  CLog::Log(LOGINFO, "Parsing INI style ASX");

  std::string name, value;

  while( stream.good() )
  {
    // consume blank rows, and blanks
    while((stream.peek() == '\r' || stream.peek() == '\n' || stream.peek() == ' ') && stream.good())
      stream.get();

    if(stream.peek() == '[')
    {
      // this is an [section] part, just ignore it
      while(stream.good() && stream.peek() != '\r' && stream.peek() != '\n')
        stream.get();
      continue;
    }
    name = "";
    value = "";
    // consume name
    while(stream.peek() != '\r' && stream.peek() != '\n' && stream.peek() != '=' && stream.good())
      name += stream.get();

    // consume =
    if(stream.get() != '=')
      continue;

    // consume value
    while(stream.peek() != '\r' && stream.peek() != '\n' && stream.good())
      value += stream.get();

    CLog::Log(LOGINFO, "Adding element {}={}", name, value);
    CFileItemPtr newItem(new CFileItem(value));
    newItem->SetPath(value);
    if (newItem->IsVideo() && !newItem->HasVideoInfoTag()) // File is a video and needs a VideoInfoTag
      newItem->GetVideoInfoTag()->Reset(); // Force VideoInfoTag creation
    Add(newItem);
  }

  return true;
}


TfLiteStatus EvalPie(TfLiteContext* context, TfLiteNode* node,
                     TfLiteFullyConnectedParams* params, OpData* data,
                     const TfLiteTensor* input, const TfLiteTensor* filter,
                     const TfLiteTensor* bias, TfLiteTensor* output) {
  int total_input_size = 1;
  for (int i = 0; i < input->dims->size; i++) {
    total_input_size *= input->dims->data[i];
  }

  int input_size = filter->dims->data[1];
  const int batch_size = total_input_size / filter->dims->data[1];
  const int num_units = filter->dims->data[0];

  // Output = bias if bias tensor exists.
  if (bias) {
    tensor_utils::VectorBatchVectorAssign(GetTensorData<float>(bias), num_units,
                                          batch_size,
                                          GetTensorData<float>(output));
  } else {
    std::fill_n(GetTensorData<float>(output), batch_size * num_units, 0.0f);
  }

  // Compute output += weight * input
  tensor_utils::MatrixBatchVectorMultiplyAccumulate(
      GetTensorData<float>(filter), num_units, input_size,
      GetTensorData<float>(input), batch_size, GetTensorData<float>(output));

  // Apply activation function
  tensor_utils::ApplyActivationToVector(
      GetTensorData<float>(output), batch_size * num_units, params->activation,
      GetTensorData<float>(output));

  return kTfLiteOk;
}

[lite] add validation check for sparse fully connected

PiperOrigin-RevId: 417629354
Change-Id: If96171c4bd4f5fdb01d6368d6deab19d1c9beca7

/* No explicit global definitions found. */


void FullyConnectedInt8(const OpData* data, const TfLiteTensor* input,
                        const TfLiteTensor* filter, const TfLiteTensor* bias,
                        TfLiteTensor* output,
                        CpuBackendContext* cpu_backend_context) {
  FullyConnectedParams op_params;
  op_params.input_offset = -input->params.zero_point;
  op_params.weights_offset = -filter->params.zero_point;
  op_params.output_offset = output->params.zero_point;
  op_params.output_multiplier = data->output_multiplier;
  op_params.output_shift = data->output_shift;
  op_params.quantized_activation_min = data->output_activation_min;
  op_params.quantized_activation_max = data->output_activation_max;
  op_params.lhs_cacheable = IsConstantTensor(filter);
  op_params.rhs_cacheable = IsConstantTensor(input);
  if (kernel_type == kReference) {
    reference_integer_ops::FullyConnected(
        op_params, GetTensorShape(input), GetTensorData<int8_t>(input),
        GetTensorShape(filter), GetTensorData<int8_t>(filter),
        GetTensorShape(bias), GetTensorData<int32_t>(bias),
        GetTensorShape(output), GetTensorData<int8_t>(output));
  } else {
    optimized_integer_ops::FullyConnected(
        op_params, GetTensorShape(input), GetTensorData<int8_t>(input),
        GetTensorShape(filter), GetTensorData<int8_t>(filter),
        GetTensorShape(bias), GetTensorData<int32_t>(bias),
        GetTensorShape(output), GetTensorData<int8_t>(output),
        cpu_backend_context);
  }
}


void FullyConnectedInt16(const OpData* data, const TfLiteTensor* input,
                         const TfLiteTensor* filter, const TfLiteTensor* bias,
                         TfLiteTensor* output) {
  FullyConnectedParams op_params;
  op_params.weights_offset = -filter->params.zero_point;
  op_params.output_multiplier = data->output_multiplier;
  op_params.output_shift = data->output_shift;
  op_params.quantized_activation_min = data->output_activation_min;
  op_params.quantized_activation_max = data->output_activation_max;
  reference_integer_ops::FullyConnected(
      op_params, GetTensorShape(input), GetTensorData<int16_t>(input),
      GetTensorShape(filter), GetTensorData<int8_t>(filter),
      GetTensorShape(bias), GetTensorData<int64_t>(bias),
      GetTensorShape(output), GetTensorData<int16_t>(output));
}


void EvalSparseHybridImpl(TfLiteContext* context, TfLiteNode* node,
                          TfLiteFullyConnectedParams* params, OpData* data,
                          const TfLiteTensor* input, const TfLiteTensor* filter,
                          const TfLiteTensor* bias, int thread_start,
                          int thread_end, TfLiteTensor* input_quantized,
                          TfLiteTensor* scaling_factors,
                          TfLiteTensor* accum_scratch, TfLiteTensor* row_sums,
                          TfLiteTensor* input_offsets, TfLiteTensor* output) {
  ruy::profiler::ScopeLabel label("FullyConnected");
  ruy::profiler::ScopeLabel inner_label("Sparse Hybrid Kernel");
  const auto& input_shape = GetTensorShape(input);
  const auto& output_shape = GetTensorShape(output);
  const auto& filter_shape = GetTensorShape(filter);
  const int input_dims_count = input_shape.DimensionsCount();
  const int output_dims_count = output_shape.DimensionsCount();
  const int filter_dims_count = filter_shape.DimensionsCount();
  const int batch_size = thread_end - thread_start;
  const int input_depth = MatchingDim(filter_shape, filter_dims_count - 1,
                                      input_shape, input_dims_count - 1);
  const int output_depth = MatchingDim(filter_shape, filter_dims_count - 2,
                                       output_shape, output_dims_count - 1);
  const int per_thread_input_size = batch_size * input_depth;

  const float* per_thread_input =
      GetTensorData<float>(input) + thread_start * input_depth;
  float* per_thread_output =
      GetTensorData<float>(output) + thread_start * output_depth;

  // Output = bias if bias tensor exists.
  if (bias) {
    tensor_utils::VectorBatchVectorAssign(GetTensorData<float>(bias),
                                          output_depth, batch_size,
                                          per_thread_output);
  } else {
    std::fill_n(per_thread_output, batch_size * output_depth, 0.0f);
  }

  // Save matrix multiplication computation for all zero input.
  if (tensor_utils::IsZeroVector(per_thread_input, per_thread_input_size)) {
    tensor_utils::ApplyActivationToVector(
        per_thread_output, batch_size * output_depth, params->activation,
        per_thread_output);
    return;
  }

  // Quantize input from float to uint8 + quantization params (scaling factor).
  float* scaling_factors_ptr =
      GetTensorData<float>(scaling_factors) + thread_start;
  int32_t* input_offset_ptr = nullptr;
  int32_t* row_sums_ptr = nullptr;
  if (params->asymmetric_quantize_inputs) {
    input_offset_ptr = GetTensorData<int32_t>(input_offsets) + thread_start;
    row_sums_ptr = GetTensorData<int32_t>(row_sums);
  }
  int8_t* quant_data =
      GetTensorData<int8_t>(input_quantized) + thread_start * input_depth;
  tensor_utils::BatchQuantizeFloats(per_thread_input, batch_size, input_depth,
                                    quant_data, scaling_factors_ptr,
                                    input_offset_ptr,
                                    params->asymmetric_quantize_inputs);
  for (int b = 0; b < batch_size; ++b) {
    // Incorporate scaling of the filter.
    scaling_factors_ptr[b] *= filter->params.scale;
  }

  if (params->asymmetric_quantize_inputs) {
    float* per_thread_output_ptr = per_thread_output;
    for (int b = 0; b < batch_size; ++b) {
      const float scaled_zp = scaling_factors_ptr[b] * input_offset_ptr[b];
      for (int row = 0; row < output_depth; ++row) {
        *per_thread_output_ptr++ -= scaled_zp * row_sums_ptr[row];
      }
    }
  }

  // Compute output += weight * quantized_input
  TfLiteTensor* filter_ledger = &context->tensors[node->temporaries->data[5]];
  tensor_utils::SparseMatrixBatchVectorMultiplyAccumulate(
      GetTensorData<int8_t>(filter), GetTensorData<uint8_t>(filter_ledger),
      output_depth, input_depth, quant_data, scaling_factors_ptr, batch_size,
      per_thread_output);

  // Apply activation function to floats.
  tensor_utils::ApplyActivationToVector(per_thread_output,
                                        batch_size * output_depth,
                                        params->activation, per_thread_output);
}


void* Init(TfLiteContext* context, const char* buffer, size_t length) {
  // This is a builtin op, so we don't use the contents in 'buffer', if any.
  // Instead, we allocate a new object to carry information from Prepare() to
  // Eval().
  auto* op_data = new OpData();
  context->AddTensors(context, /*tensors_to_add=*/6,
                      &op_data->scratch_tensor_index);
  return op_data;
}


TfLiteStatus EvalFloat(TfLiteContext* context, TfLiteNode* node,
                       TfLiteFullyConnectedParams* params, OpData* data,
                       const TfLiteTensor* input, const TfLiteTensor* filter,
                       const TfLiteTensor* bias, TfLiteTensor* output) {
  float output_activation_min, output_activation_max;
  CalculateActivationRange(params->activation, &output_activation_min,
                           &output_activation_max);
  if (kernel_type == kReference) {
    FullyConnectedParams op_params;
    op_params.float_activation_min = output_activation_min;
    op_params.float_activation_max = output_activation_max;
    if (filter->sparsity != nullptr) {
      const auto& sparsity = *filter->sparsity;
      reference_ops::FullyConnectedSparseWeight(
          sparsity, op_params, GetTensorShape(input),
          GetTensorData<float>(input), GetTensorShape(filter),
          GetTensorData<float>(filter), GetTensorShape(bias),
          GetTensorData<float>(bias), GetTensorShape(output),
          GetTensorData<float>(output));
    } else {
      reference_ops::FullyConnected(
          op_params, GetTensorShape(input), GetTensorData<float>(input),
          GetTensorShape(filter), GetTensorData<float>(filter),
          GetTensorShape(bias), GetTensorData<float>(bias),
          GetTensorShape(output), GetTensorData<float>(output));
    }
  } else if (kernel_type == kLegacyPie) {
    return EvalPie(context, node, params, data, input, filter, bias, output);
  } else {
    FullyConnectedParams op_params;
    op_params.float_activation_min = output_activation_min;
    op_params.float_activation_max = output_activation_max;
    if (filter->sparsity != nullptr) {
      const auto& sparsity = *filter->sparsity;
      if (!SupportedSparsityFormat(sparsity)) {
        TF_LITE_KERNEL_LOG(context,
                           "Unsupported sparse fully-connected weight format.");
        return kTfLiteError;
      }
      const auto& input_shape = GetTensorShape(input);
      const auto& filter_shape = GetTensorShape(filter);
      const auto& output_shape = GetTensorShape(output);
      const auto& bias_shape = GetTensorShape(bias);
      if (!VerifySparsity(filter_shape, input_shape, output_shape, &sparsity)) {
        TF_LITE_KERNEL_LOG(context, "Invalid sparse fully-connected format.");
        return kTfLiteError;
      }

      if (sparsity.dim_metadata_size == kDimMetadataSizeRandomSparse) {
        // Random sparse.
        optimized_ops::FullyConnectedSparseWeight(
            sparsity, op_params,                         // Disable formatting
            input_shape, GetTensorData<float>(input),    // Disable formatting
            filter_shape, GetTensorData<float>(filter),  // Disable formatting
            bias_shape, GetTensorData<float>(bias),      // Disable formatting
            output_shape, GetTensorData<float>(output));
      } else if (sparsity.dim_metadata_size == kDimMetadataSizeBlockSparse &&
                 sparsity.dim_metadata[2].dense_size == 4) {
        // Block sparse with block size of 1x4.
        optimized_ops::FullyConnectedSparseWeight1x4(
            sparsity, op_params,                         // Disable formatting
            input_shape, GetTensorData<float>(input),    // Disable formatting
            filter_shape, GetTensorData<float>(filter),  // Disable formatting
            bias_shape, GetTensorData<float>(bias),      // Disable formatting
            output_shape, GetTensorData<float>(output),
            CpuBackendContext::GetFromContext(context));
      } else {
        TF_LITE_KERNEL_LOG(context,
                           "Unsupported sparse fully-connected weight format.");
        return kTfLiteError;
      }

    } else {
      op_params.lhs_cacheable = IsConstantTensor(filter);
      op_params.rhs_cacheable = IsConstantTensor(input);
      optimized_ops::FullyConnected(
          op_params, GetTensorShape(input), GetTensorData<float>(input),
          GetTensorShape(filter), GetTensorData<float>(filter),
          GetTensorShape(bias), GetTensorData<float>(bias),
          GetTensorShape(output), GetTensorData<float>(output),
          CpuBackendContext::GetFromContext(context));
    }
  }

  return kTfLiteOk;
}


inline TfLiteStatus CheckTypes(TfLiteContext* context,
                               const TfLiteTensor* input,
                               const TfLiteTensor* filter,
                               const TfLiteTensor* bias, TfLiteTensor* output,
                               TfLiteFullyConnectedParams* params) {
  const bool is_quantized =
      ((filter->type == kTfLiteUInt8) || (filter->type == kTfLiteInt8));
  const bool is_hybrid = is_quantized && (input->type == kTfLiteFloat32);
  const bool is_shuffled =
      is_quantized && (params->weights_format ==
                       kTfLiteFullyConnectedWeightsFormatShuffled4x16Int8);

  // optional bias tensor.
  const bool is_optional_bias_float = !bias || (bias->type == kTfLiteFloat32);
  const bool is_optional_bias_int =
      !bias || (bias->type == kTfLiteInt32) || (bias->type == kTfLiteInt64);

  if (is_quantized) {
    if (is_shuffled) {
      TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteUInt8);
      TF_LITE_ENSURE_TYPES_EQ(context, filter->type, kTfLiteUInt8);
      TF_LITE_ENSURE_TYPES_EQ(context, output->type, kTfLiteInt16);
      TF_LITE_ENSURE_EQ(context, is_optional_bias_int, true);
    } else if (is_hybrid) {
      TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteFloat32);
      TF_LITE_ENSURE_TYPES_EQ(context, output->type, kTfLiteFloat32);
      TF_LITE_ENSURE_EQ(context, is_optional_bias_float, true);
    } else {
      TF_LITE_ENSURE(context, input->type == kTfLiteUInt8 ||
                                  input->type == kTfLiteInt8 ||
                                  input->type == kTfLiteInt16);
      TF_LITE_ENSURE(context, output->type == kTfLiteUInt8 ||
                                  output->type == kTfLiteInt8 ||
                                  output->type == kTfLiteInt16);
      TF_LITE_ENSURE_EQ(context, is_optional_bias_int, true);
    }
  } else {
    // Only float32 is supported currently
    TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteFloat32);
    TF_LITE_ENSURE_TYPES_EQ(context, output->type, kTfLiteFloat32);
    TF_LITE_ENSURE_TYPES_EQ(context, filter->type, kTfLiteFloat32);
    TF_LITE_ENSURE_EQ(context, is_optional_bias_float, true);
  }

  return kTfLiteOk;
}