Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-31 10:12:26

0001 // Protocol Buffers - Google's data interchange format
0002 // Copyright 2023 Google Inc.  All rights reserved.
0003 //
0004 // Use of this source code is governed by a BSD-style
0005 // license that can be found in the LICENSE file or at
0006 // https://developers.google.com/open-source/licenses/bsd
0007 //
0008 // This file defines the internal StringBlock class
0009 
0010 #ifndef GOOGLE_PROTOBUF_STRING_BLOCK_H__
0011 #define GOOGLE_PROTOBUF_STRING_BLOCK_H__
0012 
0013 #include <algorithm>
0014 #include <cstddef>
0015 #include <cstdint>
0016 #include <string>
0017 
0018 #include "absl/base/attributes.h"
0019 #include "absl/log/absl_check.h"
0020 #include "google/protobuf/arena_align.h"
0021 #include "google/protobuf/port.h"
0022 
0023 // Must be included last.
0024 #include "google/protobuf/port_def.inc"
0025 
0026 namespace google {
0027 namespace protobuf {
0028 namespace internal {
0029 
0030 // StringBlock provides heap allocated, dynamically sized blocks (mini arenas)
0031 // for allocating std::string instances. StringBlocks are allocated through
0032 // the `New` function, and must be freed using the `Delete` function.
0033 // StringBlocks are automatically sized from 256B to 8KB depending on the
0034 // `next` instance provided in the `New` function to keep the average maximum
0035 // unused space limited to 25%, or up to 4KB.
0036 class alignas(std::string) StringBlock {
0037  public:
0038   StringBlock() = delete;
0039   StringBlock(const StringBlock&) = delete;
0040   StringBlock& operator=(const StringBlock&) = delete;
0041 
0042   // Returns the size of the next string block based on the size information
0043   // stored in `block`. `block` may be null in which case the size of the
0044   // initial string block is returned.
0045   static size_t NextSize(StringBlock* block);
0046 
0047   // Allocates a new StringBlock pointing to `next`, which can be null.
0048   // The size of the returned block depends on the allocated size of `next`.
0049   static StringBlock* New(StringBlock* next);
0050 
0051   // Allocates a new string block `in place`. `n` must be the value returned
0052   // from a previous call to `StringBlock::NextSize(next)`
0053   static StringBlock* Emplace(void* p, size_t n, StringBlock* next);
0054 
0055   // Deletes `block` if `block` is heap allocated. `block` must not be null.
0056   // Returns the allocated size of `block`, or 0 if the block was emplaced.
0057   static size_t Delete(StringBlock* block);
0058 
0059   StringBlock* next() const;
0060 
0061   // Returns the string instance at offset `offset`.
0062   // `offset` must be a multiple of sizeof(std::string), and be less than or
0063   // equal to `effective_size()`. `AtOffset(effective_size())` returns the
0064   // end of the allocated string instances and must not be de-referenced.
0065   ABSL_ATTRIBUTE_RETURNS_NONNULL std::string* AtOffset(size_t offset);
0066 
0067   // Returns a pointer to the first string instance in this block.
0068   ABSL_ATTRIBUTE_RETURNS_NONNULL std::string* begin();
0069 
0070   // Returns a pointer directly beyond the last string instance in this block.
0071   ABSL_ATTRIBUTE_RETURNS_NONNULL std::string* end();
0072 
0073   // Returns the total allocation size of this instance.
0074   size_t allocated_size() const { return allocated_size_; }
0075 
0076   // Returns true if this block is heap allocated, false if emplaced.
0077   bool heap_allocated() const { return heap_allocated_; }
0078 
0079   // Returns the effective size available for allocation string instances.
0080   // This value is guaranteed to be a multiple of sizeof(std::string), and
0081   // guaranteed to never be zero.
0082   size_t effective_size() const;
0083 
0084  private:
0085   using size_type = uint16_t;
0086 
0087   static_assert(alignof(std::string) <= sizeof(void*), "");
0088   static_assert(alignof(std::string) <= ArenaAlignDefault::align, "");
0089 
0090   ~StringBlock() = default;
0091 
0092   explicit StringBlock(StringBlock* next, bool heap_allocated, size_type size,
0093                        size_type next_size) noexcept
0094       : next_(next),
0095         allocated_size_(size),
0096         next_size_(next_size),
0097         heap_allocated_(heap_allocated) {}
0098 
0099   static constexpr size_type min_size() { return size_type{256}; }
0100   static constexpr size_type max_size() { return size_type{8192}; }
0101 
0102   // Returns `size` rounded down such that we can fit a perfect number
0103   // of std::string instances inside a StringBlock of that size.
0104   static constexpr size_type RoundedSize(size_type size);
0105 
0106   // Returns the size of the next block.
0107   size_t next_size() const { return next_size_; }
0108 
0109   StringBlock* const next_;
0110   const size_type allocated_size_;
0111   const size_type next_size_;
0112   const bool heap_allocated_;
0113 };
0114 
0115 constexpr StringBlock::size_type StringBlock::RoundedSize(size_type size) {
0116   return size - (size - sizeof(StringBlock)) % sizeof(std::string);
0117 }
0118 
0119 inline size_t StringBlock::NextSize(StringBlock* block) {
0120   return block ? block->next_size() : min_size();
0121 }
0122 
0123 inline StringBlock* StringBlock::Emplace(void* p, size_t n, StringBlock* next) {
0124   const auto count = static_cast<size_type>(n);
0125   ABSL_DCHECK_EQ(count, NextSize(next));
0126   size_type doubled = count * 2;
0127   size_type next_size = next ? std::min(doubled, max_size()) : min_size();
0128   return new (p) StringBlock(next, false, RoundedSize(count), next_size);
0129 }
0130 
0131 inline StringBlock* StringBlock::New(StringBlock* next) {
0132   // Compute required size, rounding down to a multiple of sizeof(std:string)
0133   // so that we can optimize the allocation path. I.e., we incur a (constant
0134   // size) MOD() operation cost here to avoid any MUL() later on.
0135   size_type size = min_size();
0136   size_type next_size = min_size();
0137   if (next) {
0138     size = next->next_size_;
0139     next_size = std::min<size_type>(size * 2, max_size());
0140   }
0141   size = RoundedSize(size);
0142   void* p = ::operator new(size);
0143   return new (p) StringBlock(next, true, size, next_size);
0144 }
0145 
0146 inline size_t StringBlock::Delete(StringBlock* block) {
0147   ABSL_DCHECK(block != nullptr);
0148   if (!block->heap_allocated_) return size_t{0};
0149   size_t size = block->allocated_size();
0150   internal::SizedDelete(block, size);
0151   return size;
0152 }
0153 
0154 inline StringBlock* StringBlock::next() const { return next_; }
0155 
0156 inline size_t StringBlock::effective_size() const {
0157   return allocated_size_ - sizeof(StringBlock);
0158 }
0159 
0160 ABSL_ATTRIBUTE_RETURNS_NONNULL inline std::string* StringBlock::AtOffset(
0161     size_t offset) {
0162   ABSL_DCHECK_LE(offset, effective_size());
0163   return reinterpret_cast<std::string*>(reinterpret_cast<char*>(this + 1) +
0164                                         offset);
0165 }
0166 
0167 ABSL_ATTRIBUTE_RETURNS_NONNULL inline std::string* StringBlock::begin() {
0168   return AtOffset(0);
0169 }
0170 
0171 ABSL_ATTRIBUTE_RETURNS_NONNULL inline std::string* StringBlock::end() {
0172   return AtOffset(effective_size());
0173 }
0174 
0175 }  // namespace internal
0176 }  // namespace protobuf
0177 }  // namespace google
0178 
0179 #include "google/protobuf/port_undef.inc"
0180 
0181 #endif  // GOOGLE_PROTOBUF_STRING_BLOCK_H__