2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/string-data.h"
22 #include "hphp/runtime/base/tv-conv-notice.h"
23 #include "hphp/util/alloc.h"
24 #include "hphp/util/safe-cast.h"
25 #include "hphp/util/stack-trace.h"
27 #include "hphp/runtime/base/apc-handle-defs.h"
28 #include "hphp/runtime/base/apc-string.h"
29 #include "hphp/runtime/base/builtin-functions.h"
30 #include "hphp/runtime/base/exceptions.h"
31 #include "hphp/runtime/base/runtime-error.h"
32 #include "hphp/runtime/base/runtime-option.h"
33 #include "hphp/runtime/base/tv-uncounted.h"
34 #include "hphp/runtime/base/zend-functions.h"
35 #include "hphp/runtime/base/zend-string.h"
36 #include "hphp/runtime/ext/apc/ext_apc.h"
38 #include "hphp/runtime/vm/unit-emitter.h"
40 #include "hphp/zend/zend-strtod.h"
46 //////////////////////////////////////////////////////////////////////
48 bool isame_log(const StringData
* input
, const StringData
* arg
) {
49 FTRACE(1, "isame collision {} != {}\n", input
->slice(), arg
->slice());
50 auto const rate
= RO::EvalIsameCollisionSampleRate
;
51 if (StructuredLog::coinflip(rate
)) {
52 StructuredLogEntry sample
;
53 sample
.force_init
= true;
54 sample
.setInt("sample_rate", rate
);
55 sample
.setStr("lhs", input
->slice());
56 sample
.setStr("rhs", arg
->slice());
57 StackTrace
st(StackTrace::Force
{});
58 sample
.setStackTrace("stack", st
);
59 StructuredLog::log("hhvm_isame_collisions", sample
);
64 NEVER_INLINE
void raiseStringLengthExceededError(size_t len
) {
65 raise_error("String length exceeded: %zu > %u", len
, StringData::MaxSize
);
68 // Allocate, initialize `m_data' and HeapObject, but not `m_lenAndHash'.
69 ALWAYS_INLINE StringData
* allocFlat(size_t len
) {
70 if (UNLIKELY(len
> StringData::MaxSize
)) {
71 raiseStringLengthExceededError(len
);
73 auto const sizeIndex
= MemoryManager::size2Index(len
+ kStringOverhead
);
74 auto sd
= static_cast<StringData
*>(tl_heap
->objMallocIndex(sizeIndex
));
75 // Refcount initialized to 1.
76 sd
->initHeader_16(HeaderKind::String
, OneReference
, sizeIndex
);
77 assertx(sd
->capacity() >= len
);
81 //////////////////////////////////////////////////////////////////////
86 >::type s_theEmptyString
;
88 //////////////////////////////////////////////////////////////////////
91 std::atomic
<bool> s_symbols_loaded
;
93 SymbolPrefix
* getSymbolPrefix(StringData
* sd
) {
94 assertx(sd
->isSymbol());
95 return reinterpret_cast<SymbolPrefix
*>(sd
) - 1;
97 const SymbolPrefix
* getSymbolPrefix(const StringData
* sd
) {
98 assertx(sd
->isSymbol());
99 return getSymbolPrefix(const_cast<StringData
*>(sd
));
103 bool StringData::isSymbol() const {
104 return (m_aux16
>> 8) & kIsSymbolMask
;
107 void StringData::markSymbolsLoaded() {
108 s_symbols_loaded
.store(true, std::memory_order_release
);
111 Class
* StringData::getCachedClass() const {
112 return getSymbolPrefix(this)->cls
;
115 NamedEntity
* StringData::getNamedEntity() const {
116 return getSymbolPrefix(this)->ne
;
119 void StringData::setCachedClass(Class
* cls
) {
120 auto const prefix
= getSymbolPrefix(this);
121 assertx(IMPLIES(prefix
->cls
, prefix
->cls
== cls
));
125 void StringData::setNamedEntity(NamedEntity
* ne
) {
126 auto const prefix
= getSymbolPrefix(this);
127 assertx(IMPLIES(prefix
->ne
, prefix
->ne
== ne
));
131 ptrdiff_t StringData::isSymbolOffset() {
132 return offsetof(StringData
, m_aux16
) + 1;
135 ptrdiff_t StringData::cachedClassOffset() {
136 return offsetof(SymbolPrefix
, cls
) - sizeof(SymbolPrefix
);
139 //////////////////////////////////////////////////////////////////////
141 ptrdiff_t StringData::colorOffset() {
142 return offsetof(StringData
, m_aux16
);
145 uint16_t StringData::color() const {
146 return m_aux16
& kColorMask
;
149 void StringData::setColor(uint16_t color
) {
150 assertx((color
& ~kColorMask
) == 0);
154 //////////////////////////////////////////////////////////////////////
156 // Create either a static or an uncounted string.
157 // Difference between static and uncounted is in the lifetime
158 // of the string. Static are alive for the lifetime of the process.
159 // Uncounted are not ref counted but will be deleted at some point.
160 template <bool trueStatic
> ALWAYS_INLINE
161 MemBlock
StringData::AllocateShared(folly::StringPiece sl
) {
162 if (UNLIKELY(sl
.size() > StringData::MaxSize
)) {
163 raiseStringLengthExceededError(sl
.size());
167 trueStatic
&& !s_symbols_loaded
.load(std::memory_order_acquire
);
169 auto const extra
= symbol
? sizeof(SymbolPrefix
) : 0;
170 auto const bytes
= sl
.size() + kStringOverhead
+ extra
;
171 auto const ptr
= trueStatic
? static_alloc(bytes
) : AllocUncounted(bytes
);
172 return MemBlock
{ptr
, bytes
};
175 template <bool trueStatic
> ALWAYS_INLINE
176 StringData
* StringData::MakeSharedAt(folly::StringPiece sl
, MemBlock range
) {
177 assertx(range
.size
>= sl
.size() + kStringOverhead
);
178 auto const symbol
= trueStatic
&&
179 !s_symbols_loaded
.load(std::memory_order_acquire
) &&
180 (range
.size
>= sl
.size() + kStringOverhead
+ sizeof(SymbolPrefix
));
181 auto const extra
= symbol
? sizeof(SymbolPrefix
) : 0;
182 StringData
* sd
= reinterpret_cast<StringData
*>(
183 reinterpret_cast<uintptr_t>(range
.ptr
) + extra
185 auto const data
= reinterpret_cast<char*>(sd
+ 1);
187 auto const count
= trueStatic
? StaticValue
: UncountedValue
;
189 auto constexpr aux
= kIsSymbolMask
<< 8 | kInvalidColor
;
190 sd
->initHeader_16(HeaderKind::String
, count
, aux
);
191 getSymbolPrefix(sd
)->cls
= nullptr;
192 getSymbolPrefix(sd
)->ne
= nullptr;
194 sd
->initHeader_16(HeaderKind::String
, count
, kInvalidColor
);
196 sd
->m_len
= sl
.size(); // m_hash is computed soon.
199 auto const mcret
= memcpy(data
, sl
.data(), sl
.size());
200 auto const ret
= reinterpret_cast<StringData
*>(mcret
) - 1;
201 // Recalculating ret from mcret avoids a spill.
202 ret
->preCompute(); // get m_hash right
205 assertx(trueStatic
? ret
->isStatic() : ret
->isUncounted());
206 assertx(ret
->isSymbol() == symbol
);
207 assertx(ret
->checkSane());
211 StringData
* StringData::MakeStaticAt(folly::StringPiece sl
, MemBlock range
) {
212 return MakeSharedAt
<true>(sl
, range
);
215 StringData
* StringData::MakeStatic(folly::StringPiece sl
) {
216 assertx(StaticString::s_globalInit
);
217 return MakeStaticAt(sl
, AllocateShared
<true>(sl
));
220 StringData
* StringData::MakeUncounted(folly::StringPiece sl
) {
221 return MakeSharedAt
<false>(sl
, AllocateShared
<false>(sl
));
224 StringData
* StringData::MakeEmpty() {
225 return MakeStaticAt(folly::StringPiece
{""},
226 MemBlock
{&s_theEmptyString
, sizeof(s_theEmptyString
)});
229 void StringData::destructStatic() {
230 assertx(checkSane() && isStatic());
232 static_try_free(reinterpret_cast<SymbolPrefix
*>(this) - 1,
233 size() + kStringOverhead
+ sizeof(SymbolPrefix
));
235 static_try_free(this, size() + kStringOverhead
);
239 void StringData::ReleaseUncounted(StringData
* str
) {
240 assertx(str
->checkSane());
241 assertx(!str
->uncountedCowCheck());
242 FreeUncounted(str
, str
->size() + kStringOverhead
);
245 //////////////////////////////////////////////////////////////////////
247 StringData
* StringData::Make(const StringData
* s
, CopyStringMode
) {
248 auto const sd
= allocFlat(s
->m_len
);
249 sd
->m_lenAndHash
= s
->m_lenAndHash
;
250 auto const data
= static_cast<void*>(sd
+ 1);
251 *memcpy8(data
, s
->data(), s
->m_len
) = 0;
253 assertx(sd
->same(s
));
257 StringData
* StringData::Make(folly::StringPiece sl
, CopyStringMode
) {
258 auto const sd
= allocFlat(sl
.size());
259 sd
->m_lenAndHash
= sl
.size(); // hash=0
260 auto const data
= reinterpret_cast<char*>(sd
+ 1);
263 auto const mcret
= memcpy(data
, sl
.data(), sl
.size());
264 auto const ret
= reinterpret_cast<StringData
*>(mcret
) - 1;
265 // Recalculating ret from mcret avoids a spill.
268 assertx(ret
->m_len
== sl
.size());
269 assertx(ret
->hasExactlyOneRef());
270 assertx(ret
->m_hash
== 0);
271 assertx(ret
->checkSane());
275 StringData
* StringData::Make(const char* data
, size_t len
, CopyStringMode
) {
276 if (UNLIKELY(len
> StringData::MaxSize
)) {
277 raiseStringLengthExceededError(len
);
280 return Make(folly::StringPiece(data
, len
), CopyString
);
283 StringData
* StringData::Make(size_t reserveLen
) {
284 auto const sd
= allocFlat(reserveLen
);
287 assertx(sd
->hasExactlyOneRef());
288 assertx(sd
->checkSane());
292 StringData
* StringData::Make() {
293 return Make(SmallStringReserve
);
296 //////////////////////////////////////////////////////////////////////
298 StringData
* StringData::Make(char* data
, size_t len
, AttachStringMode
) {
299 if (UNLIKELY(len
> StringData::MaxSize
)) {
300 raiseStringLengthExceededError(len
);
302 auto const sd
= Make(folly::StringPiece(data
, len
), CopyString
);
304 assertx(sd
->checkSane());
308 StringData
* StringData::Make(folly::StringPiece r1
, folly::StringPiece r2
) {
309 // Undefined behavior if we pass nullptr strings into StringData::Make
310 assertx(r1
.data() && r2
.data());
311 auto const len
= r1
.size() + r2
.size();
312 auto const sd
= allocFlat(len
);
313 sd
->m_lenAndHash
= len
; // hash=0
315 auto const data
= reinterpret_cast<char*>(sd
+ 1);
316 memcpy(data
, r1
.data(), r1
.size());
317 memcpy(data
+ r1
.size(), r2
.data(), r2
.size());
320 assertx(sd
->hasExactlyOneRef());
321 assertx(sd
->checkSane());
325 StringData
* StringData::Make(const StringData
* s1
, const StringData
* s2
) {
326 auto const len
= s1
->m_len
+ s2
->m_len
;
327 // `memcpy8()' could overrun the buffer by at most 7 bytes, so we allocate 6
328 // more bytes here, which (together with the trailing 0) makes it safe.
329 auto const sd
= allocFlat(len
+ 6);
330 sd
->m_lenAndHash
= len
; // hash=0
332 auto const data
= reinterpret_cast<char*>(sd
+ 1);
333 auto const next
= memcpy8(data
, s1
->data(), s1
->m_len
);
334 *memcpy8(next
, s2
->data(), s2
->m_len
) = 0;
336 assertx(sd
->hasExactlyOneRef());
337 assertx(sd
->checkSane());
341 StringData
* StringData::Make(folly::StringPiece s1
, const char* lit2
) {
342 return Make(s1
, folly::StringPiece(lit2
, strlen(lit2
)));
345 StringData
* StringData::Make(folly::StringPiece r1
, folly::StringPiece r2
,
346 folly::StringPiece r3
) {
347 auto const len
= r1
.size() + r2
.size() + r3
.size();
348 auto const sd
= allocFlat(len
);
349 sd
->m_lenAndHash
= len
; // hash=0
351 auto p
= reinterpret_cast<char*>(sd
+ 1);
352 p
= static_cast<char*>(memcpy(p
, r1
.data(), r1
.size()));
353 p
= static_cast<char*>(memcpy(p
+ r1
.size(), r2
.data(), r2
.size()));
354 p
= static_cast<char*>(memcpy(p
+ r2
.size(), r3
.data(), r3
.size()));
357 assertx(sd
->hasExactlyOneRef());
358 assertx(sd
->checkSane());
362 StringData
* StringData::Make(folly::StringPiece r1
, folly::StringPiece r2
,
363 folly::StringPiece r3
, folly::StringPiece r4
) {
364 auto const len
= r1
.size() + r2
.size() + r3
.size() + r4
.size();
365 auto const sd
= allocFlat(len
);
366 sd
->m_lenAndHash
= len
; // hash=0
368 auto p
= reinterpret_cast<char*>(sd
+ 1);
369 p
= static_cast<char*>(memcpy(p
, r1
.data(), r1
.size()));
370 p
= static_cast<char*>(memcpy(p
+ r1
.size(), r2
.data(), r2
.size()));
371 p
= static_cast<char*>(memcpy(p
+ r2
.size(), r3
.data(), r3
.size()));
372 p
= static_cast<char*>(memcpy(p
+ r3
.size(), r4
.data(), r4
.size()));
375 assertx(sd
->hasExactlyOneRef());
376 assertx(sd
->checkSane());
380 //////////////////////////////////////////////////////////////////////
382 void StringData::release() noexcept
{
383 fixCountForRelease();
384 assertx(isRefCounted());
385 assertx(checkSane());
386 tl_heap
->objFreeIndex(this, m_aux16
);
387 AARCH64_WALKABLE_FRAME();
390 //////////////////////////////////////////////////////////////////////
392 #define ALIASING_APPEND_ASSERT(ptr, len) \
393 assertx(uintptr_t(ptr) <= uintptr_t(data()) || \
394 uintptr_t(ptr) >= uintptr_t(data() + capacity() + 1)); \
395 assertx(ptr != data() || len <= m_len);
397 StringData
* StringData::append(folly::StringPiece range
) {
398 assertx(!hasMultipleRefs());
400 auto s
= range
.data();
401 auto const len
= range
.size();
402 if (len
== 0) return this;
403 auto const newLen
= size_t(m_len
) + size_t(len
);
405 if (UNLIKELY(newLen
> MaxSize
)) {
406 raiseStringLengthExceededError(newLen
);
410 * We may have an aliasing append. We don't allow appending with an
411 * interior pointer, although we may be asked to append less than
412 * the whole string in an aliasing situation.
414 ALIASING_APPEND_ASSERT(s
, len
);
416 auto const requestLen
= static_cast<uint32_t>(newLen
);
417 auto const target
= reserve(requestLen
);
418 memcpy(target
->mutableData() + m_len
, s
, len
);
419 target
->setSize(newLen
);
420 assertx(target
->checkSane());
425 StringData
* StringData::append(folly::StringPiece r1
, folly::StringPiece r2
) {
426 assertx(!hasMultipleRefs());
428 auto const len
= r1
.size() + r2
.size();
430 if (len
== 0) return this;
431 if (UNLIKELY(size_t(m_len
) + size_t(len
) > MaxSize
)) {
432 raiseStringLengthExceededError(size_t(len
) + size_t(m_len
));
435 auto const newLen
= m_len
+ len
;
438 * We may have an aliasing append. We don't allow appending with an
439 * interior pointer, although we may be asked to append less than
440 * the whole string in an aliasing situation.
442 ALIASING_APPEND_ASSERT(r1
.data(), r1
.size());
443 ALIASING_APPEND_ASSERT(r2
.data(), r2
.size());
444 auto const target
= reserve(newLen
);
447 * memcpy is safe even if it's a self append---the regions will be
448 * disjoint, since rN.data() can't point past the start of our source
449 * pointer, and rN.size() is smaller than the old length.
451 void* p
= target
->mutableData();
452 p
= memcpy((char*)p
+ m_len
, r1
.data(), r1
.size());
453 memcpy((char*)p
+ r1
.size(), r2
.data(), r2
.size());
455 target
->setSize(newLen
);
456 assertx(target
->checkSane());
461 StringData
* StringData::append(folly::StringPiece r1
,
462 folly::StringPiece r2
,
463 folly::StringPiece r3
) {
464 assertx(!hasMultipleRefs());
466 auto const len
= r1
.size() + r2
.size() + r3
.size();
468 if (len
== 0) return this;
469 if (UNLIKELY(size_t(m_len
) + size_t(len
) > MaxSize
)) {
470 raiseStringLengthExceededError(size_t(len
) + size_t(m_len
));
473 auto const newLen
= m_len
+ len
;
476 * We may have an aliasing append. We don't allow appending with an
477 * interior pointer, although we may be asked to append less than
478 * the whole string in an aliasing situation.
480 ALIASING_APPEND_ASSERT(r1
.data(), r1
.size());
481 ALIASING_APPEND_ASSERT(r2
.data(), r2
.size());
482 ALIASING_APPEND_ASSERT(r3
.data(), r3
.size());
483 auto const target
= reserve(newLen
);
486 * memcpy is safe even if it's a self append---the regions will be
487 * disjoint, since rN.data() can't point past the start of our source
488 * pointer, and rN.size() is smaller than the old length.
490 void* p
= target
->mutableData();
491 p
= memcpy((char*)p
+ m_len
, r1
.data(), r1
.size());
492 p
= memcpy((char*)p
+ r1
.size(), r2
.data(), r2
.size());
493 memcpy((char*)p
+ r2
.size(), r3
.data(), r3
.size());
495 target
->setSize(newLen
);
496 assertx(target
->checkSane());
501 #undef ALIASING_APPEND_ASSERT
503 //////////////////////////////////////////////////////////////////////
505 StringData
* StringData::reserve(size_t cap
) {
506 assertx(!hasMultipleRefs());
508 if (cap
<= capacity()) return this;
510 cap
= std::min(cap
+ cap
/ 4, size_t(MaxSize
));
511 auto const sd
= allocFlat(cap
);
513 // Request-allocated StringData are always aligned at 16 bytes, thus it is
514 // safe to copy in 16-byte groups.
515 // layout: [header][m_lenAndHash][...data]
516 sd
->m_lenAndHash
= m_lenAndHash
;
517 // This copies the characters (m_len bytes), and the trailing zero (1 byte)
518 memcpy16_inline(sd
+1, this+1, (m_len
+ 1 + 15) & ~0xF);
519 assertx(reinterpret_cast<uintptr_t>(this+1) % 16 == 0);
521 assertx(sd
->hasExactlyOneRef());
522 assertx(sd
->checkSane());
526 StringData
* StringData::shrinkImpl(size_t len
) {
527 assertx(!hasMultipleRefs());
528 assertx(len
<= capacity());
530 auto const sd
= allocFlat(len
);
531 sd
->m_lenAndHash
= len
;
532 auto const src
= static_cast<void*>(this + 1);
533 auto const dst
= static_cast<void*>(sd
+ 1);
534 *memcpy8(dst
, src
, len
) = 0;
536 assertx(sd
->checkSane());
540 void StringData::dump() const {
543 printf("StringData(%d) (%s%s%d): [", m_count
,
544 isStatic() ? "static " : "",
545 isUncounted() ? "uncounted " : "",
546 static_cast<int>(s
.size()));
547 for (uint32_t i
= 0; i
< s
.size(); i
++) {
548 char ch
= s
.data()[i
];
552 printf("\\x%02x", ch
);
558 StringData
* StringData::getChar(int offset
) const {
559 if (offset
>= 0 && offset
< size()) {
560 return makeStaticString(data()[offset
]);
562 raise_notice("Uninitialized string offset: %d", offset
);
563 return staticEmptyString();
566 StringData
* StringData::increment() {
567 assertx(!hasMultipleRefs());
569 auto const sd
= reserve(m_len
+ 1);
570 sd
->incrementHelper();
574 void StringData::incrementHelper() {
575 raise_notice("Increment on string '%s'", data());
578 enum class CharKind
{
585 auto const len
= m_len
;
586 auto const s
= mutableData();
589 auto last
= CharKind::UNKNOWN
; // Shut up the compiler warning
594 if (ch
>= 'a' && ch
<= 'z') {
602 last
= CharKind::LOWER_CASE
;
603 } else if (ch
>= 'A' && ch
<= 'Z') {
611 last
= CharKind::UPPER_CASE
;
612 } else if (ch
>= '0' && ch
<= '9') {
620 last
= CharKind::NUMERIC
;
632 if (UNLIKELY(len
+ 1 > MaxSize
)) {
633 raiseStringLengthExceededError(len
+ 1);
636 assertx(len
+ 1 <= capacity());
637 memmove(s
+ 1, s
, len
);
642 case CharKind::NUMERIC
:
645 case CharKind::UPPER_CASE
:
648 case CharKind::LOWER_CASE
:
657 void StringData::preCompute() {
659 m_hash
= hash_string_i_unsafe(s
.data(), s
.size());
660 assertx(m_hash
>= 0);
662 (is_numeric_string(s
.data(), s
.size(), nullptr, nullptr,
663 1, nullptr) == KindOfNull
)) {
664 m_hash
|= STRHASH_MSB
;
668 #if !defined(USE_X86_STRING_HELPERS) && !defined(USE_ARM_STRING_HELPERS)
669 // This function is implemented directly in ASM in string-data-*.S otherwise.
670 NEVER_INLINE strhash_t
StringData::hashHelper() const {
671 strhash_t h
= hash_string_i_unsafe(data(), m_len
);
678 ///////////////////////////////////////////////////////////////////////////////
681 DataType
StringData::isNumericWithVal(int64_t &lval
, double &dval
,
682 int allow_errors
, int* overflow
) const {
683 if (m_hash
< 0) return KindOfNull
;
684 DataType ret
= KindOfNull
;
687 ret
= is_numeric_string(
695 if (ret
== KindOfNull
&& allow_errors
) {
696 m_hash
|= STRHASH_MSB
;
702 bool StringData::isNumeric() const {
703 if (m_hash
< 0) return false;
704 int64_t lval
; double dval
;
705 DataType ret
= isNumericWithVal(lval
, dval
, 0);
714 case KindOfPersistentString
:
716 case KindOfPersistentVec
:
718 case KindOfPersistentDict
:
720 case KindOfPersistentKeyset
:
727 case KindOfLazyClass
:
735 bool StringData::toBoolean() const {
736 return !empty() && !isZero();
739 int64_t StringData::toInt64(int base
/* = 10 */) const {
740 return strtoll(data(), nullptr, base
);
743 double StringData::toDouble() const {
745 if (s
.size()) return zend_strtod(s
.data(), nullptr);
749 DataType
StringData::toNumeric(int64_t &lval
, double &dval
) const {
750 if (m_hash
< 0) return KindOfString
;
751 DataType ret
= isNumericWithVal(lval
, dval
, 0);
752 if (ret
== KindOfInt64
|| ret
== KindOfDouble
) return ret
;
756 ///////////////////////////////////////////////////////////////////////////////
759 bool StringData::equal(const StringData
*s
) const {
761 if (s
== this) return true;
765 int StringData::numericCompare(const StringData
*v2
) const {
769 int64_t lval1
, lval2
;
772 if ((ret1
= isNumericWithVal(lval1
, dval1
, 0, &oflow1
)) == KindOfNull
||
773 (ret1
== KindOfDouble
&& !std::isfinite(dval1
)) ||
774 (ret2
= v2
->isNumericWithVal(lval2
, dval2
, 0, &oflow2
)) == KindOfNull
||
775 (ret2
== KindOfDouble
&& !std::isfinite(dval2
))) {
778 if (oflow1
&& oflow1
== oflow2
&& dval1
== dval2
) {
779 return -2; // overflow in same direction, comparison will be inaccurate
781 if (ret1
== KindOfInt64
&& ret2
== KindOfInt64
) {
782 if (lval1
> lval2
) return 1;
783 if (lval1
== lval2
) return 0;
786 if (ret1
== KindOfDouble
&& ret2
== KindOfDouble
) {
787 if (dval1
> dval2
) return 1;
788 if (dval1
== dval2
) return 0;
792 if (ret1
== KindOfDouble
) {
793 assertx(ret2
== KindOfInt64
);
797 dval2
= (double)lval2
;
799 assertx(ret1
== KindOfInt64
);
800 assertx(ret2
== KindOfDouble
);
804 dval1
= (double)lval1
;
807 if (dval1
> dval2
) return 1;
808 if (dval1
== dval2
) return 0;
812 int StringData::compare(const StringData
*v2
) const {
815 if (v2
== this) return 0;
817 int ret
= numericCompare(v2
);
820 int len2
= v2
->size();
821 int len
= len1
< len2
? len1
: len2
;
822 ret
= memcmp(data(), v2
->data(), len
);
824 if (len1
== len2
) return 0;
825 return len
< len1
? 1 : -1;
831 StringData::substr(int start
, int length
/* = StringData::MaxSize */) {
832 if (start
< 0 || start
>= size() || length
<= 0) {
833 return staticEmptyString();
836 auto const max_len
= size() - start
;
837 if (length
> max_len
) {
842 if (UNLIKELY(length
== size())) {
846 if (UNLIKELY(length
== 1)) {
847 return makeStaticString(data()[start
]);
849 return StringData::Make(data() + start
, length
, CopyString
);
852 ///////////////////////////////////////////////////////////////////////////////
855 __thread UnitEmitter
* BlobEncoderHelper
<const StringData
*>::tl_unitEmitter
{nullptr};
856 __thread Unit
* BlobEncoderHelper
<const StringData
*>::tl_unit
{nullptr};
858 void BlobEncoderHelper
<const StringData
*>::serde(BlobEncoder
& encoder
,
859 const StringData
* sd
) {
861 if (auto const ue
= tl_unitEmitter
) {
862 Id id
= ue
->mergeLitstr(sd
);
867 encoder(uint32_t(0));
870 auto const sz
= sd
->size();
871 encoder(static_cast<uint32_t>(sz
+ 1));
873 encoder
.writeRaw(sd
->data(), sz
);
876 void BlobEncoderHelper
<const StringData
*>::serde(BlobDecoder
& decoder
,
877 const StringData
*& sd
,
879 if (auto const ue
= tl_unitEmitter
) {
882 sd
= ue
->lookupLitstr(id
);
884 } else if (auto const u
= tl_unit
) {
887 sd
= u
->lookupLitstrId(id
);
899 sd
= staticEmptyString();
903 assertx(decoder
.remaining() >= size
);
904 auto const data
= decoder
.data();
906 ? makeStaticString((const char*)data
, size
)
907 : StringData::Make((const char*)data
, size
, CopyString
);
908 decoder
.advance(size
);
912 BlobEncoderHelper
<const StringData
*>::asStringPiece(BlobDecoder
& decoder
) {
913 if (auto const ue
= tl_unitEmitter
) {
916 auto const sd
= ue
->lookupLitstr(id
);
917 if (!sd
) return { nullptr, size_t{0} };
923 if (size
== 0) return { (const char*)nullptr, size_t{0} };
924 auto const data
= reinterpret_cast<const char*>(decoder
.data());
925 if (size
== 1) return { data
, size_t{0} };
927 assertx(decoder
.remaining() >= size
);
928 decoder
.advance(size
);
929 return { data
, size
};
932 void BlobEncoderHelper
<const StringData
*>::skip(BlobDecoder
& decoder
) {
933 if (auto const ue
= tl_unitEmitter
) {
941 // Any size less than 2 has no data (0 is a nullptr, and 1 is an
943 if (size
<= 1) return;
945 assertx(decoder
.remaining() >= size
);
946 decoder
.advance(size
);
949 size_t BlobEncoderHelper
<const StringData
*>::peekSize(BlobDecoder
& decoder
) {
950 if (auto const ue
= tl_unitEmitter
) {
951 auto const before
= decoder
.advanced();
954 auto const size
= decoder
.advanced() - before
;
955 decoder
.retreat(size
);
959 auto const before
= decoder
.advanced();
962 auto const sizeBytes
= decoder
.advanced() - before
;
963 decoder
.retreat(sizeBytes
);
964 if (size
<= 1) return sizeBytes
;
966 return sizeBytes
+ size
;
969 void BlobEncoderHelper
<LowStringPtr
>::serde(BlobEncoder
& encoder
,
971 auto const sd
= s
.get();
975 void BlobEncoderHelper
<LowStringPtr
>::serde(BlobDecoder
& decoder
,
977 const StringData
* sd
;
982 ///////////////////////////////////////////////////////////////////////////////
985 std::string
StringData::toCppString() const {
987 return std::string(s
.data(), s
.size());
990 bool StringData::checkSane() const {
991 static_assert(sizeof(StringData
) == 16);
992 static_assert(size_t(MaxSize
) <= size_t(INT_MAX
), "Beware int wraparound");
993 static_assert(sizeof(StringData
) == SD_DATA
, "");
994 static_assert(offsetof(StringData
, m_len
) == SD_LEN
, "");
995 static_assert(offsetof(StringData
, m_hash
) == SD_HASH
, "");
996 assertx(kindIsValid());
997 assertx(uint32_t(size()) <= MaxSize
);
998 assertx(size() >= 0);
999 assertx(IMPLIES(isSymbol(), isStatic()));
1000 if (isRefCounted()) {
1001 assertx(size() <= capacity());
1002 assertx(capacity() <= MaxSize
);
1007 //////////////////////////////////////////////////////////////////////