diff --git a/utils/managed_bytes.hh b/utils/managed_bytes.hh index d2c82f6cc5..402bf84fd4 100644 --- a/utils/managed_bytes.hh +++ b/utils/managed_bytes.hh @@ -30,9 +30,21 @@ struct blob_storage { } __attribute__((packed)); // A managed version of "bytes" (can be used with LSA). -// FIXME: Add small managed_bytes optimization class managed_bytes { - blob_storage* _ptr; + static constexpr size_t max_inline_size = 15; + struct small_blob { + bytes_view::value_type data[max_inline_size]; + int8_t size; // -1 -> use blob_storage + }; + union { + blob_storage* ptr; + small_blob small; + } _u; + static_assert(sizeof(small_blob) > sizeof(blob_storage*), "inline size too small"); +private: + bool external() const { + return _u.small.size < 0; + } public: using size_type = blob_storage::size_type; struct initialized_later {}; @@ -45,30 +57,37 @@ public: managed_bytes(const bytes& b) : managed_bytes(static_cast(b)) {} managed_bytes(initialized_later, size_type size) { - void* p = current_allocator().alloc(standard_migrator, - sizeof(blob_storage) + size, alignof(blob_storage)); - new (p) blob_storage(&_ptr, size); + if (size <= max_inline_size) { + _u.small.size = size; + } else { + _u.small.size = -1; + void* p = current_allocator().alloc(standard_migrator, + sizeof(blob_storage) + size, alignof(blob_storage)); + new (p) blob_storage(&_u.ptr, size); + } } managed_bytes(bytes_view v) : managed_bytes(initialized_later(), v.size()) { - memcpy(_ptr->data, v.data(), v.size()); + memcpy(data(), v.data(), v.size()); } ~managed_bytes() { - if (_ptr) { - current_allocator().destroy(_ptr); + if (external()) { + current_allocator().destroy(_u.ptr); } } managed_bytes(const managed_bytes& o) : managed_bytes(static_cast(o)) {} managed_bytes(managed_bytes&& o) noexcept - : _ptr(o._ptr) + : _u(o._u) { - o._ptr = nullptr; - if (_ptr) { - _ptr->backref = &_ptr; + if (external()) { + if (_u.ptr) { + _u.ptr->backref = &_u.ptr; + } } + o._u.small.size = 0; } managed_bytes& operator=(managed_bytes&& o) { @@ -92,40 +111,56 @@ public: } operator bytes_view() const { - return { _ptr->data, _ptr->size }; + return { data(), size() }; } bytes_view::value_type& operator[](size_type index) { - return _ptr->data[index]; + return data()[index]; } const bytes_view::value_type& operator[](size_type index) const { - return _ptr->data[index]; + return data()[index]; } size_type size() const { - return _ptr->size; + if (external()) { + return _u.ptr->size; + } else { + return _u.small.size; + } } const blob_storage::char_type* begin() const { - return _ptr->data; + return data(); } const blob_storage::char_type* end() const { - return _ptr->data + _ptr->size; + return data() + size(); } blob_storage::char_type* begin() { - return _ptr->data; + return data(); } blob_storage::char_type* end() { - return _ptr->data + _ptr->size; + return data() + size(); } bool empty() const { return size() == 0; } + + blob_storage::char_type* data() { + if (external()) { + return _u.ptr->data; + } else { + return _u.small.data; + } + } + + const blob_storage::char_type* data() const { + return const_cast(this)->data(); + } }; namespace std {