// Create either a static or an uncounted string. // Diffrence between static and uncounted is in the lifetime // of the string. Static are alive for the lifetime of the process. // Uncounted are not ref counted but will be deleted at some point. ALWAYS_INLINE StringData* StringData::MakeShared(StringSlice sl, bool trueStatic) { if (UNLIKELY(sl.len > StringData::MaxSize)) { throw_string_too_large(sl.len); } auto const cc = CapCode::ceil(sl.len); auto const need = cc.decode() + kCapOverhead; auto const sd = static_cast<StringData*>( trueStatic ? low_malloc(need) : malloc(need) ); auto const data = reinterpret_cast<char*>(sd + 1); sd->m_data = data; auto const count = trueStatic ? StaticValue : UncountedValue; sd->m_hdr.init(cc, HeaderKind::String, count); sd->m_len = sl.len; // m_hash is computed soon. data[sl.len] = 0; auto const mcret = memcpy(data, sl.ptr, sl.len); auto const ret = reinterpret_cast<StringData*>(mcret) - 1; // Recalculating ret from mcret avoids a spill. ret->preCompute(); // get m_hash right assert(ret == sd); assert(ret->isFlat()); assert(trueStatic ? ret->isStatic() : ret->isUncounted()); assert(ret->checkSane()); return ret; }
// Allocate a string with length > kMaxStringSimpleLen, initialize `m_data' // and `m_hdr', but not `m_lenAndHash'. We sometimes want to inline this slow // path, too. ALWAYS_INLINE StringData* allocFlatSlowImpl(size_t len) { // Slow path when length is large enough to need the real CapCode encoding. if (UNLIKELY(len > StringData::MaxSize)) { throw_string_too_large(len); } auto const need = CapCode::roundUp(len) + kCapOverhead; StringData* sd; CapCode cc; size_t sizeClass = 0; static_assert(kSmallIndex2Size[0] < kCapOverhead, "Size class 0 indicates shared or big allocations"); if (LIKELY(need <= kMaxSmallSize)) { sizeClass = MemoryManager::computeSmallSize2Index(need); auto const sz = MemoryManager::smallIndex2Size(sizeClass); cc = CapCode::floor(sz - kCapOverhead); sd = static_cast<StringData*>(MM().mallocSmallIndex(sizeClass, sz)); } else { auto const block = MM().mallocBigSize<true>(need); size_t actualCap = block.size - kCapOverhead; if (UNLIKELY(actualCap > StringData::MaxSize)) { actualCap = StringData::MaxSize; } cc = CapCode::floor(static_cast<uint32_t>(actualCap)); sd = static_cast<StringData*>(block.ptr); } assert(cc.decode() >= len); sd->m_data = reinterpret_cast<char*>(sd + 1); // Refcount initialized to 1. sd->m_hdr.init(cc, HeaderKind::String, 1, sizeClass); return sd; }
StringData* StringData::append(StringSlice range) { assert(!hasMultipleRefs()); auto s = range.ptr; auto const len = range.len; if (len == 0) return this; auto const newLen = size_t(m_len) + size_t(len); if (UNLIKELY(newLen > MaxSize)) { throw_string_too_large(newLen); } /* * We may have an aliasing append. We don't allow appending with an * interior pointer, although we may be asked to append less than * the whole string in an aliasing situation. */ ALIASING_APPEND_ASSERT(s, len); auto const requestLen = static_cast<uint32_t>(newLen); auto const target = UNLIKELY(isShared()) ? escalate(requestLen) : reserve(requestLen); memcpy(target->mutableData() + m_len, s, len); target->setSize(newLen); assert(target->checkSane()); return target; }
StringData* StringData::Make(const char* data, size_t len, CopyStringMode) { if (UNLIKELY(len > StringData::MaxSize)) { throw_string_too_large(len); } return Make(StringSlice(data, len), CopyString); }
StringData* StringData::append(StringSlice r1, StringSlice r2, StringSlice r3) { assert(!hasMultipleRefs()); auto const len = r1.len + r2.len + r3.len; if (len == 0) return this; if (UNLIKELY(uint32_t(len) > MaxSize)) { throw_string_too_large(len); } if (UNLIKELY(size_t(m_len) + size_t(len) > MaxSize)) { throw_string_too_large(size_t(len) + size_t(m_len)); } auto const newLen = m_len + len; /* * We may have an aliasing append. We don't allow appending with an * interior pointer, although we may be asked to append less than * the whole string in an aliasing situation. */ ALIASING_APPEND_ASSERT(r1.ptr, r1.len); ALIASING_APPEND_ASSERT(r2.ptr, r2.len); ALIASING_APPEND_ASSERT(r3.ptr, r3.len); auto const target = UNLIKELY(isShared()) ? escalate(newLen) : reserve(newLen); auto const mslice = target->bufferSlice(); /* * memcpy is safe even if it's a self append---the regions will be * disjoint, since rN.ptr can't point past the start of our source * pointer, and rN.len is smaller than the old length. */ void* p = mslice.ptr; p = memcpy((char*)p + m_len, r1.ptr, r1.len); p = memcpy((char*)p + r1.len, r2.ptr, r2.len); memcpy((char*)p + r2.len, r3.ptr, r3.len); target->setSize(newLen); assert(target->checkSane()); return target; }
StringData* StringData::Make(char* data, size_t len, AttachStringMode) { if (UNLIKELY(len > StringData::MaxSize)) { throw_string_too_large(len); } auto const sd = Make(StringSlice(data, len), CopyString); free(data); assert(sd->checkSane()); return sd; }
ALWAYS_INLINE std::pair<StringData*,uint32_t> allocFlatForLen(uint32_t len) { auto const needed = static_cast<uint32_t>(sizeof(StringData) + len + 1); if (LIKELY(needed <= kMaxSmartSize)) { auto const cap = MemoryManager::smartSizeClass(needed); auto const sd = static_cast<StringData*>(MM().smartMallocSizeLogged(cap)); return std::make_pair(sd, cap); } if (UNLIKELY(needed > StringData::MaxSize + sizeof(StringData) + 1)) { throw_string_too_large(len); } auto const cap = needed; auto const ret = MM().smartMallocSizeBigLogged(cap); return std::make_pair(static_cast<StringData*>(ret.first), static_cast<uint32_t>(ret.second)); }
StringData* StringData::append(folly::StringPiece r1, folly::StringPiece r2) { assert(!hasMultipleRefs()); auto const len = r1.size() + r2.size(); if (len == 0) return this; if (UNLIKELY(size_t(m_len) + size_t(len) > MaxSize)) { throw_string_too_large(size_t(len) + size_t(m_len)); } auto const newLen = m_len + len; /* * We may have an aliasing append. We don't allow appending with an * interior pointer, although we may be asked to append less than * the whole string in an aliasing situation. */ ALIASING_APPEND_ASSERT(r1.data(), r1.size()); ALIASING_APPEND_ASSERT(r2.data(), r2.size()); auto const target = UNLIKELY(isProxy()) ? escalate(newLen) : reserve(newLen); /* * memcpy is safe even if it's a self append---the regions will be * disjoint, since rN.data() can't point past the start of our source * pointer, and rN.size() is smaller than the old length. */ void* p = target->mutableData(); p = memcpy((char*)p + m_len, r1.data(), r1.size()); memcpy((char*)p + r1.size(), r2.data(), r2.size()); target->setSize(newLen); assert(target->checkSane()); return target; }
// create either a static or an uncounted string. // Diffrence between static and uncounted is in the lifetime // of the string. Static are alive for the lifetime of the process. // Uncounted are not ref counted but will be deleted at some point. ALWAYS_INLINE StringData* StringData::MakeShared(StringSlice sl, bool trueStatic) { if (UNLIKELY(sl.len > StringData::MaxSize)) { throw_string_too_large(sl.len); } auto const cc = CapCode::ceil(sl.len); auto const need = cc.decode() + kCapOverhead; auto const sd = static_cast<StringData*>( trueStatic ? low_malloc(need) : malloc(need) ); auto const data = reinterpret_cast<char*>(sd + 1); sd->m_data = data; sd->m_hdr.init(cc, HeaderKind::String, 0); sd->m_lenAndHash = sl.len; // hash=0 data[sl.len] = 0; auto const mcret = memcpy(data, sl.ptr, sl.len); auto const ret = reinterpret_cast<StringData*>(mcret) - 1; // Recalculating ret from mcret avoids a spill. assert(ret->m_hash == 0); assert(ret->getCount() == 0); if (trueStatic) { ret->setStatic(); } else { ret->setUncounted(); } assert(ret == sd); assert(ret->isFlat()); assert(trueStatic ? ret->isStatic() : ret->isUncounted()); assert(ret->checkSane()); return ret; }
// create either a static or an uncounted string. // Diffrence between static and uncounted is in the lifetime // of the string. Static are alive for the lifetime of the process. // Uncounted are not ref counted but will be deleted at some point. StringData* StringData::MakeShared(StringSlice sl, bool trueStatic) { if (UNLIKELY(sl.len > StringData::MaxSize)) { throw_string_too_large(sl.len); } auto const encodable = roundUpPackedCap(sl.len); auto const need = encodable + kCapOverhead; auto const sd = static_cast<StringData*>( trueStatic ? low_malloc(need) : malloc(need) ); auto const data = reinterpret_cast<char*>(sd + 1); auto const capCode = packedCapToCode(encodable); sd->m_data = data; sd->m_capAndCount = HeaderKind::String << 24 | capCode; // count=0 sd->m_lenAndHash = sl.len; // hash=0 data[sl.len] = 0; auto const mcret = memcpy(data, sl.ptr, sl.len); auto const ret = reinterpret_cast<StringData*>(mcret) - 1; // Recalculating ret from mcret avoids a spill. assert(ret->m_hash == 0); assert(ret->m_count == 0); if (trueStatic) { ret->setStatic(); } else { ret->setUncounted(); } assert(ret == sd); assert(ret->isFlat()); assert(trueStatic ? ret->isStatic() : ret->isUncounted()); assert(ret->checkSane()); return ret; }
void StringData::incrementHelper() { m_hash = 0; enum class CharKind { UNKNOWN, LOWER_CASE, UPPER_CASE, NUMERIC }; auto const len = m_len; auto const s = m_data; int carry = 0; int pos = len - 1; auto last = CharKind::UNKNOWN; // Shut up the compiler warning int ch; while (pos >= 0) { ch = s[pos]; if (ch >= 'a' && ch <= 'z') { if (ch == 'z') { s[pos] = 'a'; carry=1; } else { s[pos]++; carry=0; } last = CharKind::LOWER_CASE; } else if (ch >= 'A' && ch <= 'Z') { if (ch == 'Z') { s[pos] = 'A'; carry=1; } else { s[pos]++; carry=0; } last = CharKind::UPPER_CASE; } else if (ch >= '0' && ch <= '9') { if (ch == '9') { s[pos] = '0'; carry=1; } else { s[pos]++; carry=0; } last = CharKind::NUMERIC; } else { carry=0; break; } if (carry == 0) { break; } pos--; } if (carry) { if (UNLIKELY(len + 1 > MaxSize)) { throw_string_too_large(len); } assert(len + 1 <= capacity()); memmove(s + 1, s, len); s[len + 1] = '\0'; m_len = len + 1; switch (last) { case CharKind::NUMERIC: s[0] = '1'; break; case CharKind::UPPER_CASE: s[0] = 'A'; break; case CharKind::LOWER_CASE: s[0] = 'a'; break; default: break; } } }