rippled
SHAMapItem.h
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #ifndef RIPPLE_SHAMAP_SHAMAPITEM_H_INCLUDED
21 #define RIPPLE_SHAMAP_SHAMAPITEM_H_INCLUDED
22 
23 #include <ripple/basics/ByteUtilities.h>
24 #include <ripple/basics/CountedObject.h>
25 #include <ripple/basics/SlabAllocator.h>
26 #include <ripple/basics/Slice.h>
27 #include <ripple/basics/base_uint.h>
28 #include <boost/smart_ptr/intrusive_ptr.hpp>
29 #include <cassert>
30 
31 namespace ripple {
32 
33 // an item stored in a SHAMap
34 class SHAMapItem : public CountedObject<SHAMapItem>
35 {
36  // These are used to support boost::intrusive_ptr reference counting
37  // These functions are used internally by boost::intrusive_ptr to handle
38  // lifetime management.
39  friend void
41 
42  friend void
44 
45  // This is the interface for creating new instances of this class.
46  friend boost::intrusive_ptr<SHAMapItem>
47  make_shamapitem(uint256 const& tag, Slice data);
48 
49 private:
50  uint256 const tag_;
51 
52  // We use std::uint32_t to minimize the size; there's no SHAMapItem whose
53  // size exceeds 4GB and there won't ever be (famous last words?), so this
54  // is safe.
56 
57  // This is the reference count used to support boost::intrusive_ptr
59 
60  // Because of the unusual way in which SHAMapItem objects are constructed
61  // the only way to properly create one is to first allocate enough memory
62  // so we limit this constructor to codepaths that do this right and limit
63  // arbitrary construction.
65  : tag_(tag), size_(static_cast<std::uint32_t>(data.size()))
66  {
68  reinterpret_cast<std::uint8_t*>(this) + sizeof(*this),
69  data.data(),
70  data.size());
71  }
72 
73 public:
74  SHAMapItem() = delete;
75 
76  SHAMapItem(SHAMapItem const& other) = delete;
77 
78  SHAMapItem&
79  operator=(SHAMapItem const& other) = delete;
80 
81  SHAMapItem(SHAMapItem&& other) = delete;
82 
83  SHAMapItem&
84  operator=(SHAMapItem&&) = delete;
85 
86  uint256 const&
87  key() const
88  {
89  return tag_;
90  }
91 
93  size() const
94  {
95  return size_;
96  }
97 
98  void const*
99  data() const
100  {
101  return reinterpret_cast<std::uint8_t const*>(this) + sizeof(*this);
102  }
103 
104  Slice
105  slice() const
106  {
107  return {data(), size()};
108  }
109 };
110 
111 namespace detail {
112 
113 // clang-format off
114 // The slab cutoffs and the number of megabytes per allocation are customized
115 // based on the number of objects of each size we expect to need at any point
116 // in time and with an eye to minimize the number of slack bytes in a block.
117 inline SlabAllocatorSet<SHAMapItem> slabber({
118  { 128, megabytes(std::size_t(60)) },
119  { 192, megabytes(std::size_t(46)) },
120  { 272, megabytes(std::size_t(60)) },
121  { 384, megabytes(std::size_t(56)) },
122  { 564, megabytes(std::size_t(40)) },
123  { 772, megabytes(std::size_t(46)) },
124  { 1052, megabytes(std::size_t(60)) },
125 });
126 // clang-format on
127 
128 } // namespace detail
129 
130 inline void
132 {
133  // This can only happen if someone releases the last reference to the
134  // item while we were trying to increment the refcount.
135  if (x->refcount_++ == 0)
136  LogicError("SHAMapItem: the reference count is 0!");
137 }
138 
139 inline void
141 {
142  if (--x->refcount_ == 0)
143  {
144  auto p = reinterpret_cast<std::uint8_t const*>(x);
145 
146  // The SHAMapItem constuctor isn't trivial (because the destructor
147  // for CountedObject isn't) so we can't avoid calling it here, but
148  // plan for a future where we might not need to.
149  if constexpr (!std::is_trivially_destructible_v<SHAMapItem>)
150  std::destroy_at(x);
151 
152  // If the slabber doens't claim this pointer, it was allocated
153  // manually, so we free it manually.
154  if (!detail::slabber.deallocate(const_cast<std::uint8_t*>(p)))
155  delete[] p;
156  }
157 }
158 
159 inline boost::intrusive_ptr<SHAMapItem>
160 make_shamapitem(uint256 const& tag, Slice data)
161 {
162  assert(data.size() <= megabytes<std::size_t>(16));
163 
164  std::uint8_t* raw = detail::slabber.allocate(data.size());
165 
166  // If we can't grab memory from the slab allocators, we fall back to
167  // the standard library and try to grab a precisely-sized memory block:
168  if (raw == nullptr)
169  raw = new std::uint8_t[sizeof(SHAMapItem) + data.size()];
170 
171  // We do not increment the reference count here on purpose: the
172  // constructor of SHAMapItem explicitly sets it to 1. We use the fact
173  // that the refcount can never be zero before incrementing as an
174  // invariant.
175  return {new (raw) SHAMapItem{tag, data}, false};
176 }
177 
178 static_assert(alignof(SHAMapItem) != 40);
179 static_assert(alignof(SHAMapItem) == 8 || alignof(SHAMapItem) == 4);
180 
181 inline boost::intrusive_ptr<SHAMapItem>
183 {
184  return make_shamapitem(other.key(), other.slice());
185 }
186 
187 } // namespace ripple
188 
189 #endif
ripple::SHAMapItem::intrusive_ptr_add_ref
friend void intrusive_ptr_add_ref(SHAMapItem const *x)
Definition: SHAMapItem.h:131
ripple::SHAMapItem::data
void const * data() const
Definition: SHAMapItem.h:99
ripple::SHAMapItem::SHAMapItem
SHAMapItem()=delete
ripple::CountedObject
Tracks the number of instances of an object.
Definition: CountedObject.h:124
std::destroy_at
T destroy_at(T... args)
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
ripple::make_shamapitem
boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition: SHAMapItem.h:160
ripple::SHAMapItem::operator=
SHAMapItem & operator=(SHAMapItem const &other)=delete
ripple::intrusive_ptr_add_ref
void intrusive_ptr_add_ref(SHAMapItem const *x)
Definition: SHAMapItem.h:131
ripple::SHAMapItem::make_shamapitem
friend boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition: SHAMapItem.h:160
ripple::detail::slabber
SlabAllocatorSet< SHAMapItem > slabber({ { 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)) }, })
ripple::SHAMapItem::key
uint256 const & key() const
Definition: SHAMapItem.h:87
ripple::SHAMapItem::size_
const std::uint32_t size_
Definition: SHAMapItem.h:55
ripple::base_uint< 256 >
ripple::SHAMapItem
Definition: SHAMapItem.h:34
ripple::megabytes
constexpr auto megabytes(T value) noexcept
Definition: ByteUtilities.h:34
ripple::SHAMapItem::size
std::size_t size() const
Definition: SHAMapItem.h:93
std::uint32_t
std::atomic< std::uint32_t >
ripple::SHAMapItem::slice
Slice slice() const
Definition: SHAMapItem.h:105
ripple::SHAMapItem::intrusive_ptr_release
friend void intrusive_ptr_release(SHAMapItem const *x)
Definition: SHAMapItem.h:140
ripple::SHAMapItem::refcount_
std::atomic< std::uint32_t > refcount_
Definition: SHAMapItem.h:58
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
std
STL namespace.
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:48
cassert
ripple::intrusive_ptr_release
void intrusive_ptr_release(SHAMapItem const *x)
Definition: SHAMapItem.h:140
std::size_t
std::memcpy
T memcpy(T... args)
ripple::SHAMapItem::tag_
const uint256 tag_
Definition: SHAMapItem.h:50
ripple::SHAMapItem::SHAMapItem
SHAMapItem(uint256 const &tag, Slice data)
Definition: SHAMapItem.h:64