Example #1
0
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;
}
Example #2
0
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;
}
Example #3
0
// 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;
}
Example #4
0
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;
}
Example #5
0
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;
}
Example #6
0
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;
}
Example #7
0
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;
}
Example #8
0
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;
        }
    }
}
Example #9
0
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));
}