Skip to content

Commit

Permalink
Merge pull request #535 from evoskuil/master
Browse files Browse the repository at this point in the history
Add and update arraymap and arrayhead tests, fix/simplify impl.
  • Loading branch information
evoskuil authored Jan 11, 2025
2 parents dc6b5be + fdaebf0 commit 6dc275f
Show file tree
Hide file tree
Showing 9 changed files with 654 additions and 1,368 deletions.
30 changes: 17 additions & 13 deletions include/bitcoin/database/impl/primitives/arrayhead.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,20 @@ bool CLASS::enabled() const NOEXCEPT
}

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

return {};
// Put index does not validate, allowing for head expansion.
return putter_index(key);
}

TEMPLATE
Link CLASS::putter_index(size_t key) const NOEXCEPT
{
// Key is the logical bucket index (no-hash).
return body::cast_link(key);
}

TEMPLATE
Expand Down Expand Up @@ -117,15 +123,13 @@ bool CLASS::set_body_count(const Link& count) NOEXCEPT
}

TEMPLATE
Link CLASS::top(const Key& key) const NOEXCEPT
Link CLASS::at(size_t key) const NOEXCEPT
{
return top(index(key));
}
const auto link = index(key);
if (link.is_terminal())
return {};

TEMPLATE
Link CLASS::top(const Link& index) const NOEXCEPT
{
const auto ptr = file_.get(link_to_position(index));
const auto ptr = file_.get(link_to_position(link));
if (is_null(ptr))
return {};

Expand Down
34 changes: 10 additions & 24 deletions include/bitcoin/database/impl/primitives/arraymap.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -129,62 +129,48 @@ code CLASS::reload() NOEXCEPT
// ----------------------------------------------------------------------------

TEMPLATE
Link CLASS::top(const Link& link) const NOEXCEPT
bool CLASS::exists(size_t key) const NOEXCEPT
{
if (link >= head_.buckets())
return {};

return head_.top(link);
return !at(key).is_terminal();
}

TEMPLATE
bool CLASS::exists(const Key& key) const NOEXCEPT
Link CLASS::at(size_t key) const NOEXCEPT
{
return !first(key).is_terminal();
}

TEMPLATE
Link CLASS::first(const Key& key) const NOEXCEPT
{
return head_.top(key);
return head_.at(key);
}

TEMPLATE
template <typename Element, if_equal<Element::size, Size>>
bool CLASS::find(const Key& key, Element& element) const NOEXCEPT
bool CLASS::at(size_t key, Element& element) const NOEXCEPT
{
// This override avoids duplicated memory_ptr construct in get(first()).
const auto ptr = body_.get();
return read(ptr, first(ptr, head_.top(key), key), element);
return get(at(key), element);
}

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

TEMPLATE
template <typename Element, if_equal<Element::size, Size>>
bool CLASS::put(const Key& key, const Element& element) NOEXCEPT
bool CLASS::put(size_t key, const Element& element) NOEXCEPT
{
using namespace system;
const auto count = element.count();
const auto link = allocate(count);
const auto link = body_.allocate(count);
const auto ptr = body_.get(link);
if (!ptr)
return false;

// iostream.flush is a nop (direct copy).
iostream stream{ *ptr };
finalizer sink{ stream };
sink.skip_bytes(Link::size);
sink.write_bytes(key);

if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(Size * count);) }
return element.to_data(sink) && head_.push(link, head_.index(key));
return element.to_data(sink) && head_.push(link, head_.putter_index(key));
}

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

using namespace system;
const auto start = body::link_to_position(link);
if (is_limited<ptrdiff_t>(start))
return false;
Expand Down
20 changes: 13 additions & 7 deletions include/bitcoin/database/primitives/arrayhead.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace database {

/// Dynamically expanding array map header.
/// Less efficient than a fixed-size header.
template <typename Link, typename Key = size_t>
template <typename Link>
class arrayhead
{
public:
Expand Down Expand Up @@ -57,15 +57,21 @@ class arrayhead
bool get_body_count(Link& count) const NOEXCEPT;
bool set_body_count(const Link& count) NOEXCEPT;

/// Convert natural key to head bucket index.
Link index(const Key& key) const NOEXCEPT;
/// Convert natural key to head bucket index (validated).
Link index(size_t key) const NOEXCEPT;

/// Convert natural key to head bucket index (unvalidated).
Link putter_index(size_t key) const NOEXCEPT;

/// Unsafe if verify false.
Link top(const Key& key) const NOEXCEPT;
Link top(const Link& index) const NOEXCEPT;
Link at(size_t key) const NOEXCEPT;

/// Assign value to bucket index.
bool push(const bytes& current, const Link& index) NOEXCEPT;

private:
using body = manager<Link, system::data_array<zero>, Link::size>;

template <size_t Bytes>
static auto& array_cast(memory::iterator buffer) NOEXCEPT
{
Expand Down Expand Up @@ -98,8 +104,8 @@ class arrayhead
} // namespace database
} // namespace libbitcoin

#define TEMPLATE template <typename Link, typename Key>
#define CLASS arrayhead<Link, Key>
#define TEMPLATE template <typename Link>
#define CLASS arrayhead<Link>

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

Expand Down
26 changes: 10 additions & 16 deletions include/bitcoin/database/primitives/arraymap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,13 @@ namespace database {
/// Readers and writers are always prepositioned at data, and are limited to
/// the extent the record/slab size is known (limit can always be removed).
/// Streams are always initialized from first element byte up to file limit.
template <typename Link, typename Key, size_t Size>
template <typename Link, size_t Size>
class arraymap
{
public:
DEFAULT_COPY_MOVE_DESTRUCT(arraymap);

using key = Key;
using link = Link;
using iterator = database::iterator<Link, Key, Size>;

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

Expand Down Expand Up @@ -89,18 +87,15 @@ class arraymap
/// Query interface, iterator is not thread safe.
/// -----------------------------------------------------------------------

/// Return the link at the top of the conflict list (for table scanning).
Link top(const Link& list) const NOEXCEPT;

/// True if an instance of object with key exists.
bool exists(const Key& key) const NOEXCEPT;
bool exists(size_t key) const NOEXCEPT;

/// Return first element link or terminal if not found/error.
Link first(const Key& key) const NOEXCEPT;
/// Return element link at key or terminal if not found/error.
Link at(size_t key) const NOEXCEPT;

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

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

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

using head = database::arrayhead<Link>;
using body = database::manager<Link, Key, Size>;
using body = database::manager<Link, system::data_array<0>, Size>;

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

template <typename Element>
using array_map = arraymap<linkage<Element::pk>, system::data_array<Element::sk>,
Element::size>;
using array_map = arraymap<linkage<Element::pk>, Element::size>;

} // namespace database
} // namespace libbitcoin

#define TEMPLATE template <typename Link, typename Key, size_t Size>
#define CLASS arraymap<Link, Key, Size>
#define TEMPLATE template <typename Link, size_t Size>
#define CLASS arraymap<Link, Size>

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

Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/database/tables/schema.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ namespace schema
struct prevout
{
static constexpr size_t pk = schema::spend_;
static constexpr size_t sk = zero;
////static constexpr size_t sk = zero;
static constexpr size_t minsize =
schema::bit + // TODO: merge bit.
schema::spend_ +
Expand Down
22 changes: 14 additions & 8 deletions test/mocks/chunk_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,20 @@ size_t chunk_storage::allocate(size_t chunk) NOEXCEPT
memory_ptr chunk_storage::set(size_t offset, size_t size,
uint8_t backfill) NOEXCEPT
{
std::unique_lock field_lock(field_mutex_);
if (system::is_add_overflow(offset, size))
return {};

std::unique_lock map_lock(map_mutex_);
const auto minimum = offset + size;
if (minimum > buffer_.size())
buffer_.resize(minimum, backfill);
{
std::unique_lock field_lock(field_mutex_);
if (system::is_add_overflow(offset, size))
{
return {};
}
else
{
std::unique_lock map_lock(map_mutex_);
const auto minimum = offset + size;
if (minimum > buffer_.size())
buffer_.resize(minimum, backfill);
}
}

return get(offset);
}
Expand Down
22 changes: 3 additions & 19 deletions test/primitives/arrayhead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ constexpr auto buckets = sub1(links);
static_assert(buckets == 20u);

using link = linkage<link_size>;
using test_header = arrayhead<link, size_t>;
using test_header = arrayhead<link>;

class nullptr_storage
: public test::chunk_storage
Expand Down Expand Up @@ -103,30 +103,14 @@ BOOST_AUTO_TEST_CASE(arrayhead__set_body_count__get__expected)
BOOST_REQUIRE_EQUAL(count, expected);
}

BOOST_AUTO_TEST_CASE(arrayhead__top__link__terminal)
{
test::chunk_storage store;
test_header head{ store, buckets };
BOOST_REQUIRE(head.create());
BOOST_REQUIRE(head.top(9).is_terminal());
}

BOOST_AUTO_TEST_CASE(arrayhead__top__nullptr__terminal)
{
nullptr_storage store;
test_header head{ store, buckets };
BOOST_REQUIRE(head.create());
BOOST_REQUIRE(head.top(9).is_terminal());
}

BOOST_AUTO_TEST_CASE(arrayhead__top__key__terminal)
BOOST_AUTO_TEST_CASE(arrayhead__at__key__terminal)
{
test::chunk_storage store;
test_header head{ store, buckets };

// create() allocates and fills buckets with terminal.
BOOST_REQUIRE(head.create());
BOOST_REQUIRE(head.top(zero).is_terminal());
BOOST_REQUIRE(head.at(zero).is_terminal());
}

BOOST_AUTO_TEST_SUITE_END()
Loading

0 comments on commit 6dc275f

Please sign in to comment.