xrpld
Loading...
Searching...
No Matches
SHAMapItem.h
1#pragma once
2
3#include <xrpl/basics/ByteUtilities.h>
4#include <xrpl/basics/CountedObject.h>
5#include <xrpl/basics/SlabAllocator.h>
6#include <xrpl/basics/Slice.h>
7#include <xrpl/basics/base_uint.h>
8#include <xrpl/basics/contract.h>
9#include <xrpl/beast/utility/instrumentation.h>
10
11#include <boost/smart_ptr/intrusive_ptr.hpp>
12
13#include <atomic>
14#include <cstddef>
15#include <cstdint>
16#include <cstring>
17#include <memory>
18#include <type_traits>
19
20namespace xrpl {
21
22// an item stored in a SHAMap
23class SHAMapItem : public CountedObject<SHAMapItem>
24{
25 // These are used to support boost::intrusive_ptr reference counting
26 // These functions are used internally by boost::intrusive_ptr to handle
27 // lifetime management.
28 friend void
30
31 friend void
33
34 // This is the interface for creating new instances of this class.
35 friend boost::intrusive_ptr<SHAMapItem>
36 makeShamapitem(uint256 const& tag, Slice data);
37
38private:
40
41 // We use std::uint32_t to minimize the size; there's no SHAMapItem whose
42 // size exceeds 4GB and there won't ever be (famous last words?), so this
43 // is safe.
45
46 // This is the reference count used to support boost::intrusive_ptr
48
49 // Because of the unusual way in which SHAMapItem objects are constructed
50 // the only way to properly create one is to first allocate enough memory
51 // so we limit this constructor to codepaths that do this right and limit
52 // arbitrary construction.
54 : tag_(tag), size_(static_cast<std::uint32_t>(data.size()))
55 {
57 reinterpret_cast<std::uint8_t*>(this) + sizeof(*this), data.data(), data.size());
58 }
59
60public:
61 SHAMapItem() = delete;
62
63 SHAMapItem(SHAMapItem const& other) = delete;
64
66 operator=(SHAMapItem const& other) = delete;
67
68 SHAMapItem(SHAMapItem&& other) = delete;
69
71 operator=(SHAMapItem&&) = delete;
72
73 uint256 const&
74 key() const
75 {
76 return tag_;
77 }
78
80 size() const
81 {
82 return size_;
83 }
84
85 void const*
86 data() const
87 {
88 return reinterpret_cast<std::uint8_t const*>(this) + sizeof(*this);
89 }
90
91 Slice
92 slice() const
93 {
94 return {data(), size()};
95 }
96};
97
98namespace detail {
99
100// clang-format off
101// The slab cutoffs and the number of megabytes per allocation are customized
102// based on the number of objects of each size we expect to need at any point
103// in time and with an eye to minimize the number of slack bytes in a block.
105 { 128, megabytes(std::size_t(60)) },
106 { 192, megabytes(std::size_t(46)) },
107 { 272, megabytes(std::size_t(60)) },
108 { 384, megabytes(std::size_t(56)) },
109 { 564, megabytes(std::size_t(40)) },
110 { 772, megabytes(std::size_t(46)) },
111 { 1052, megabytes(std::size_t(60)) },
112});
113// clang-format on
114
115} // namespace detail
116
117inline void
119{
120 // This can only happen if someone releases the last reference to the
121 // item while we were trying to increment the refcount.
122 if (x->refcount_++ == 0)
123 logicError("SHAMapItem: the reference count is 0!");
124}
125
126inline void
128{
129 if (--x->refcount_ == 0)
130 {
131 auto p = reinterpret_cast<std::uint8_t const*>(x);
132
133 // The SHAMapItem constructor isn't trivial (because the destructor
134 // for CountedObject isn't) so we can't avoid calling it here, but
135 // plan for a future where we might not need to.
138
139 // If the slabber doesn't claim this pointer, it was allocated
140 // manually, so we free it manually.
141 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
142 if (!detail::gSlabber.deallocate(const_cast<std::uint8_t*>(p)))
143 delete[] p;
144 }
145}
146
147inline boost::intrusive_ptr<SHAMapItem>
149{
150 XRPL_ASSERT(
151 data.size() <= megabytes<std::size_t>(16), "xrpl::makeShamapitem : maximum input size");
152
153 // NOLINTNEXTLINE(misc-const-correctness)
154 std::uint8_t* raw = detail::gSlabber.allocate(data.size());
155
156 // If we can't grab memory from the slab allocators, we fall back to
157 // the standard library and try to grab a precisely-sized memory block:
158 if (raw == nullptr)
159 raw = new std::uint8_t[sizeof(SHAMapItem) + data.size()];
160
161 // We do not increment the reference count here on purpose: the
162 // constructor of SHAMapItem explicitly sets it to 1. We use the fact
163 // that the refcount can never be zero before incrementing as an
164 // invariant.
165 return {new (raw) SHAMapItem{tag, data}, false};
166}
167
168static_assert(alignof(SHAMapItem) != 40);
169static_assert(alignof(SHAMapItem) == 8 || alignof(SHAMapItem) == 4);
170
171inline boost::intrusive_ptr<SHAMapItem>
173{
174 return makeShamapitem(other.key(), other.slice());
175}
176
177} // namespace xrpl
SHAMapItem & operator=(SHAMapItem const &other)=delete
uint256 const tag_
Definition SHAMapItem.h:39
SHAMapItem(SHAMapItem &&other)=delete
SHAMapItem()=delete
std::uint32_t const size_
Definition SHAMapItem.h:44
SHAMapItem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:53
friend void intrusive_ptr_add_ref(SHAMapItem const *x)
Definition SHAMapItem.h:118
SHAMapItem(SHAMapItem const &other)=delete
std::size_t size() const
Definition SHAMapItem.h:80
friend void intrusive_ptr_release(SHAMapItem const *x)
Definition SHAMapItem.h:127
friend boost::intrusive_ptr< SHAMapItem > makeShamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:148
Slice slice() const
Definition SHAMapItem.h:92
void const * data() const
Definition SHAMapItem.h:86
uint256 const & key() const
Definition SHAMapItem.h:74
std::atomic< std::uint32_t > refcount_
Definition SHAMapItem.h:47
SHAMapItem & operator=(SHAMapItem &&)=delete
A collection of slab allocators of various sizes for a given type.
An immutable linear range of bytes.
Definition Slice.h:28
T destroy_at(T... args)
T is_trivially_destructible_v
T memcpy(T... args)
STL namespace.
SlabAllocatorSet< SHAMapItem > gSlabber({ { 128, megabytes(std::size_t(60)) }, { 192, megabytes(std::size_t(46)) }, { 272, megabytes(std::size_t(60)) }, { 384, megabytes(std::size_t(56)) }, { 564, megabytes(std::size_t(40)) }, { 772, megabytes(std::size_t(46)) }, { 1052, megabytes(std::size_t(60)) }, })
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void intrusive_ptr_release(SHAMapItem const *x)
Definition SHAMapItem.h:127
void intrusive_ptr_add_ref(SHAMapItem const *x)
Definition SHAMapItem.h:118
void logicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
constexpr auto megabytes(T value) noexcept
boost::intrusive_ptr< SHAMapItem > makeShamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:148
BaseUInt< 256 > uint256
Definition base_uint.h:580