TypedValue HHVM_FUNCTION(serialize_memoize_param, TypedValue param) { // Memoize throws in the emitter if any function parameters are references, so // we can just assert that the param is cell here assertx(param.m_type != KindOfRef); auto const type = param.m_type; if (isStringType(type)) { auto const str = param.m_data.pstr; if (str->empty()) { return make_tv<KindOfPersistentString>(s_emptyStrMemoKey.get()); } else if ((unsigned char)str->data()[0] < '~') { // fb_compact_serialize always returns a string with the high-bit set in // the first character. Furthermore, we use ~ to begin all our special // constants, so anything less than ~ can't collide. There's no worry // about int-like strings because we use dicts (which don't perform key // coercion) to store the memoized values. str->incRefCount(); return param; } } else if (isContainer(param) && getContainerSize(param) == 0) { return make_tv<KindOfPersistentString>(s_emptyArrMemoKey.get()); } else if (type == KindOfUninit || type == KindOfNull) { return make_tv<KindOfPersistentString>(s_nullMemoKey.get()); } else if (type == KindOfBoolean) { return make_tv<KindOfPersistentString>( param.m_data.num ? s_trueMemoKey.get() : s_falseMemoKey.get() ); } else if (type == KindOfInt64) { return param; } return tvReturn( fb_compact_serialize(tvAsCVarRef(¶m), FBCompactSerializeBehavior::MemoizeParam)); }
Variant HHVM_FUNCTION(serialize_memoize_param, const Variant& param) { // Memoize throws in the emitter if any function parameters are references, so // we can just assert that the param is cell here const auto& cell_param = *tvAssertCell(param.asTypedValue()); auto type = param.getType(); if (type == KindOfInt64) { return param; } else if (type == KindOfUninit || type == KindOfNull) { return s_empty; } else if (type == KindOfBoolean) { return param.asBooleanVal() ? s_true : s_false; } else if (type == KindOfString) { auto str = param.asCStrRef(); if (str.empty()) { return s_emptyStr; } else if (str.charAt(0) > '9') { // If it doesn't start with a number, then we know it can never collide // with an int or any of our constants, so it's fine as is return param; } } else if (isContainer(cell_param) && getContainerSize(cell_param) == 0) { return s_emptyArr; } return fb_compact_serialize(param, FBCompactSerializeBehavior::MemoizeParam); }
Variant f_array_combine(CVarRef keys, CVarRef values) { const auto& cell_keys = *keys.asCell(); const auto& cell_values = *values.asCell(); if (UNLIKELY(!isContainer(cell_keys) || !isContainer(cell_values))) { raise_warning("Invalid operand type was used: array_combine expects " "arrays or collections"); return uninit_null(); } if (UNLIKELY(getContainerSize(cell_keys) != getContainerSize(cell_values))) { raise_warning("array_combine(): Both parameters should have an equal " "number of elements"); return false; } Array ret = ArrayData::Create(); for (ArrayIter iter1(cell_keys), iter2(cell_values); iter1; ++iter1, ++iter2) { ret.lvalAt(iter1.secondRefPlus()).setWithRef(iter2.secondRefPlus()); } return ret; }
Variant f_array_values(CVarRef input) { const auto& cell_input = *input.asCell(); if (!isContainer(cell_input)) { raise_warning("array_values() expects parameter 1 to be an array " "or collection"); return uninit_null(); } PackedArrayInit ai(getContainerSize(cell_input)); for (ArrayIter iter(cell_input); iter; ++iter) { ai.appendWithRef(iter.secondRefPlus()); } return ai.toArray(); }
static Variant arrayKeysSetHelper(const Cell& cell_input, CVarRef search_value, bool strict) { ArrayIter iter(cell_input); if (LIKELY(!search_value.isInitialized())) { PackedArrayInit ai(getContainerSize(cell_input)); for (; iter; ++iter) { ai.append(iter.second()); } return ai.toArray(); } Array ai = HphpArray::MakeReserve(0); for (; iter; ++iter) { if ((strict && HPHP::same(iter.secondRefPlus(), search_value)) || (!strict && HPHP::equal(iter.secondRefPlus(), search_value))) { ai.append(iter.second()); } } return ai; }
String StringUtil::Implode(const Variant& items, const String& delim) { if (!isContainer(items)) { throw_param_is_not_container(); } int size = getContainerSize(items); if (size == 0) return ""; String* sitems = (String*)smart_malloc(size * sizeof(String)); int len = 0; int lenDelim = delim.size(); int i = 0; for (ArrayIter iter(items); iter; ++iter) { new (&sitems[i]) String(iter.second().toString()); len += sitems[i].size() + lenDelim; i++; } len -= lenDelim; // always one delimiter less than count of items assert(i == size); String s = String(len, ReserveString); char *buffer = s.bufferSlice().ptr; const char *sdelim = delim.data(); char *p = buffer; for (int i = 0; i < size; i++) { String &item = sitems[i]; if (i && lenDelim) { memcpy(p, sdelim, lenDelim); p += lenDelim; } int lenItem = item.size(); if (lenItem) { memcpy(p, item.data(), lenItem); p += lenItem; } sitems[i].~String(); } smart_free(sitems); assert(p - buffer == len); s.setSize(len); return s; }
String StringUtil::Implode(const Variant& items, const String& delim, const bool checkIsContainer /* = true */) { if (checkIsContainer && !isContainer(items)) { throw_param_is_not_container(); } int size = getContainerSize(items); if (size == 0) return empty_string(); req::vector<String> sitems; sitems.reserve(size); size_t len = 0; size_t lenDelim = delim.size(); for (ArrayIter iter(items); iter; ++iter) { sitems.emplace_back(iter.second().toString()); len += sitems.back().size() + lenDelim; } len -= lenDelim; // always one delimiter less than count of items assert(sitems.size() == size); String s = String(len, ReserveString); char *buffer = s.mutableData(); const char *sdelim = delim.data(); char *p = buffer; String &init_str = sitems[0]; int init_len = init_str.size(); memcpy(p, init_str.data(), init_len); p += init_len; for (int i = 1; i < size; i++) { String &item = sitems[i]; memcpy(p, sdelim, lenDelim); p += lenDelim; int lenItem = item.size(); memcpy(p, item.data(), lenItem); p += lenItem; } assert(p - buffer == len); s.setSize(len); return s; }
Variant f_array_keys(CVarRef input, CVarRef search_value /* = null_variant */, bool strict /* = false */) { const auto& cell_input = *input.asCell(); if (UNLIKELY(!isContainer(cell_input))) { goto warn; } { // We treat Sets differently. For Sets, we pretend the values are // also the keys (similar to how Set::toArray() behaves). bool isSetType = cell_input.m_type == KindOfObject && cell_input.m_data.pobj->getCollectionType() == Collection::SetType; if (UNLIKELY(isSetType)) { return arrayKeysSetHelper(cell_input, search_value, strict); } ArrayIter iter(cell_input); if (LIKELY(!search_value.isInitialized())) { PackedArrayInit ai(getContainerSize(cell_input)); for (; iter; ++iter) { ai.append(iter.first()); } return ai.toArray(); } Array ai = Array::attach(HphpArray::MakeReserve(0)); for (; iter; ++iter) { if ((strict && HPHP::same(iter.secondRefPlus(), search_value)) || (!strict && HPHP::equal(iter.secondRefPlus(), search_value))) { ai.append(iter.first()); } } return ai; } warn: raise_warning("array_keys() expects parameter 1 to be an array " "or collection"); return uninit_null(); }