File indexing completed on 2026-07-13 08:23:32
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef GOOGLE_PROTOBUF_MICRO_STRING_H__
0009 #define GOOGLE_PROTOBUF_MICRO_STRING_H__
0010
0011 #include <cstddef>
0012 #include <cstdint>
0013
0014 #include "absl/base/config.h"
0015 #include "absl/log/absl_check.h"
0016 #include "absl/strings/string_view.h"
0017 #include "google/protobuf/arena.h"
0018
0019
0020 #include "google/protobuf/port_def.inc"
0021
0022 namespace google {
0023 namespace protobuf {
0024 namespace internal {
0025
0026 struct MicroStringTestPeer;
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069 class PROTOBUF_EXPORT MicroString {
0070 struct LargeRep {
0071 char* payload;
0072 uint32_t size;
0073
0074 uint32_t capacity;
0075
0076 absl::string_view view() const { return {payload, size}; }
0077 char* owned_head() {
0078 ABSL_DCHECK_GE(capacity, kOwned);
0079 return reinterpret_cast<char*>(this + 1);
0080 }
0081
0082 void SetExternalBuffer(absl::string_view buffer) {
0083 payload = const_cast<char*>(buffer.data());
0084 size = buffer.size();
0085 }
0086
0087 void SetInitialSize(size_t size) {
0088 PoisonMemoryRegion(owned_head() + size, capacity - size);
0089 this->size = size;
0090 }
0091
0092 void Unpoison() { UnpoisonMemoryRegion(owned_head(), capacity); }
0093
0094 void ChangeSize(size_t new_size) {
0095 PoisonMemoryRegion(owned_head() + new_size, capacity - new_size);
0096 UnpoisonMemoryRegion(owned_head(), new_size);
0097 size = new_size;
0098 }
0099 };
0100
0101 struct MicroRep {
0102 uint8_t size;
0103 uint8_t capacity;
0104
0105 char* data() { return reinterpret_cast<char*>(this + 1); }
0106 const char* data() const { return reinterpret_cast<const char*>(this + 1); }
0107 absl::string_view view() const { return {data(), size}; }
0108
0109 void SetInitialSize(uint8_t size) {
0110 PoisonMemoryRegion(data() + size, capacity - size);
0111 this->size = size;
0112 }
0113
0114 void Unpoison() { UnpoisonMemoryRegion(data(), capacity); }
0115
0116 void ChangeSize(uint8_t new_size) {
0117 PoisonMemoryRegion(data() + new_size, capacity - new_size);
0118 UnpoisonMemoryRegion(data(), new_size);
0119 size = new_size;
0120 }
0121 };
0122
0123 public:
0124
0125
0126 static constexpr bool kAllowExtraCapacity = IsLittleEndian();
0127 static constexpr size_t kInlineCapacity = sizeof(uintptr_t) - 1;
0128 static constexpr size_t kMaxMicroRepCapacity = 256 - sizeof(MicroRep);
0129
0130
0131 constexpr MicroString() : rep_() {}
0132
0133 explicit MicroString(Arena*) : MicroString() {}
0134
0135 MicroString(Arena* arena, const MicroString& other)
0136 : MicroString(FromOtherTag{}, other, arena) {}
0137
0138
0139
0140
0141
0142 ~MicroString() = default;
0143
0144 union UnownedPayload {
0145 LargeRep payload;
0146
0147
0148
0149
0150 char for_tag[1];
0151
0152
0153 auto get() const { return payload.view(); }
0154 };
0155 constexpr MicroString(const UnownedPayload& unowned_input)
0156 : rep_(const_cast<char*>(unowned_input.for_tag + kIsLargeRepTag)) {}
0157
0158
0159
0160
0161
0162 static MicroString MakeDefaultValuePrototype(absl::string_view default_value);
0163 void DestroyDefaultValuePrototype();
0164
0165
0166
0167
0168
0169 void InitDefault() { rep_ = nullptr; }
0170
0171
0172
0173
0174 void Destroy() {
0175 if (!is_inline()) DestroySlow();
0176 }
0177
0178
0179
0180 void Clear() {
0181 if (is_inline()) {
0182 set_inline_size(0);
0183 return;
0184 }
0185 ClearSlow();
0186 }
0187
0188
0189 void Set(const MicroString& other, Arena* arena) {
0190 SetFromOtherImpl(*this, other, arena);
0191 }
0192
0193 void Set(const MicroString& other, Arena* arena, size_t inline_capacity) {
0194
0195 if (other.is_large_rep() && other.large_rep_kind() == kUnowned) {
0196 if (arena == nullptr) Destroy();
0197 rep_ = other.rep_;
0198 return;
0199 }
0200 Set(other.Get(), arena, inline_capacity);
0201 }
0202
0203
0204 void Set(absl::string_view data, Arena* arena) {
0205 SetMaybeConstant(*this, data, arena);
0206 }
0207 void Set(absl::string_view data, Arena* arena, size_t inline_capacity) {
0208 SetImpl(data, arena, inline_capacity);
0209 }
0210
0211
0212
0213
0214 template <typename... Args>
0215 void Set(const std::string& data, Args... args) {
0216 Set(absl::string_view(data), args...);
0217 }
0218 template <typename... Args>
0219 void Set(std::string&& data, Args... args) {
0220 SetString(std::move(data), args...);
0221 }
0222 template <typename... Args>
0223 void Set(const char* data, Args... args) {
0224 Set(absl::string_view(data), args...);
0225 }
0226
0227
0228 void SetAlias(absl::string_view data, Arena* arena,
0229 size_t inline_capacity = kInlineCapacity);
0230
0231
0232
0233 void SetUnowned(const UnownedPayload& unowned_input, Arena* arena);
0234
0235
0236
0237
0238
0239 void ClearToDefault(const UnownedPayload& unowned_input, Arena* arena);
0240
0241
0242
0243 void ClearToDefault(const MicroString& other, Arena* arena);
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264 template <typename F>
0265 void SetInChunks(size_t size, Arena* arena, F setter,
0266 size_t inline_capacity = kInlineCapacity);
0267
0268
0269
0270 size_t Capacity() const;
0271
0272 size_t SpaceUsedExcludingSelfLong() const;
0273
0274 absl::string_view Get() const {
0275 if (is_micro_rep()) {
0276 return micro_rep()->view();
0277 } else if (is_inline()) {
0278 return inline_view();
0279 } else {
0280 return large_rep()->view();
0281 }
0282 }
0283
0284
0285
0286
0287 static constexpr UnownedPayload MakeUnownedPayload(absl::string_view data) {
0288 return UnownedPayload{LargeRep{const_cast<char*>(data.data()),
0289 static_cast<uint32_t>(data.size()),
0290 kUnowned}};
0291 }
0292
0293 void InternalSwap(MicroString* other,
0294 size_t inline_capacity = kInlineCapacity) {
0295 std::swap_ranges(reinterpret_cast<char*>(this),
0296 reinterpret_cast<char*>(this) + inline_capacity + 1,
0297 reinterpret_cast<char*>(other));
0298 }
0299
0300 protected:
0301 friend MicroStringTestPeer;
0302
0303 struct StringRep : LargeRep {
0304 std::string str;
0305 void ResetBase() { SetExternalBuffer(str); }
0306 };
0307
0308 static_assert(alignof(void*) >= 4, "We need two tag bits from pointers.");
0309 static constexpr uintptr_t kIsLargeRepTag = 0x1;
0310 static_assert(sizeof(UnownedPayload::for_tag) == kIsLargeRepTag,
0311 "See comment in for_tag declaration above.");
0312
0313 static constexpr uintptr_t kIsMicroRepTag = 0x2;
0314 static constexpr int kTagShift = 2;
0315 static constexpr size_t kMaxInlineCapacity = 255 >> kTagShift;
0316
0317 static_assert((kIsLargeRepTag & kIsMicroRepTag) == 0,
0318 "The tags are exclusive.");
0319
0320 enum LargeRepKind {
0321
0322 kAlias,
0323
0324 kUnowned,
0325
0326 kString,
0327
0328
0329 kOwned
0330 };
0331 LargeRepKind large_rep_kind() const {
0332 ABSL_DCHECK(is_large_rep());
0333 size_t cap = large_rep()->capacity;
0334 return cap >= kOwned ? kOwned : static_cast<LargeRepKind>(cap);
0335 }
0336
0337
0338
0339 static_assert(sizeof(MicroRep) == kIsMicroRepTag);
0340 MicroRep* micro_rep() const {
0341 ABSL_DCHECK(is_micro_rep());
0342
0343
0344
0345 return reinterpret_cast<MicroRep*>(reinterpret_cast<uintptr_t>(rep_) -
0346 kIsMicroRepTag);
0347 }
0348 static size_t MicroRepSize(size_t capacity) {
0349 return sizeof(MicroRep) + capacity;
0350 }
0351 static size_t OwnedRepSize(size_t capacity) {
0352 return sizeof(LargeRep) + capacity;
0353 }
0354
0355 LargeRep* large_rep() const {
0356 ABSL_DCHECK(is_large_rep());
0357
0358
0359 return reinterpret_cast<LargeRep*>(reinterpret_cast<uintptr_t>(rep_) -
0360 kIsLargeRepTag);
0361 }
0362 StringRep* string_rep() const {
0363 ABSL_DCHECK_EQ(+kString, +large_rep_kind());
0364 return static_cast<StringRep*>(large_rep());
0365 }
0366
0367 bool is_micro_rep() const {
0368 return (reinterpret_cast<uintptr_t>(rep_) & kIsMicroRepTag) ==
0369 kIsMicroRepTag;
0370 }
0371 bool is_large_rep() const {
0372 return (reinterpret_cast<uintptr_t>(rep_) & kIsLargeRepTag) ==
0373 kIsLargeRepTag;
0374 }
0375 bool is_inline() const { return !is_micro_rep() && !is_large_rep(); }
0376 size_t inline_size() const {
0377 ABSL_DCHECK(is_inline());
0378 return static_cast<uint8_t>(reinterpret_cast<uintptr_t>(rep_)) >> kTagShift;
0379 }
0380 void set_inline_size(size_t size) {
0381 size <<= kTagShift;
0382 PROTOBUF_ASSUME(size <= 0xFF);
0383
0384
0385 rep_ = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(rep_) & ~0xFF) |
0386 size);
0387 ABSL_DCHECK(is_inline());
0388 }
0389 char* inline_head() {
0390 ABSL_DCHECK(is_inline());
0391
0392
0393
0394
0395
0396 return IsLittleEndian() ? reinterpret_cast<char*>(&rep_) + 1
0397 : reinterpret_cast<char*>(&rep_);
0398 }
0399 const char* inline_head() const {
0400 return const_cast<MicroString*>(this)->inline_head();
0401 }
0402 absl::string_view inline_view() const {
0403 return {inline_head(), inline_size()};
0404 }
0405
0406
0407
0408 struct FromOtherTag {};
0409 template <typename Self>
0410 MicroString(FromOtherTag, const Self& other, Arena* arena) {
0411 if (other.is_inline()) {
0412 static_cast<Self&>(*this) = other;
0413 return;
0414 }
0415
0416 InitDefault();
0417 SetFromOtherSlow(other, arena, Self::kInlineCapacity);
0418 }
0419
0420 template <typename Self>
0421 static void SetFromOtherImpl(Self& self, const Self& other, Arena* arena) {
0422
0423
0424
0425 if (static_cast<int>(self.is_inline()) &
0426 static_cast<int>(other.is_inline())) {
0427 self = other;
0428 return;
0429 }
0430 self.SetFromOtherSlow(other, arena, Self::kInlineCapacity);
0431 }
0432
0433
0434 void SetString(std::string&& data, Arena* arena,
0435 size_t inline_capacity = kInlineCapacity);
0436
0437 void SetFromOtherSlow(const MicroString& other, Arena* arena,
0438 size_t inline_capacity);
0439
0440 void ClearSlow();
0441
0442 template <typename Self>
0443 static void SetMaybeConstant(Self& self, absl::string_view data,
0444 Arena* arena) {
0445 const size_t size = data.size();
0446 if (PROTOBUF_BUILTIN_CONSTANT_P(size <= Self::kInlineCapacity) &&
0447 size <= Self::kInlineCapacity && self.is_inline()) {
0448
0449
0450 Self tmp;
0451 tmp.set_inline_size(size);
0452 if (size != 0) {
0453 memcpy(tmp.inline_head(), data.data(), data.size());
0454 }
0455 self = tmp;
0456 return;
0457 }
0458 self.SetImpl(data, arena, Self::kInlineCapacity);
0459 }
0460 void SetImpl(absl::string_view data, Arena* arena, size_t inline_capacity);
0461
0462 void DestroySlow();
0463
0464
0465
0466
0467
0468
0469 MicroRep* AllocateMicroRep(size_t size, Arena* arena);
0470 LargeRep* AllocateOwnedRep(size_t size, Arena* arena);
0471 StringRep* AllocateStringRep(Arena* arena);
0472
0473 void* rep_;
0474 };
0475
0476 template <typename F>
0477 void MicroString::SetInChunks(size_t size, Arena* arena, F setter,
0478 size_t inline_capacity) {
0479 const auto invoke_setter = [&](char* p) {
0480 char* start = p;
0481 setter([&](absl::string_view chunk) {
0482 ABSL_DCHECK_LE(p - start + chunk.size(), size);
0483 memcpy(p, chunk.data(), chunk.size());
0484 p += chunk.size();
0485 });
0486 return p - start;
0487 };
0488
0489 const auto do_inline = [&] {
0490 ABSL_DCHECK_LE(size, inline_capacity);
0491 set_inline_size(invoke_setter(inline_head()));
0492 };
0493
0494 const auto do_micro = [&](MicroRep* r) {
0495 ABSL_DCHECK_LE(size, r->capacity);
0496 r->Unpoison();
0497 r->ChangeSize(invoke_setter(r->data()));
0498 };
0499
0500 const auto do_owned = [&](LargeRep* r) {
0501 ABSL_DCHECK_LE(size, r->capacity);
0502 r->Unpoison();
0503 r->ChangeSize(invoke_setter(r->owned_head()));
0504 };
0505
0506 const auto do_string = [&](StringRep* r) {
0507 r->str.clear();
0508 setter([&](absl::string_view chunk) {
0509 r->str.append(chunk.data(), chunk.size());
0510 });
0511 r->ResetBase();
0512 };
0513
0514 if (is_inline()) {
0515 if (size <= inline_capacity) {
0516 return do_inline();
0517 }
0518 } else if (is_micro_rep()) {
0519 if (auto* r = micro_rep(); size <= r->capacity) {
0520 return do_micro(r);
0521 }
0522 } else if (is_large_rep()) {
0523 switch (large_rep_kind()) {
0524 case kOwned:
0525 if (auto* r = large_rep(); size <= r->capacity) {
0526 return do_owned(r);
0527 }
0528 break;
0529 case kString:
0530 return do_string(string_rep());
0531 case kAlias:
0532 case kUnowned:
0533 break;
0534 }
0535 }
0536
0537
0538
0539 static constexpr size_t kSafeStringSize = 50000000;
0540
0541
0542 if (arena == nullptr) Destroy();
0543
0544 if (size <= inline_capacity) {
0545 set_inline_size(0);
0546 do_inline();
0547 } else if (size <= kMaxMicroRepCapacity) {
0548 do_micro(AllocateMicroRep(size, arena));
0549 } else if (size <= kSafeStringSize) {
0550 do_owned(AllocateOwnedRep(size, arena));
0551 } else {
0552
0553 do_string(AllocateStringRep(arena));
0554 }
0555 }
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569 template <size_t RequestedSpace>
0570 class MicroStringExtraImpl : private MicroString {
0571 static constexpr size_t RoundUp(size_t n) {
0572 return (n + (alignof(MicroString) - 1)) & ~(alignof(MicroString) - 1);
0573 }
0574
0575 public:
0576
0577 static constexpr size_t kInlineCapacity =
0578 RoundUp(RequestedSpace + 1) - 1;
0579
0580 static_assert(kInlineCapacity < MicroString::kMaxInlineCapacity,
0581 "Must fit with the tags.");
0582
0583 constexpr MicroStringExtraImpl() {
0584
0585
0586 static_assert(static_cast<int>(RequestedSpace != 0) &
0587 static_cast<int>(MicroString::kAllowExtraCapacity));
0588 }
0589 MicroStringExtraImpl(Arena* arena, const MicroStringExtraImpl& other)
0590 : MicroString(FromOtherTag{}, other, arena) {}
0591
0592 using MicroString::Get;
0593
0594 void Set(const MicroStringExtraImpl& other, Arena* arena) {
0595 SetFromOtherImpl(*this, other, arena);
0596 }
0597 void Set(absl::string_view data, Arena* arena) {
0598 SetMaybeConstant(*this, data, arena);
0599 }
0600 void Set(const std::string& data, Arena* arena) {
0601 Set(absl::string_view(data), arena);
0602 }
0603 void Set(const char* data, Arena* arena) {
0604 Set(absl::string_view(data), arena);
0605 }
0606 void Set(std::string&& str, Arena* arena) {
0607 MicroString::SetString(std::move(str), arena, kInlineCapacity);
0608 }
0609
0610 void SetAlias(absl::string_view data, Arena* arena) {
0611 MicroString::SetAlias(data, arena, kInlineCapacity);
0612 }
0613
0614 using MicroString::Destroy;
0615
0616 size_t Capacity() const {
0617 return is_inline() ? kInlineCapacity : MicroString::Capacity();
0618 }
0619
0620 void InternalSwap(MicroStringExtraImpl* other) {
0621 MicroString::InternalSwap(other, kInlineCapacity);
0622 }
0623
0624 using MicroString::SpaceUsedExcludingSelfLong;
0625
0626 private:
0627 friend MicroString;
0628
0629 char extra_buffer_[kInlineCapacity - MicroString::kInlineCapacity];
0630 };
0631
0632
0633
0634
0635
0636
0637
0638
0639
0640
0641 template <size_t InlineCapacity>
0642 using MicroStringExtra =
0643 std::conditional_t<(!MicroString::kAllowExtraCapacity ||
0644 InlineCapacity <= MicroString::kInlineCapacity),
0645 MicroString, MicroStringExtraImpl<InlineCapacity>>;
0646
0647 }
0648 }
0649 }
0650
0651 #include "google/protobuf/port_undef.inc"
0652
0653 #endif