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::shrinkImpl(size_t len) { assert(!isImmutable() && !hasMultipleRefs()); assert(isFlat()); assert(len <= capacity()); auto const sd = Make(len); sd->m_len = len; auto const src = slice(); auto const dst = sd->mutableData(); *memcpy8(dst, src.ptr, len) = 0; assert(sd->checkSane()); return sd; }
// State transition from Mode::Shared to Mode::Flat. StringData* StringData::escalate(size_t cap) { assert(isShared() && !isStatic() && cap >= m_len); auto const sd = Make(cap); auto const src = slice(); auto const dst = sd->mutableData(); sd->setSize(src.len); auto const mcret = memcpy(dst, src.ptr, src.len); auto const ret = static_cast<StringData*>(mcret) - 1; // Recalculating ret from mcret avoids a spill. assert(ret == sd); assert(ret->checkSane()); return ret; }
QPpsAttributeMap QPpsObjectPrivate::decode(const QByteArray &rawData, bool *ok) { QPpsAttributeMap attributeMap; pps_decoder_t decoder; QByteArray mutableData(rawData); pps_decoder_error_t error = pps_decoder_initialize(&decoder, mutableData.data()); if (error == PPS_DECODER_OK) { // no need to check ok in this case attributeMap = decodeObject(&decoder, ok); } else { qWarning() << "QPpsObjectPrivate::decode: pps_decoder_initialize failed"; *ok = false; } pps_decoder_cleanup(&decoder); return attributeMap; }
StringData* StringData::shrinkImpl(size_t len) { assert(!isImmutable() && !hasMultipleRefs()); assert(isFlat()); assert(len <= m_len); assert(len <= capacity()); auto const sd = Make(len); auto const src = slice(); auto const dst = sd->mutableData(); assert(len <= src.len); sd->setSize(len); auto const mcret = memcpy(dst, src.ptr, len); auto const ret = static_cast<StringData*>(mcret) - 1; // Recalculating ret from mcret avoids a spill. assert(ret == sd); assert(ret->checkSane()); return ret; }
StringData* StringData::reserve(size_t cap) { assert(!isImmutable() && !hasMultipleRefs()); assert(isFlat()); if (cap <= capacity()) return this; cap = std::min(cap + cap/4, size_t(MaxSize) + 1); auto const sd = Make(cap); auto const src = slice(); auto const dst = sd->mutableData(); sd->setSize(src.len); auto const mcret = memcpy(dst, src.ptr, src.len); auto const ret = static_cast<StringData*>(mcret) - 1; // Recalculating ret from mcret avoids a spill. assert(ret == sd); assert(ret->checkSane()); return ret; }
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(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); /* * 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 = target->mutableData(); 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; }
void StringData::incrementHelper() { m_hash = 0; enum class CharKind { UNKNOWN, LOWER_CASE, UPPER_CASE, NUMERIC }; auto const len = m_len; auto const s = mutableData(); 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; } } }
Variant HHVM_FUNCTION(wordwrap, const String& str, int64_t linewidth /* = 75 */, const String& brk /* = s_nl */, bool cut /* = false */) { const char* brkstr = brk.data(); size_t textlen = str.size(); size_t brklen = brk.size(); if (textlen == 0) { return empty_string(); } if (brklen == 0) { raise_warning("Break string cannot be empty"); return false; } if (linewidth == 0 && cut) { raise_warning("Can't force cut when width is zero"); return false; } size_t w = linewidth >= 0 ? linewidth : 0; // If the string's length is less than or equal to the specified // width, there's nothing to do and we can just return the string. if (textlen <= w) return str; // Special case for a single-character break as it needs no // additional storage space if (brklen == 1 && !cut) { auto new_sd = StringData::Make(str.get(), CopyString); new_sd->invalidateHash(); auto ret = Variant::attach(new_sd); char* newtext = new_sd->mutableData(); auto bc = brkstr[0]; size_t current = 0, laststart = 0, lastspace = 0; for (; current < textlen; current++) { if (newtext[current] == bc) { laststart = lastspace = current + 1; } else if (newtext[current] == ' ') { if (current - laststart >= w) { newtext[current] = bc; laststart = current + 1; } lastspace = current; } else if (current - laststart >= w && laststart != lastspace) { newtext[lastspace] = bc; laststart = lastspace + 1; } } return ret; } // Multiple character line break or forced cut // Estimate how big the output string will be. It's okay if this estimate // is wrong as we will grow or shrink as needed. The goals here are two- // fold: (1) avoid the need to grow or shrink in the common case, and // (2) for extreme cases where it's hard to make an accurate estimate // (ex. when w is very small or brk is very large) we should be careful // to avoid making huge over-estimations. StringBuffer strbuf( textlen + textlen / (std::max<size_t>(w, 16) - 8) * std::min<size_t>(brklen, 8)); const char* text = str.data(); size_t current = 0, laststart = 0, lastspace = 0; for (; current < textlen; current++) { // when we hit an existing break, copy to new buffer, and // fix up laststart and lastspace if (text[current] == brkstr[0] && current + brklen < textlen && !strncmp(text + current, brkstr, brklen)) { strbuf.append(text + laststart, current - laststart + brklen); current += brklen - 1; laststart = lastspace = current + 1; } // if it is a space, check if it is at the line boundary, // copy and insert a break, or just keep track of it else if (text[current] == ' ') { if (current - laststart >= w) { strbuf.append(text + laststart, current - laststart); strbuf.append(brkstr, brklen); laststart = current + 1; } lastspace = current; } // if we are cutting, and we've accumulated enough // characters, and we haven't see a space for this line, // copy and insert a break. else if (current - laststart >= w && cut && laststart >= lastspace) { strbuf.append(text + laststart, current - laststart); strbuf.append(brkstr, brklen); laststart = lastspace = current; } // if the current word puts us over width w, copy back up // until the last space, insert a break, and move up the // laststart else if (current - laststart >= w && laststart < lastspace) { strbuf.append(text + laststart, lastspace - laststart); strbuf.append(brkstr, brklen); laststart = lastspace = lastspace + 1; } } // copy over any stragglers if (laststart != current) { strbuf.append(text + laststart, current - laststart); } auto s = strbuf.detach(); // if it's not possible to reduce the output string's capacity by more // than 25%, then we can just return the string as is. size_t estShrinkCap = MemoryManager::estimateCap(sizeof(StringData) + s.size() + 1); if (estShrinkCap * 4 >= (size_t)s.capacity() * 3) { return s; } // reallocate into a smaller buffer so that we don't waste memory return Variant::attach(StringData::Make(s.get(), CopyString)); }