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