Skip to content

Commit 6dc275f

Browse files
authored
Merge pull request #535 from evoskuil/master
Add and update arraymap and arrayhead tests, fix/simplify impl.
2 parents dc6b5be + fdaebf0 commit 6dc275f

File tree

9 files changed

+654
-1368
lines changed

9 files changed

+654
-1368
lines changed

include/bitcoin/database/impl/primitives/arrayhead.ipp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,20 @@ bool CLASS::enabled() const NOEXCEPT
5353
}
5454

5555
TEMPLATE
56-
Link CLASS::index(const Key& key) const NOEXCEPT
56+
Link CLASS::index(size_t key) const NOEXCEPT
5757
{
58-
// Key is the logical bucket index (no-hash).
59-
if (key < buckets())
60-
return manager<Link, system::data_array<zero>, Link::size>::
61-
cast_link(key);
58+
if (key >= buckets())
59+
return {};
6260

63-
return {};
61+
// Put index does not validate, allowing for head expansion.
62+
return putter_index(key);
63+
}
64+
65+
TEMPLATE
66+
Link CLASS::putter_index(size_t key) const NOEXCEPT
67+
{
68+
// Key is the logical bucket index (no-hash).
69+
return body::cast_link(key);
6470
}
6571

6672
TEMPLATE
@@ -117,15 +123,13 @@ bool CLASS::set_body_count(const Link& count) NOEXCEPT
117123
}
118124

119125
TEMPLATE
120-
Link CLASS::top(const Key& key) const NOEXCEPT
126+
Link CLASS::at(size_t key) const NOEXCEPT
121127
{
122-
return top(index(key));
123-
}
128+
const auto link = index(key);
129+
if (link.is_terminal())
130+
return {};
124131

125-
TEMPLATE
126-
Link CLASS::top(const Link& index) const NOEXCEPT
127-
{
128-
const auto ptr = file_.get(link_to_position(index));
132+
const auto ptr = file_.get(link_to_position(link));
129133
if (is_null(ptr))
130134
return {};
131135

include/bitcoin/database/impl/primitives/arraymap.ipp

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -129,62 +129,48 @@ code CLASS::reload() NOEXCEPT
129129
// ----------------------------------------------------------------------------
130130

131131
TEMPLATE
132-
Link CLASS::top(const Link& link) const NOEXCEPT
132+
bool CLASS::exists(size_t key) const NOEXCEPT
133133
{
134-
if (link >= head_.buckets())
135-
return {};
136-
137-
return head_.top(link);
134+
return !at(key).is_terminal();
138135
}
139136

140137
TEMPLATE
141-
bool CLASS::exists(const Key& key) const NOEXCEPT
138+
Link CLASS::at(size_t key) const NOEXCEPT
142139
{
143-
return !first(key).is_terminal();
144-
}
145-
146-
TEMPLATE
147-
Link CLASS::first(const Key& key) const NOEXCEPT
148-
{
149-
return head_.top(key);
140+
return head_.at(key);
150141
}
151142

152143
TEMPLATE
153144
template <typename Element, if_equal<Element::size, Size>>
154-
bool CLASS::find(const Key& key, Element& element) const NOEXCEPT
145+
bool CLASS::at(size_t key, Element& element) const NOEXCEPT
155146
{
156-
// This override avoids duplicated memory_ptr construct in get(first()).
157-
const auto ptr = body_.get();
158-
return read(ptr, first(ptr, head_.top(key), key), element);
147+
return get(at(key), element);
159148
}
160149

161150
TEMPLATE
162151
template <typename Element, if_equal<Element::size, Size>>
163152
bool CLASS::get(const Link& link, Element& element) const NOEXCEPT
164153
{
165-
// This override is the normal form.
166154
return read(body_.get(), link, element);
167155
}
168156

169157
TEMPLATE
170158
template <typename Element, if_equal<Element::size, Size>>
171-
bool CLASS::put(const Key& key, const Element& element) NOEXCEPT
159+
bool CLASS::put(size_t key, const Element& element) NOEXCEPT
172160
{
173161
using namespace system;
174162
const auto count = element.count();
175-
const auto link = allocate(count);
163+
const auto link = body_.allocate(count);
176164
const auto ptr = body_.get(link);
177165
if (!ptr)
178166
return false;
179167

180168
// iostream.flush is a nop (direct copy).
181169
iostream stream{ *ptr };
182170
finalizer sink{ stream };
183-
sink.skip_bytes(Link::size);
184-
sink.write_bytes(key);
185171

186172
if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(Size * count);) }
187-
return element.to_data(sink) && head_.push(link, head_.index(key));
173+
return element.to_data(sink) && head_.push(link, head_.putter_index(key));
188174
}
189175

190176
// protected
@@ -195,10 +181,10 @@ template <typename Element, if_equal<Element::size, Size>>
195181
bool CLASS::read(const memory_ptr& ptr, const Link& link,
196182
Element& element) NOEXCEPT
197183
{
184+
using namespace system;
198185
if (!ptr || link.is_terminal())
199186
return false;
200187

201-
using namespace system;
202188
const auto start = body::link_to_position(link);
203189
if (is_limited<ptrdiff_t>(start))
204190
return false;

include/bitcoin/database/primitives/arrayhead.hpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace database {
2929

3030
/// Dynamically expanding array map header.
3131
/// Less efficient than a fixed-size header.
32-
template <typename Link, typename Key = size_t>
32+
template <typename Link>
3333
class arrayhead
3434
{
3535
public:
@@ -57,15 +57,21 @@ class arrayhead
5757
bool get_body_count(Link& count) const NOEXCEPT;
5858
bool set_body_count(const Link& count) NOEXCEPT;
5959

60-
/// Convert natural key to head bucket index.
61-
Link index(const Key& key) const NOEXCEPT;
60+
/// Convert natural key to head bucket index (validated).
61+
Link index(size_t key) const NOEXCEPT;
62+
63+
/// Convert natural key to head bucket index (unvalidated).
64+
Link putter_index(size_t key) const NOEXCEPT;
6265

6366
/// Unsafe if verify false.
64-
Link top(const Key& key) const NOEXCEPT;
65-
Link top(const Link& index) const NOEXCEPT;
67+
Link at(size_t key) const NOEXCEPT;
68+
69+
/// Assign value to bucket index.
6670
bool push(const bytes& current, const Link& index) NOEXCEPT;
6771

6872
private:
73+
using body = manager<Link, system::data_array<zero>, Link::size>;
74+
6975
template <size_t Bytes>
7076
static auto& array_cast(memory::iterator buffer) NOEXCEPT
7177
{
@@ -98,8 +104,8 @@ class arrayhead
98104
} // namespace database
99105
} // namespace libbitcoin
100106

101-
#define TEMPLATE template <typename Link, typename Key>
102-
#define CLASS arrayhead<Link, Key>
107+
#define TEMPLATE template <typename Link>
108+
#define CLASS arrayhead<Link>
103109

104110
#include <bitcoin/database/impl/primitives/arrayhead.ipp>
105111

include/bitcoin/database/primitives/arraymap.hpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,13 @@ namespace database {
3535
/// Readers and writers are always prepositioned at data, and are limited to
3636
/// the extent the record/slab size is known (limit can always be removed).
3737
/// Streams are always initialized from first element byte up to file limit.
38-
template <typename Link, typename Key, size_t Size>
38+
template <typename Link, size_t Size>
3939
class arraymap
4040
{
4141
public:
4242
DEFAULT_COPY_MOVE_DESTRUCT(arraymap);
4343

44-
using key = Key;
4544
using link = Link;
46-
using iterator = database::iterator<Link, Key, Size>;
4745

4846
arraymap(storage& header, storage& body, const Link& buckets) NOEXCEPT;
4947

@@ -89,18 +87,15 @@ class arraymap
8987
/// Query interface, iterator is not thread safe.
9088
/// -----------------------------------------------------------------------
9189

92-
/// Return the link at the top of the conflict list (for table scanning).
93-
Link top(const Link& list) const NOEXCEPT;
94-
9590
/// True if an instance of object with key exists.
96-
bool exists(const Key& key) const NOEXCEPT;
91+
bool exists(size_t key) const NOEXCEPT;
9792

98-
/// Return first element link or terminal if not found/error.
99-
Link first(const Key& key) const NOEXCEPT;
93+
/// Return element link at key or terminal if not found/error.
94+
Link at(size_t key) const NOEXCEPT;
10095

10196
/// Get first element matching the search key, false if not found/error.
10297
template <typename Element, if_equal<Element::size, Size> = true>
103-
bool find(const Key& key, Element& element) const NOEXCEPT;
98+
bool at(size_t key, Element& element) const NOEXCEPT;
10499

105100
/// Get element at link, false if deserialize error.
106101
template <typename Element, if_equal<Element::size, Size> = true>
@@ -109,7 +104,7 @@ class arraymap
109104
/// Allocate, set, commit element to key.
110105
/// Expands table AND HEADER as necessary.
111106
template <typename Element, if_equal<Element::size, Size> = true>
112-
bool put(const Key& key, const Element& element) NOEXCEPT;
107+
bool put(size_t key, const Element& element) NOEXCEPT;
113108

114109
protected:
115110
/// Get element at link using memory object, false if deserialize error.
@@ -121,7 +116,7 @@ class arraymap
121116
static constexpr auto is_slab = (Size == max_size_t);
122117

123118
using head = database::arrayhead<Link>;
124-
using body = database::manager<Link, Key, Size>;
119+
using body = database::manager<Link, system::data_array<0>, Size>;
125120

126121
// Thread safe (index/top/push).
127122
// Not thread safe (create/open/close/backup/restore).
@@ -132,14 +127,13 @@ class arraymap
132127
};
133128

134129
template <typename Element>
135-
using array_map = arraymap<linkage<Element::pk>, system::data_array<Element::sk>,
136-
Element::size>;
130+
using array_map = arraymap<linkage<Element::pk>, Element::size>;
137131

138132
} // namespace database
139133
} // namespace libbitcoin
140134

141-
#define TEMPLATE template <typename Link, typename Key, size_t Size>
142-
#define CLASS arraymap<Link, Key, Size>
135+
#define TEMPLATE template <typename Link, size_t Size>
136+
#define CLASS arraymap<Link, Size>
143137

144138
#include <bitcoin/database/impl/primitives/arraymap.ipp>
145139

include/bitcoin/database/tables/schema.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ namespace schema
359359
struct prevout
360360
{
361361
static constexpr size_t pk = schema::spend_;
362-
static constexpr size_t sk = zero;
362+
////static constexpr size_t sk = zero;
363363
static constexpr size_t minsize =
364364
schema::bit + // TODO: merge bit.
365365
schema::spend_ +

test/mocks/chunk_storage.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,20 @@ size_t chunk_storage::allocate(size_t chunk) NOEXCEPT
124124
memory_ptr chunk_storage::set(size_t offset, size_t size,
125125
uint8_t backfill) NOEXCEPT
126126
{
127-
std::unique_lock field_lock(field_mutex_);
128-
if (system::is_add_overflow(offset, size))
129-
return {};
130-
131-
std::unique_lock map_lock(map_mutex_);
132-
const auto minimum = offset + size;
133-
if (minimum > buffer_.size())
134-
buffer_.resize(minimum, backfill);
127+
{
128+
std::unique_lock field_lock(field_mutex_);
129+
if (system::is_add_overflow(offset, size))
130+
{
131+
return {};
132+
}
133+
else
134+
{
135+
std::unique_lock map_lock(map_mutex_);
136+
const auto minimum = offset + size;
137+
if (minimum > buffer_.size())
138+
buffer_.resize(minimum, backfill);
139+
}
140+
}
135141

136142
return get(offset);
137143
}

test/primitives/arrayhead.cpp

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ constexpr auto buckets = sub1(links);
3535
static_assert(buckets == 20u);
3636

3737
using link = linkage<link_size>;
38-
using test_header = arrayhead<link, size_t>;
38+
using test_header = arrayhead<link>;
3939

4040
class nullptr_storage
4141
: public test::chunk_storage
@@ -103,30 +103,14 @@ BOOST_AUTO_TEST_CASE(arrayhead__set_body_count__get__expected)
103103
BOOST_REQUIRE_EQUAL(count, expected);
104104
}
105105

106-
BOOST_AUTO_TEST_CASE(arrayhead__top__link__terminal)
107-
{
108-
test::chunk_storage store;
109-
test_header head{ store, buckets };
110-
BOOST_REQUIRE(head.create());
111-
BOOST_REQUIRE(head.top(9).is_terminal());
112-
}
113-
114-
BOOST_AUTO_TEST_CASE(arrayhead__top__nullptr__terminal)
115-
{
116-
nullptr_storage store;
117-
test_header head{ store, buckets };
118-
BOOST_REQUIRE(head.create());
119-
BOOST_REQUIRE(head.top(9).is_terminal());
120-
}
121-
122-
BOOST_AUTO_TEST_CASE(arrayhead__top__key__terminal)
106+
BOOST_AUTO_TEST_CASE(arrayhead__at__key__terminal)
123107
{
124108
test::chunk_storage store;
125109
test_header head{ store, buckets };
126110

127111
// create() allocates and fills buckets with terminal.
128112
BOOST_REQUIRE(head.create());
129-
BOOST_REQUIRE(head.top(zero).is_terminal());
113+
BOOST_REQUIRE(head.at(zero).is_terminal());
130114
}
131115

132116
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)