From 28fda91a498a9d62306ca0dd9140cac4e54f5754 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Thu, 23 Jul 2026 21:06:11 -0400 Subject: [PATCH 01/12] Posix memory map write staging. --- include/bitcoin/database/impl/memory/mmap.ipp | 10 +- .../database/impl/memory/mmap_private.ipp | 401 +++++++++++++++++- .../database/impl/memory/mmap_storage.ipp | 49 ++- include/bitcoin/database/impl/store/store.ipp | 42 +- include/bitcoin/database/memory/mman.hpp | 32 ++ include/bitcoin/database/memory/mmap.hpp | 55 ++- include/bitcoin/database/memory/mmaps.hpp | 8 +- include/bitcoin/database/store.hpp | 4 + .../bitcoin/database/tables/caches/ecdsa.hpp | 4 +- .../database/tables/caches/schnorr.hpp | 4 +- .../bitcoin/database/tables/caches/silent.hpp | 4 +- src/memory/mman.cpp | 87 ++++ test/memory/mmap.cpp | 173 ++++++++ test/mocks/chunk_storage.hpp | 8 +- 14 files changed, 827 insertions(+), 54 deletions(-) diff --git a/include/bitcoin/database/impl/memory/mmap.ipp b/include/bitcoin/database/impl/memory/mmap.ipp index 18071a865..621ef877c 100644 --- a/include/bitcoin/database/impl/memory/mmap.ipp +++ b/include/bitcoin/database/impl/memory/mmap.ipp @@ -33,24 +33,26 @@ namespace database { TEMPLATE CLASS::mmap(const path& filename, size_t minimum, size_t expansion, - bool random) NOEXCEPT + bool random, bool staged) NOEXCEPT requires (is_one(columns)) : filenames_{ filename }, minimum_(to_rows(minimum)), expansion_(expansion), random_(random), + staged_(staged), opened_{ file::invalid } { } TEMPLATE CLASS::mmap(const paths& filenames, size_t minimum, size_t expansion, - bool random) NOEXCEPT + bool random, bool staged) NOEXCEPT requires (columns > one) : filenames_(filenames), minimum_(to_rows(minimum)), expansion_(expansion), random_(random), + staged_(staged), opened_{} { opened_.fill(file::invalid); @@ -66,6 +68,10 @@ CLASS::~mmap() NOEXCEPT [](auto map) NOEXCEPT { return is_null(map); })); BC_ASSERT(std::ranges::all_of(opened_, [](auto opened) NOEXCEPT { return opened == file::invalid; })); +#if defined(HAVE_STAGING) + BC_ASSERT(std::ranges::all_of(reserved_, + [](auto reserved) NOEXCEPT { return is_zero(reserved); })); +#endif } TEMPLATE diff --git a/include/bitcoin/database/impl/memory/mmap_private.ipp b/include/bitcoin/database/impl/memory/mmap_private.ipp index b32266312..adf69d79b 100644 --- a/include/bitcoin/database/impl/memory/mmap_private.ipp +++ b/include/bitcoin/database/impl/memory/mmap_private.ipp @@ -34,15 +34,33 @@ namespace database { TEMPLATE template -bool CLASS::flush_all_(std::index_sequence) NOEXCEPT +bool CLASS::flush_all_(size_t rows, std::index_sequence) NOEXCEPT { - return (flush_() && ...); + return (flush_(rows) && ...); } TEMPLATE template bool CLASS::map_all_(std::index_sequence) NOEXCEPT { +#if defined(HAVE_STAGING) + // Get page size (usually 4KB), one bit ensures a power of two as required. + const int page_size = ::sysconf(_SC_PAGESIZE); + const auto page = system::possible_narrow_sign_cast(page_size); + + if (page_size == fail || !is_one(system::ones_count(page))) + { + set_first_code(error::sysconf_failure); + capacity_.store(zero); + return false; + } + + page_ = page; + + // File content is fully flushed by definition at load. + settled_.store(staged_ ? logical_.load() : zero); +#endif + if (!(map_() && ...)) { capacity_.store(zero); @@ -60,6 +78,11 @@ bool CLASS::unmap_all_(std::index_sequence) NOEXCEPT const auto capacity = capacity_.load(); const auto success = (unmap_(capacity) && ...); capacity_.store(zero); + +#if defined(HAVE_STAGING) + settled_.store(zero); +#endif + return success; } @@ -84,13 +107,32 @@ bool CLASS::remap_all_(size_t capacity, std::index_sequence) NOEXCEPT // Never results in unmapped. TEMPLATE template -bool CLASS::flush_() NOEXCEPT +bool CLASS::flush_(size_t + #if defined(HAVE_STAGING) || defined(HAVE_MSC) + rows + #endif +) NOEXCEPT { -#if defined(HAVE_MSC) +#if defined(HAVE_STAGING) + // Transfer unflushed rows from anonymous memory to the file. Settled rows + // are already on disk (staged); unstaged content is written in full. + const auto from = to_width(staged_ ? settled_.load() : zero); + const auto to = to_width(rows); + + const auto success = + ((from >= to) || pwrite_all(opened_[Column], + std::next(memory_map_[Column], from), to - from, from)) +#if defined(F_FULLFSYNC) + // non-standard macOS behavior: news.ycombinator.com/item?id=30372218 + && (::fcntl(opened_[Column], F_FULLFSYNC, 0) != fail); +#else + && (::fsync(opened_[Column]) != fail); +#endif +#elif defined(HAVE_MSC) // unmap (and therefore msync) must be called before ftruncate. // "To flush all the dirty pages plus the metadata for the file and ensure // that they are physically written to disk..." - const auto size = to_width(logical_.load()); + const auto size = to_width(rows); const auto success = (::msync(memory_map_[Column], size, MS_SYNC) != fail) && (::fsync(opened_[Column]) != fail); @@ -137,11 +179,34 @@ bool CLASS::release_(size_t size) NOEXCEPT // Always results in unmapped, trims to logical (can be zero). TEMPLATE template -bool CLASS::unmap_(size_t size) NOEXCEPT +bool CLASS::unmap_(size_t + #if !defined(HAVE_STAGING) + size + #endif +) NOEXCEPT { const auto logical = to_width(logical_.load()); -#if defined(HAVE_MSC) +#if defined(HAVE_STAGING) + // Persist unflushed rows, trim preallocation to logical, sync to disk. + const auto from = to_width(staged_ ? settled_.load() : zero); + const auto transferred = + ((from >= logical) || pwrite_all(opened_[Column], + std::next(memory_map_[Column], from), logical - from, from)) + && (::ftruncate(opened_[Column], logical) != fail) +#if defined(F_FULLFSYNC) + && (::fcntl(opened_[Column], F_FULLFSYNC, 0) != fail); +#else + && (::fsync(opened_[Column]) != fail); +#endif + + // Order ensures release of the reservation in case of transfer failure. + const auto success = (::munmap(memory_map_[Column], + reserved_[Column]) != fail) && transferred; + + memory_map_[Column] = {}; + reserved_[Column] = zero; +#elif defined(HAVE_MSC) // Windows cannot resize a mapped file. // msync requires the live mapping, ftruncate requires it gone. const auto synced = @@ -179,6 +244,9 @@ TEMPLATE template bool CLASS::map_() NOEXCEPT { +#if defined(HAVE_STAGING) + return stage_(); +#else auto size = logical_.load(); // Cannot map empty file, and want minimum capacity, so expand as required. @@ -191,6 +259,7 @@ bool CLASS::map_() NOEXCEPT MAP_SHARED, opened_[Column], 0)); return finalize_(size); +#endif } // Remap failure results in unmapped. @@ -205,6 +274,15 @@ bool CLASS::remap_(size_t size) NOEXCEPT if (is_zero(size)) size = minimum_; +#if defined(HAVE_STAGING) + // The file is preallocated to capacity, preserving disk full detection at + // allocation, and growth commits reserved anonymous pages in place, so no + // mapping is released and the map base is stable within the reservation. + if (!resize_(size)) + return false; + + return commit_(size); +#else #if !defined(HAVE_MSC) && !defined(MREMAP_MAYMOVE) // macOS cannot remap in place, so release the mapping without trimming and // the file remains at capacity_ bytes and resize_'s fallocate delta (and @@ -248,6 +326,7 @@ bool CLASS::remap_(size_t size) NOEXCEPT #endif return finalize_(size); +#endif // HAVE_STAGING } // disk_full: space is set but no code is set with false return. @@ -342,6 +421,314 @@ bool CLASS::finalize_(size_t return true; } +#if defined(HAVE_STAGING) + +// staging dispatch, not thread safe. +// ---------------------------------------------------------------------------- +// private + +TEMPLATE +template +bool CLASS::settle_all_(size_t rows, std::index_sequence) NOEXCEPT +{ + const auto from = settled_.load(); + if (!(settle_(from, rows) && ...)) + return false; + + settled_.store(rows); + return true; +} + +TEMPLATE +template +bool CLASS::unsettle_all_(size_t rows, std::index_sequence) NOEXCEPT +{ + if (!(unsettle_(rows) && ...)) + return false; + + settled_.store(rows); + return true; +} + +// staging wrappers, not thread safe. +// ---------------------------------------------------------------------------- +// private + +// Stage failure results in unmapped. +// Staging has no effect on logical size, commits max(logical, min) capacity. +TEMPLATE +template +bool CLASS::stage_() NOEXCEPT +{ + auto size = logical_.load(); + + // Cannot map empty file, and want minimum capacity, so expand as required. + // disk_full: space is set but no code is set with false return. + if ((size < minimum_) && !resize_(size = minimum_)) + return false; + + // Reserve address space with generous multiple of capacity (costless). + const auto reserved = page_ceiling(to_width(to_reservation(size))); + const auto base = mmap_reserve(reserved); + + if (base == MAP_FAILED) + { + set_first_code(error::mmap_failure); + return false; + } + + memory_map_[Column] = system::pointer_cast(base); + reserved_[Column] = reserved; + + // Commit anonymous pages above the settle boundary page floor. + const auto settled = page_floor(to_width(settled_.load())); + const auto target = to_width(size); + + if ((target > settled) && (mmap_commit(std::next(memory_map_[Column], + settled), target - settled) == fail)) + { + teardown_(error::mmap_failure); + return false; + } + + // Populate anonymous memory from the file (unstaged content in full, + // staged only the settle boundary page remainder). + const auto logical = to_width(logical_.load()); + + if ((settled < logical) && !pread_all(opened_[Column], + std::next(memory_map_[Column], settled), logical - settled, settled)) + { + teardown_(error::fsync_failure); + return false; + } + + // Convert the settled prefix to a read-only file mapping. + if (!settle_(zero, settled_.load())) + return false; + + loaded_.store(true); + return true; +} + +// Commit failure results in unmapped. +// Growth within the reservation commits pages in place (stable map base); an +// exhausted reservation is replaced and its unsettled content copied, under +// the exclusive remap lock held by the caller. +TEMPLATE +template +bool CLASS::commit_(size_t size) NOEXCEPT +{ + const auto target = to_width(size); + + if (target <= reserved_[Column]) + { + // Never commit below the settle boundary page floor (the settled + // prefix is a read-only file mapping); recommit is idempotent. + const auto settled = page_floor(to_width(settled_.load())); + const auto current = page_floor(to_width(capacity_.load())); + const auto from = std::max(settled, current); + + if ((target > from) && (mmap_commit(std::next(memory_map_[Column], + from), target - from) == fail)) + { + teardown_(error::mmap_failure); + return false; + } + + return true; + } + + // Reservation exhausted, so reserve larger and migrate (rare by sizing). + const auto reserved = page_ceiling(to_width(to_reservation(size))); + const auto replace = mmap_reserve(reserved); + + if (replace == MAP_FAILED) + { + teardown_(error::mmap_failure); + return false; + } + + const auto base = system::pointer_cast(replace); + const auto settled = page_floor(to_width(settled_.load())); + + if (mmap_commit(std::next(base, settled), target - settled) == fail) + { + ::munmap(replace, reserved); + teardown_(error::mmap_failure); + return false; + } + + // Copy unsettled content (writes are excluded by the remap lock). + const auto logical = to_width(logical_.load()); + + if (settled < logical) + std::copy_n(std::next(memory_map_[Column], settled), + logical - settled, std::next(base, settled)); + + // Convert the settled prefix on the replacement reservation. + if (!is_zero(settled) && + (mmap_settle(replace, settled, opened_[Column], zero) == fail)) + { + ::munmap(replace, reserved); + teardown_(error::mmap_failure); + return false; + } + +#if !defined(WITHOUT_MADVISE) + if (!is_zero(settled) && !advise_(base, settled)) + { + ::munmap(replace, reserved); + teardown_(error::madvise_failure); + return false; + } +#endif + + // Release the exhausted reservation and adopt the replacement. + const auto released = ::munmap(memory_map_[Column], + reserved_[Column]) != fail; + + memory_map_[Column] = base; + reserved_[Column] = reserved; + + if (!released) + { + set_first_code(error::munmap_failure); + return false; + } + + return true; +} + +// Convert flushed rows [from, to) to a read-only shared file mapping, page +// floored so the settle boundary page remains anonymous with its settled +// bytes retained. Releases the covered anonymous pages. Failure results in +// unmapped. +TEMPLATE +template +bool CLASS::settle_(size_t from, size_t to) NOEXCEPT +{ + if (!staged_) + return true; + + const auto begin = page_floor(to_width(from)); + const auto end = page_floor(to_width(to)); + + if (begin == end) + return true; + + const auto address = std::next(memory_map_[Column], begin); + + if (mmap_settle(address, end - begin, opened_[Column], begin) == fail) + { + teardown_(error::mmap_failure); + return false; + } + +#if !defined(WITHOUT_MADVISE) + if (!advise_(address, end - begin)) + { + teardown_(error::madvise_failure); + return false; + } +#endif + + return true; +} + +// Revert settled pages at/above rows to committed anonymous memory and +// restore the retained bytes below rows from the file (truncation below the +// settle boundary). Failure results in unmapped. +TEMPLATE +template +bool CLASS::unsettle_(size_t rows) NOEXCEPT +{ + const auto bytes = to_width(rows); + const auto begin = page_floor(bytes); + const auto end = page_floor(to_width(settled_.load())); + + if (begin == end) + return true; + + const auto address = std::next(memory_map_[Column], begin); + + if (mmap_unsettle(address, end - begin) == fail) + { + teardown_(error::mmap_failure); + return false; + } + + if ((begin < bytes) && !pread_all(opened_[Column], address, bytes - begin, + begin)) + { + teardown_(error::fsync_failure); + return false; + } + + return true; +} + +// Teardown results in unmapped (release failure is not further reported). +TEMPLATE +template +void CLASS::teardown_(const error::error_t& ec) NOEXCEPT +{ + set_first_code(ec); + + if (!is_null(memory_map_[Column])) + ::munmap(memory_map_[Column], reserved_[Column]); + + memory_map_[Column] = {}; + reserved_[Column] = zero; + loaded_.store(false); +} + +// staging utilities, not thread safe. +// ---------------------------------------------------------------------------- +// private + +#if !defined(WITHOUT_MADVISE) +TEMPLATE +bool CLASS::advise_(uint8_t* map, size_t size) const NOEXCEPT +{ + // Use 1GB chunks to avoid large-length issues. + constexpr auto chunk = system::power2(30u); + const auto advice = random_ ? MADV_RANDOM : MADV_SEQUENTIAL; + + for (auto offset = zero; offset < size; offset += chunk) + { + const auto length = std::min(chunk, size - offset); + if (::madvise(std::next(map, offset), length, advice) == fail) + return false; + } + + return true; +} +#endif // WITHOUT_MADVISE + +// Reservation is address space only (costless), so multiply for headroom; +// exhaustion is handled by reservation replacement. +TEMPLATE +size_t CLASS::to_reservation(size_t rows) const NOEXCEPT +{ + constexpr size_t headroom = 4; + return system::ceilinged_multiply(to_capacity(std::max(rows, minimum_)), + headroom); +} + +TEMPLATE +size_t CLASS::page_floor(size_t bytes) const NOEXCEPT +{ + return system::bit_and(bytes, system::bit_not(sub1(page_))); +} + +TEMPLATE +size_t CLASS::page_ceiling(size_t bytes) const NOEXCEPT +{ + return page_floor(system::ceilinged_add(bytes, sub1(page_))); +} + +#endif // HAVE_STAGING + } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/memory/mmap_storage.ipp b/include/bitcoin/database/impl/memory/mmap_storage.ipp index 64eb881e1..54868179d 100644 --- a/include/bitcoin/database/impl/memory/mmap_storage.ipp +++ b/include/bitcoin/database/impl/memory/mmap_storage.ipp @@ -176,14 +176,39 @@ code CLASS::reload() NOEXCEPT TEMPLATE code CLASS::flush() NOEXCEPT { - // Prevent unload, resize, remap. - std::shared_lock map_lock(remap_mutex_); + // The suspend-writes contract holds logical_ stable across both phases. + size_t rows{}; - if (!loaded_.load()) - return error::flush_unloaded; + { + // Prevent unload, resize, remap. + std::shared_lock map_lock(remap_mutex_); + + if (!loaded_.load()) + return error::flush_unloaded; + + rows = logical_.load(); + + // Reads fields and the memory map. + if (!flush_all_(rows, sequence{})) + return error::flush_failure; + } - // Reads fields and the memory map. - return flush_all_(sequence{}) ? error::success : error::flush_failure; +#if defined(HAVE_STAGING) + // Convert flushed rows to read-only file mappings, releasing their + // anonymous pages to clean file cache (excludes accessors, briefly). + if (staged_) + { + std::unique_lock map_lock(remap_mutex_); + + if (!loaded_.load()) + return error::flush_unloaded; + + if (!settle_all_(rows, sequence{})) + return error::flush_failure; + } +#endif + + return error::success; } // Suspend writes before calling. @@ -283,6 +308,18 @@ bool CLASS::truncate(size_t count) NOEXCEPT if (count > logical_.load()) return false; +#if defined(HAVE_STAGING) + // Truncation below the settle boundary reverts settled rows to anonymous + // memory, as append-only bodies may re-append after reorganization. + if (staged_ && loaded_.load() && (count < settled_.load())) + { + std::unique_lock remap_lock(remap_mutex_); + + if (!unsettle_all_(count, sequence{})) + return false; + } +#endif + logical_.store(count); return true; } diff --git a/include/bitcoin/database/impl/store/store.ipp b/include/bitcoin/database/impl/store/store.ipp index 29f589649..7f9b35a28 100644 --- a/include/bitcoin/database/impl/store/store.ipp +++ b/include/bitcoin/database/impl/store/store.ipp @@ -32,78 +32,78 @@ CLASS::store(const settings& config) NOEXCEPT // ------------------------------------------------------------------------ header_head_(head(config.path / schema::dir::heads, schema::archive::header), 1, 0, random), - header_body_(body(config.path, schema::archive::header), config.header_size, config.header_rate, sequential), + header_body_(body(config.path, schema::archive::header), config.header_size, config.header_rate, sequential, staged), input_head_(head(config.path / schema::dir::heads, schema::archive::input), 1, 0, sequential), - input_body_(body(config.path, schema::archive::input), config.input_size, config.input_rate, sequential), + input_body_(body(config.path, schema::archive::input), config.input_size, config.input_rate, sequential, staged), output_head_(head(config.path / schema::dir::heads, schema::archive::output), 1, 0, sequential), - output_body_(body(config.path, schema::archive::output), config.output_size, config.output_rate, sequential), + output_body_(body(config.path, schema::archive::output), config.output_size, config.output_rate, sequential, staged), ins_head_(head(config.path / schema::dir::heads, schema::archive::ins), 1, 0, random), - ins_body_(body(config.path, schema::archive::ins), config.ins_size, config.ins_rate, sequential), + ins_body_(body(config.path, schema::archive::ins), config.ins_size, config.ins_rate, sequential, staged), outs_head_(head(config.path / schema::dir::heads, schema::archive::outs), 1, 0, sequential), - outs_body_(body(config.path, schema::archive::outs), config.outs_size, config.outs_rate, sequential), + outs_body_(body(config.path, schema::archive::outs), config.outs_size, config.outs_rate, sequential, staged), tx_head_(head(config.path / schema::dir::heads, schema::archive::tx), 1, 0, random), - tx_body_(body(config.path, schema::archive::tx), config.tx_size, config.tx_rate, sequential), + tx_body_(body(config.path, schema::archive::tx), config.tx_size, config.tx_rate, sequential, staged), txs_head_(head(config.path / schema::dir::heads, schema::archive::txs), 1, 0, random), - txs_body_(body(config.path, schema::archive::txs), config.txs_size, config.txs_rate, sequential), + txs_body_(body(config.path, schema::archive::txs), config.txs_size, config.txs_rate, sequential, staged), // Indexes. // ------------------------------------------------------------------------ candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), 1, 0, sequential), - candidate_body_(body(config.path, schema::indexes::candidate), config.candidate_size, config.candidate_rate, sequential), + candidate_body_(body(config.path, schema::indexes::candidate), config.candidate_size, config.candidate_rate, sequential, staged), confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), 1, 0, sequential), - confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed_size, config.confirmed_rate, sequential), + confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed_size, config.confirmed_rate, sequential, staged), strong_tx_head_(head(config.path / schema::dir::heads, schema::indexes::strong_tx), 1, 0, random), - strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx_size, config.strong_tx_rate, sequential), + strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx_size, config.strong_tx_rate, sequential, staged), // Caches. // ------------------------------------------------------------------------ // TODO: body not random, but keep in memory. ecdsa_head_(head(config.path / schema::dir::heads, schema::caches::ecdsa), 1, 0, sequential), - ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa_size, config.ecdsa_rate, sequential), + ecdsa_body_(body(config.path, schema::caches::ecdsa), config.ecdsa_size, config.ecdsa_rate, sequential, staged), // TODO: body not random, but keep in memory. schnorr_head_(head(config.path / schema::dir::heads, schema::caches::schnorr), 1, 0, sequential), - schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr_size, config.schnorr_rate, sequential), + schnorr_body_(body(config.path, schema::caches::schnorr), config.schnorr_size, config.schnorr_rate, sequential, staged), silent_head_(head(config.path / schema::dir::heads, schema::caches::silent), 1, 0, sequential), - silent_body_(body(config.path, schema::caches::silent), config.silent_size, config.silent_rate, sequential), + silent_body_(body(config.path, schema::caches::silent), config.silent_size, config.silent_rate, sequential, staged), duplicate_head_(head(config.path / schema::dir::heads, schema::caches::duplicate), 1, 0, random), - duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate_size, config.duplicate_rate, sequential), + duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate_size, config.duplicate_rate, sequential, staged), prevalid_head_(head(config.path / schema::dir::heads, schema::caches::prevalid), 1, 0, sequential), - prevalid_body_(body(config.path, schema::caches::prevalid), config.prevalid_size, config.prevalid_rate, sequential), + prevalid_body_(body(config.path, schema::caches::prevalid), config.prevalid_size, config.prevalid_rate, sequential, staged), prevout_head_(head(config.path / schema::dir::heads, schema::caches::prevout), 1, 0, random), - prevout_body_(body(config.path, schema::caches::prevout), config.prevout_size, config.prevout_rate, sequential), + prevout_body_(body(config.path, schema::caches::prevout), config.prevout_size, config.prevout_rate, sequential, staged), validated_bk_head_(head(config.path / schema::dir::heads, schema::caches::validated_bk), 1, 0, random), - validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk_size, config.validated_bk_rate, sequential), + validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk_size, config.validated_bk_rate, sequential, staged), validated_tx_head_(head(config.path / schema::dir::heads, schema::caches::validated_tx), 1, 0, random), - validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx_size, config.validated_tx_rate, sequential), + validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx_size, config.validated_tx_rate, sequential, staged), // Optionals. // ------------------------------------------------------------------------ address_head_(head(config.path / schema::dir::heads, schema::optionals::address), 1, 0, random), - address_body_(body(config.path, schema::optionals::address), config.address_size, config.address_rate, sequential), + address_body_(body(config.path, schema::optionals::address), config.address_size, config.address_rate, sequential, staged), filter_bk_head_(head(config.path / schema::dir::heads, schema::optionals::filter_bk), 1, 0, random), - filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk_size, config.filter_bk_rate, sequential), + filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk_size, config.filter_bk_rate, sequential, staged), filter_tx_head_(head(config.path / schema::dir::heads, schema::optionals::filter_tx), 1, 0, random), - filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx_size, config.filter_tx_rate, sequential), + filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx_size, config.filter_tx_rate, sequential, staged), // Locks. // ------------------------------------------------------------------------ diff --git a/include/bitcoin/database/memory/mman.hpp b/include/bitcoin/database/memory/mman.hpp index cde0497df..dad52a15f 100644 --- a/include/bitcoin/database/memory/mman.hpp +++ b/include/bitcoin/database/memory/mman.hpp @@ -58,4 +58,36 @@ int fallocate(int fd, int, off_t offset, off_t len) NOEXCEPT; #endif // HAVE_MSC +// The anonymous staging backend accumulates writes in committed anonymous +// memory and transfers them to the backing file explicitly, so that no dirty +// file-backed page ever exists (posix kernels bound the dirty file page pool +// and force early writeback, untunably so on macOS). Default on macOS, which +// offers no writeback configuration; opt-in elsewhere (Linux exposes +// vm.dirty_* as an alternative). +#if !defined(HAVE_MSC) && !defined(WITHOUT_STAGING) && \ + (defined(HAVE_APPLE) || defined(WITH_STAGING)) + #define HAVE_STAGING +#endif + +#if defined(HAVE_STAGING) + +/// Reserve inaccessible anonymous address space (MAP_FAILED on failure). +void* mmap_reserve(size_t size) NOEXCEPT; + +/// Commit reserved pages as readable/writable anonymous memory. +int mmap_commit(void* address, size_t size) NOEXCEPT; + +/// Replace committed pages with a read-only shared mapping of the file. +int mmap_settle(void* address, size_t size, int fd, size_t offset) NOEXCEPT; + +/// Replace settled pages with committed anonymous memory (contents undefined). +int mmap_unsettle(void* address, size_t size) NOEXCEPT; + +/// Full-transfer positional file read/write (false on failure or early eof). +bool pread_all(int fd, uint8_t* to, size_t size, size_t offset) NOEXCEPT; +bool pwrite_all(int fd, const uint8_t* from, size_t size, + size_t offset) NOEXCEPT; + +#endif // HAVE_STAGING + #endif diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index f98a403b4..6abd7b3a0 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace libbitcoin { namespace database { @@ -57,13 +58,22 @@ class mmap /// Constructors. /// ----------------------------------------------------------------------- + /// Staged instances (append-only bodies) stage writes in anonymous memory + /// and convert flushed rows to a read-only file mapping, hard-faulting any + /// write into settled space. Unstaged instances (heads) hold all logical + /// content in anonymous memory for the life of the load. Both write the + /// file only by explicit transfer, so no dirty file-backed page ever + /// exists (no effect where the staging backend is not built). + /// Scalar construction (columns == 1): unchanged signature and codegen. mmap(const path& filename, size_t minimum=1, size_t expansion=0, - bool random=true) NOEXCEPT requires (is_one(columns)); + bool random=true, bool staged=false) NOEXCEPT + requires (is_one(columns)); /// Aggregate construction (columns > 1): one file per column, shared guards. mmap(const paths& filenames, size_t minimum=1, size_t expansion=0, - bool random=true) NOEXCEPT requires (columns > one); + bool random=true, bool staged=false) NOEXCEPT + requires (columns > one); /// Destruct for debug assertion only. virtual ~mmap() NOEXCEPT; @@ -180,7 +190,7 @@ class mmap // mman dispatch, not thread safe. template - bool flush_all_(std::index_sequence) NOEXCEPT; + bool flush_all_(size_t rows, std::index_sequence) NOEXCEPT; template bool map_all_(std::index_sequence) NOEXCEPT; template @@ -190,7 +200,7 @@ class mmap // mman wrappers, not thread safe. template - bool flush_() NOEXCEPT; + bool flush_(size_t rows) NOEXCEPT; template bool map_() NOEXCEPT; template @@ -204,11 +214,38 @@ class mmap template bool finalize_(size_t size) NOEXCEPT; +#if defined(HAVE_STAGING) + // staging dispatch, not thread safe. + template + bool settle_all_(size_t rows, std::index_sequence) NOEXCEPT; + template + bool unsettle_all_(size_t rows, std::index_sequence) NOEXCEPT; + + // staging wrappers, not thread safe. + template + bool stage_() NOEXCEPT; + template + bool commit_(size_t size) NOEXCEPT; + template + bool settle_(size_t from, size_t to) NOEXCEPT; + template + bool unsettle_(size_t rows) NOEXCEPT; + template + void teardown_(const error::error_t& ec) NOEXCEPT; + + // staging utilities, not thread safe. + bool advise_(uint8_t* map, size_t size) const NOEXCEPT; + size_t to_reservation(size_t rows) const NOEXCEPT; + size_t page_floor(size_t bytes) const NOEXCEPT; + size_t page_ceiling(size_t bytes) const NOEXCEPT; +#endif // HAVE_STAGING + // These are thread safe. const paths filenames_; const size_t minimum_; const size_t expansion_; const bool random_; + const bool staged_; std::atomic space_{ zero }; std::atomic error_{ error::success }; @@ -229,6 +266,16 @@ class mmap // These are protected by remap_mutex_. std::array memory_map_{}; mutable std::shared_mutex remap_mutex_{}; + +#if defined(HAVE_STAGING) + // settled_ is the count of rows flushed to disk (atomic for lock-free + // reads, updated only under remap_mutex_ exclusive). Rows below settled_ + // are backed by a read-only file mapping (staged instances); rows above + // are committed anonymous memory. The others are protected by remap_mutex_. + std::atomic settled_{}; + std::array reserved_{}; + size_t page_{}; +#endif // HAVE_STAGING }; using map = mmap; diff --git a/include/bitcoin/database/memory/mmaps.hpp b/include/bitcoin/database/memory/mmaps.hpp index e8edfa90d..8f42eceb3 100644 --- a/include/bitcoin/database/memory/mmaps.hpp +++ b/include/bitcoin/database/memory/mmaps.hpp @@ -52,17 +52,17 @@ class mmaps }; mmaps(const path& base_path, size_t size, size_t rate, - bool random_access) NOEXCEPT - : mmaps(base_path, size, rate, random_access, sequence) + bool random_access, bool staged=false) NOEXCEPT + : mmaps(base_path, size, rate, random_access, staged, sequence) { } protected: template mmaps(const path& base_path, size_t size, size_t rate, bool random_access, - std::index_sequence) NOEXCEPT + bool staged, std::index_sequence) NOEXCEPT : base(paths{ to_subpath(base_path, suffixes[Index])... }, size, rate, - random_access), + random_access, staged), base_path_{ base_path } { } diff --git a/include/bitcoin/database/store.hpp b/include/bitcoin/database/store.hpp index 183b864f6..08c5ddfef 100644 --- a/include/bitcoin/database/store.hpp +++ b/include/bitcoin/database/store.hpp @@ -234,6 +234,10 @@ class store static constexpr bool random = true; static constexpr bool sequential = false; + // Bodies are append-only, so stage writes in anonymous memory where the + // staging backend is built (heads update in place and remain resident). + static constexpr bool staged = true; + static inline path head(const path& folder, const std::string& name) NOEXCEPT { return folder / (name + schema::ext::head); diff --git a/include/bitcoin/database/tables/caches/ecdsa.hpp b/include/bitcoin/database/tables/caches/ecdsa.hpp index 0268258a4..85d864090 100644 --- a/include/bitcoin/database/tables/caches/ecdsa.hpp +++ b/include/bitcoin/database/tables/caches/ecdsa.hpp @@ -424,8 +424,8 @@ class ecdsa_storage { public: ecdsa_storage(const std::filesystem::path& path, size_t size, - size_t rate, bool random_access) NOEXCEPT - : ecdsa_files(path, size, rate, random_access) + size_t rate, bool random_access, bool staged=false) NOEXCEPT + : ecdsa_files(path, size, rate, random_access, staged) { } }; diff --git a/include/bitcoin/database/tables/caches/schnorr.hpp b/include/bitcoin/database/tables/caches/schnorr.hpp index dc0c7aec8..9e0256ee6 100644 --- a/include/bitcoin/database/tables/caches/schnorr.hpp +++ b/include/bitcoin/database/tables/caches/schnorr.hpp @@ -251,8 +251,8 @@ class schnorr_storage { public: schnorr_storage(const std::filesystem::path& path, size_t size, - size_t rate, bool random_access) NOEXCEPT - : schnorr_files(path, size, rate, random_access) + size_t rate, bool random_access, bool staged=false) NOEXCEPT + : schnorr_files(path, size, rate, random_access, staged) { } }; diff --git a/include/bitcoin/database/tables/caches/silent.hpp b/include/bitcoin/database/tables/caches/silent.hpp index 34b4a8e77..40005d97d 100644 --- a/include/bitcoin/database/tables/caches/silent.hpp +++ b/include/bitcoin/database/tables/caches/silent.hpp @@ -154,8 +154,8 @@ class silent_storage { public: silent_storage(const std::filesystem::path& path, size_t size, - size_t rate, bool random_access) NOEXCEPT - : silent_files(path, size, rate, random_access) + size_t rate, bool random_access, bool staged=false) NOEXCEPT + : silent_files(path, size, rate, random_access, staged) { } }; diff --git a/src/memory/mman.cpp b/src/memory/mman.cpp index 46f76c836..e93c85b84 100644 --- a/src/memory/mman.cpp +++ b/src/memory/mman.cpp @@ -1,5 +1,7 @@ // mman_win32 based on code.google.com/p/mman-win32 (MIT License). +#include +#include #include #include @@ -328,3 +330,88 @@ int fallocate(int fd, int, off_t offset, off_t len) NOEXCEPT } #endif // HAVE_MSC + +#if defined(HAVE_STAGING) + +void* mmap_reserve(size_t size) NOEXCEPT +{ + return ::mmap(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); +} + +int mmap_commit(void* address, size_t size) NOEXCEPT +{ + return ::mprotect(address, size, PROT_READ | PROT_WRITE); +} + +int mmap_settle(void* address, size_t size, int fd, size_t offset) NOEXCEPT +{ + return ::mmap(address, size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, + static_cast(offset)) == MAP_FAILED ? -1 : 0; +} + +int mmap_unsettle(void* address, size_t size) NOEXCEPT +{ + return ::mmap(address, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED ? -1 : 0; +} + +// Chunked to avoid platform-specific single-transfer length limits. +constexpr size_t transfer_chunk = size_t{ 1 } << 30; + +bool pread_all(int fd, uint8_t* to, size_t size, size_t offset) NOEXCEPT +{ + while (size > 0) + { + const auto request = std::min(size, transfer_chunk); + const auto result = ::pread(fd, to, request, + static_cast(offset)); + + if (result < 0) + { + if (errno == EINTR) + continue; + + return false; + } + + // Early eof implies the requested range exceeds the file. + if (result == 0) + return false; + + const auto bytes = static_cast(result); + to += bytes; + offset += bytes; + size -= bytes; + } + + return true; +} + +bool pwrite_all(int fd, const uint8_t* from, size_t size, + size_t offset) NOEXCEPT +{ + while (size > 0) + { + const auto request = std::min(size, transfer_chunk); + const auto result = ::pwrite(fd, from, request, + static_cast(offset)); + + if (result <= 0) + { + if (result < 0 && errno == EINTR) + continue; + + return false; + } + + const auto bytes = static_cast(result); + from += bytes; + offset += bytes; + size -= bytes; + } + + return true; +} + +#endif // HAVE_STAGING diff --git a/test/memory/mmap.cpp b/test/memory/mmap.cpp index 5e7776ae1..d17a17386 100644 --- a/test/memory/mmap.cpp +++ b/test/memory/mmap.cpp @@ -789,6 +789,179 @@ BOOST_AUTO_TEST_CASE(mmap__write__read__expected) BOOST_REQUIRE(!instance.get_fault()); } +// The staged parameter engages the anonymous staging backend where built +// (posix); elsewhere it is ignored. Content assertions are identical either +// way, so these cases validate staging on posix and the classic map on win32. + +constexpr auto stage_pattern = [](size_t index) NOEXCEPT +{ + return static_cast(index % 251); +}; + +constexpr auto stage_repattern = [](size_t index) NOEXCEPT +{ + return static_cast((index * 3) % 241); +}; + +BOOST_AUTO_TEST_CASE(mmap__staged__write_flush_write_reload__expected) +{ + constexpr size_t first = 10'000; + constexpr size_t second = 7'000; + const std::string file = TEST_PATH; + BOOST_REQUIRE(test::create(file)); + + map instance(file, 1, 50, true, true); + BOOST_REQUIRE(!instance.open()); + BOOST_REQUIRE(!instance.load()); + + // Write and flush twice, settling from zero and then a page interior. + auto memory = instance.get(instance.allocate(first)); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < first; ++index) + memory.begin()[index] = stage_pattern(index); + + memory.reset(); + BOOST_REQUIRE(!instance.flush()); + + memory = instance.get(instance.allocate(second)); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < second; ++index) + memory.begin()[index] = stage_pattern(first + index); + + memory.reset(); + BOOST_REQUIRE(!instance.flush()); + + // All content is readable through the settled and unsettled regions. + memory = instance.get(); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < first + second; ++index) + BOOST_REQUIRE_EQUAL(memory.begin()[index], stage_pattern(index)); + + memory.reset(); + BOOST_REQUIRE(!instance.unload()); + BOOST_REQUIRE(!instance.close()); + BOOST_REQUIRE(!instance.get_fault()); + + // All content persists across close and reopen. + map reopened(file, 1, 50, true, true); + BOOST_REQUIRE(!reopened.open()); + BOOST_REQUIRE(!reopened.load()); + BOOST_REQUIRE_EQUAL(reopened.size(), first + second); + + memory = reopened.get(); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < first + second; ++index) + BOOST_REQUIRE_EQUAL(memory.begin()[index], stage_pattern(index)); + + memory.reset(); + BOOST_REQUIRE(!reopened.unload()); + BOOST_REQUIRE(!reopened.close()); + BOOST_REQUIRE(!reopened.get_fault()); +} + +BOOST_AUTO_TEST_CASE(mmap__staged__truncate_below_flush_rewrite__expected) +{ + constexpr size_t initial = 12'000; + constexpr size_t retained = 5'000; + constexpr size_t appended = 4'000; + const std::string file = TEST_PATH; + BOOST_REQUIRE(test::create(file)); + + map instance(file, 1, 50, true, true); + BOOST_REQUIRE(!instance.open()); + BOOST_REQUIRE(!instance.load()); + + auto memory = instance.get(instance.allocate(initial)); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < initial; ++index) + memory.begin()[index] = stage_pattern(index); + + memory.reset(); + BOOST_REQUIRE(!instance.flush()); + + // Truncate below the flushed extent and append replacement content. + BOOST_REQUIRE(instance.truncate(retained)); + BOOST_REQUIRE_EQUAL(instance.allocate(appended), retained); + + memory = instance.get(retained); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < appended; ++index) + memory.begin()[index] = stage_repattern(retained + index); + + memory.reset(); + BOOST_REQUIRE(!instance.flush()); + BOOST_REQUIRE(!instance.unload()); + BOOST_REQUIRE(!instance.close()); + BOOST_REQUIRE(!instance.get_fault()); + + // Retained content precedes replacement content across reopen. + map reopened(file, 1, 50, true, true); + BOOST_REQUIRE(!reopened.open()); + BOOST_REQUIRE(!reopened.load()); + BOOST_REQUIRE_EQUAL(reopened.size(), retained + appended); + + memory = reopened.get(); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < retained; ++index) + BOOST_REQUIRE_EQUAL(memory.begin()[index], stage_pattern(index)); + for (size_t index = retained; index < retained + appended; ++index) + BOOST_REQUIRE_EQUAL(memory.begin()[index], stage_repattern(index)); + + memory.reset(); + BOOST_REQUIRE(!reopened.unload()); + BOOST_REQUIRE(!reopened.close()); + BOOST_REQUIRE(!reopened.get_fault()); +} + +BOOST_AUTO_TEST_CASE(mmap__unstaged__rewrite_below_flush__expected) +{ + constexpr size_t size = 10'000; + constexpr size_t rewrite = 100; + const std::string file = TEST_PATH; + BOOST_REQUIRE(test::create(file)); + + // Unstaged (default) retains in-place rewrite of flushed content (heads). + map instance(file, 1, 50, true); + BOOST_REQUIRE(!instance.open()); + BOOST_REQUIRE(!instance.load()); + + auto memory = instance.get(instance.allocate(size)); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < size; ++index) + memory.begin()[index] = stage_pattern(index); + + memory.reset(); + BOOST_REQUIRE(!instance.flush()); + + memory = instance.get(); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < rewrite; ++index) + memory.begin()[index] = stage_repattern(index); + + memory.reset(); + BOOST_REQUIRE(!instance.flush()); + BOOST_REQUIRE(!instance.unload()); + BOOST_REQUIRE(!instance.close()); + BOOST_REQUIRE(!instance.get_fault()); + + map reopened(file, 1, 50, true); + BOOST_REQUIRE(!reopened.open()); + BOOST_REQUIRE(!reopened.load()); + BOOST_REQUIRE_EQUAL(reopened.size(), size); + + memory = reopened.get(); + BOOST_REQUIRE(memory); + for (size_t index = 0; index < rewrite; ++index) + BOOST_REQUIRE_EQUAL(memory.begin()[index], stage_repattern(index)); + for (size_t index = rewrite; index < size; ++index) + BOOST_REQUIRE_EQUAL(memory.begin()[index], stage_pattern(index)); + + memory.reset(); + BOOST_REQUIRE(!reopened.unload()); + BOOST_REQUIRE(!reopened.close()); + BOOST_REQUIRE(!reopened.get_fault()); +} + BOOST_AUTO_TEST_CASE(mmap__unload__shared__unload_locked) { const std::string file = TEST_PATH; diff --git a/test/mocks/chunk_storage.hpp b/test/mocks/chunk_storage.hpp index 62cd63907..d31e9ea45 100644 --- a/test/mocks/chunk_storage.hpp +++ b/test/mocks/chunk_storage.hpp @@ -69,15 +69,15 @@ class chunk_storages { } - chunk_storages(const path& filename, size_t=1, size_t=0, bool=true) NOEXCEPT - requires (is_one(columns)) + chunk_storages(const path& filename, size_t=1, size_t=0, bool=true, + bool=false) NOEXCEPT requires (is_one(columns)) : alias_{}, paths_{ filename }, logical_{} { } /// Aggregate construction (columns > 1): one backing per column. - chunk_storages(const paths& filenames, size_t=1, size_t=0, - bool=true) NOEXCEPT requires (columns > one) + chunk_storages(const paths& filenames, size_t=1, size_t=0, bool=true, + bool=false) NOEXCEPT requires (columns > one) : alias_{}, paths_{ filenames }, logical_{} { } From 05b3d7d713c789175091a4158f436b950f76c6a5 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 25 Jul 2026 01:28:08 -0400 Subject: [PATCH 02/12] Posix memory map allocated write completion accounting. --- builds/gnu/Makefile.am | 967 ++++++------- .../libbitcoin-database.vcxproj | 667 ++++----- .../libbitcoin-database.vcxproj.filters | 1209 ++++++++-------- .../libbitcoin-database.vcxproj | 667 ++++----- .../libbitcoin-database.vcxproj.filters | 1209 ++++++++-------- include/bitcoin/database.hpp | 1 + include/bitcoin/database/impl/memory/mmap.ipp | 248 ++-- .../database/impl/memory/mmap_private.ipp | 344 +---- .../database/impl/memory/mmap_staging.ipp | 438 ++++++ .../database/impl/memory/mmap_storage.ipp | 43 +- .../database/impl/primitives/arraymap.ipp | 432 +++--- .../bitcoin/database/impl/primitives/body.ipp | 598 ++++---- .../database/impl/primitives/hashmap.ipp | 1144 +++++++-------- .../database/impl/primitives/hashmaps.ipp | 1276 +++++++++-------- .../database/impl/primitives/nomap.ipp | 635 ++++---- .../database/impl/primitives/nomaps.ipp | 428 +++--- .../database/memory/interfaces/storage.hpp | 248 ++-- include/bitcoin/database/memory/memory.hpp | 1 + include/bitcoin/database/memory/mman.hpp | 154 +- include/bitcoin/database/memory/mmap.hpp | 63 +- include/bitcoin/database/memory/mstage.hpp | 49 + include/bitcoin/database/primitives/body.hpp | 269 ++-- src/memory/mman.cpp | 86 -- src/memory/mstage.cpp | 112 ++ test/memory/mmap.cpp | 54 + test/mocks/chunk_storage.hpp | 675 ++++----- 26 files changed, 6203 insertions(+), 5814 deletions(-) create mode 100644 include/bitcoin/database/impl/memory/mmap_staging.ipp create mode 100644 include/bitcoin/database/memory/mstage.hpp create mode 100644 src/memory/mstage.cpp diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am index 4ae37f988..f14afe97d 100644 --- a/builds/gnu/Makefile.am +++ b/builds/gnu/Makefile.am @@ -1,482 +1,485 @@ -############################################################################### -# Copyright (c) 2014-2026 libbitcoin-database developers (see COPYING). -# -# GENERATED SOURCE CODE, DO NOT EDIT EXCEPT EXPERIMENTALLY -# -############################################################################### - -# Automake settings. -#============================================================================== -# Look for macros in the 'm4' subdirectory. -#------------------------------------------------------------------------------ -ACLOCAL_AMFLAGS = -I m4 - -# Distribute data. -#============================================================================== -# files => ${pkgconfigdir} -#------------------------------------------------------------------------------ -pkgconfig_DATA = \ - libbitcoin-database.pc - -# files => ${docdir} -#------------------------------------------------------------------------------ -doc_DATA = \ - AUTHORS \ - ../../COPYING \ - ChangeLog \ - INSTALL \ - NEWS \ - README - -# Libraries. -#============================================================================== -# Target library 'src/libbitcoin-database.la' -#------------------------------------------------------------------------------ -lib_LTLIBRARIES = src/libbitcoin-database.la - -src_libbitcoin_database_la_LIBS = src/libbitcoin-database.la - -src_libbitcoin_database_la_CPPFLAGS = \ - -I${srcdir}/../../include \ - ${libbitcoin_system_BUILD_CPPFLAGS} - -src_libbitcoin_database_la_LDFLAGS = \ - ${libbitcoin_system_LDFLAGS} - -src_libbitcoin_database_la_LIBADD = \ - ${libbitcoin_system_LIBS} - -src_libbitcoin_database_la_SOURCES = \ - ${srcdir}/../../src/define.cpp \ - ${srcdir}/../../src/error.cpp \ - ${srcdir}/../../src/settings.cpp \ - ${srcdir}/../../src/file/rotator.cpp \ - ${srcdir}/../../src/file/utilities.cpp \ - ${srcdir}/../../src/locks/file_lock.cpp \ - ${srcdir}/../../src/locks/flush_lock.cpp \ - ${srcdir}/../../src/locks/interprocess_lock.cpp \ - ${srcdir}/../../src/memory/mman.cpp \ - ${srcdir}/../../src/memory/utilities.cpp \ - ${srcdir}/../../src/types/history.cpp \ - ${srcdir}/../../src/types/multisig_view.cpp \ - ${srcdir}/../../src/types/unspent.cpp - -include_bitcoindir = \ - ${includedir}/bitcoin - -include_bitcoin_HEADERS = \ - ${srcdir}/../../include/bitcoin/database.hpp - -include_bitcoin_databasedir = \ - ${includedir}/bitcoin/database - -include_bitcoin_database_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/boost.hpp \ - ${srcdir}/../../include/bitcoin/database/define.hpp \ - ${srcdir}/../../include/bitcoin/database/error.hpp \ - ${srcdir}/../../include/bitcoin/database/query.hpp \ - ${srcdir}/../../include/bitcoin/database/settings.hpp \ - ${srcdir}/../../include/bitcoin/database/store.hpp \ - ${srcdir}/../../include/bitcoin/database/version.hpp - -include_bitcoin_database_filedir = \ - ${includedir}/bitcoin/database/file - -include_bitcoin_database_file_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/file/file.hpp \ - ${srcdir}/../../include/bitcoin/database/file/rotator.hpp \ - ${srcdir}/../../include/bitcoin/database/file/utilities.hpp - -include_bitcoin_database_impl_memorydir = \ - ${includedir}/bitcoin/database/impl/memory - -include_bitcoin_database_impl_memory_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_dispatch.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_private.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_storage.ipp - -include_bitcoin_database_impl_primitivesdir = \ - ${includedir}/bitcoin/database/impl/primitives - -include_bitcoin_database_impl_primitives_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/arrayhead.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/arraymap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/body.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/hashhead.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/hashmap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/hashmaps.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/iterator.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/keys.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/linkage.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/nohead.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/nomap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/nomaps.ipp - -include_bitcoin_database_impl_querydir = \ - ${includedir}/bitcoin/database/impl/query - -include_bitcoin_database_impl_query_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/amounts.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/confirmed.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/extent.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/fee_rate.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/filters.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/height.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/initialize.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/locator.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/merkle.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/properties_block.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/properties_tx.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/query.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/sequences.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/sizes.ipp - -include_bitcoin_database_impl_query_addressdir = \ - ${includedir}/bitcoin/database/impl/query/address - -include_bitcoin_database_impl_query_address_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/address/address_balance.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/address/address_history.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/address/address_outpoints.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/address/address_unspent.ipp - -include_bitcoin_database_impl_query_archivedir = \ - ${includedir}/bitcoin/database/impl/query/archive - -include_bitcoin_database_impl_query_archive_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/archive/chain_reader.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/archive/chain_writer.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/archive/wire_reader.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/archive/wire_writer.ipp - -include_bitcoin_database_impl_query_batchdir = \ - ${includedir}/bitcoin/database/impl/query/batch - -include_bitcoin_database_impl_query_batch_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/batch/ecdsa.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/batch/prevalid.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/batch/schnorr.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/batch/silent.ipp - -include_bitcoin_database_impl_query_consensusdir = \ - ${includedir}/bitcoin/database/impl/query/consensus - -include_bitcoin_database_impl_query_consensus_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_block.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_chain_state.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_compact.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_forks.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_populate.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_prevouts.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_states.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_strong.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_transaction.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_work.ipp - -include_bitcoin_database_impl_query_navigatedir = \ - ${includedir}/bitcoin/database/impl/query/navigate - -include_bitcoin_database_impl_query_navigate_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_arraymap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_forward.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_hashmap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_natural.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_reverse.ipp - -include_bitcoin_database_impl_storedir = \ - ${includedir}/bitcoin/database/impl/store - -include_bitcoin_database_impl_store_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/store/store.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_backup.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_close.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_create.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_dump.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_events.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_open.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_open_load.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_prune.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_reload.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_report.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_restore.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_snapshot.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_tables.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_unload_close.ipp - -include_bitcoin_database_locksdir = \ - ${includedir}/bitcoin/database/locks - -include_bitcoin_database_locks_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/locks/file_lock.hpp \ - ${srcdir}/../../include/bitcoin/database/locks/flush_lock.hpp \ - ${srcdir}/../../include/bitcoin/database/locks/interprocess_lock.hpp \ - ${srcdir}/../../include/bitcoin/database/locks/locks.hpp - -include_bitcoin_database_memorydir = \ - ${includedir}/bitcoin/database/memory - -include_bitcoin_database_memory_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/memory/accessor.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/finalizer.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/memory.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/mman.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/mmap.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/mmaps.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/reader.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/streamers.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/utilities.hpp - -include_bitcoin_database_memory_interfacesdir = \ - ${includedir}/bitcoin/database/memory/interfaces - -include_bitcoin_database_memory_interfaces_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/memory/interfaces/storage.hpp - -include_bitcoin_database_primitivesdir = \ - ${includedir}/bitcoin/database/primitives - -include_bitcoin_database_primitives_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/primitives/arrayhead.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/arraymap.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/body.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/column.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/hashhead.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/hashmap.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/hashmaps.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/iterator.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/keys.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/linkage.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/nohead.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/nomap.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/nomaps.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/primitives.hpp - -include_bitcoin_database_tablesdir = \ - ${includedir}/bitcoin/database/tables - -include_bitcoin_database_tables_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/context.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/event.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/names.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/schema.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/table.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/tables.hpp - -include_bitcoin_database_tables_archivesdir = \ - ${includedir}/bitcoin/database/tables/archives - -include_bitcoin_database_tables_archives_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/archives/header.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/input.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/ins.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/output.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/outs.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/transaction.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/txs.hpp - -include_bitcoin_database_tables_cachesdir = \ - ${includedir}/bitcoin/database/tables/caches - -include_bitcoin_database_tables_caches_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/caches/duplicate.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/ecdsa.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/prevalid.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/prevout.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/schnorr.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/silent.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/validated_bk.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/validated_tx.hpp - -include_bitcoin_database_tables_indexesdir = \ - ${includedir}/bitcoin/database/tables/indexes - -include_bitcoin_database_tables_indexes_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/indexes/height.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/indexes/strong_tx.hpp - -include_bitcoin_database_tables_optionalsdir = \ - ${includedir}/bitcoin/database/tables/optionals - -include_bitcoin_database_tables_optionals_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/optionals/address.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/optionals/filter_bk.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/optionals/filter_tx.hpp - -include_bitcoin_database_typesdir = \ - ${includedir}/bitcoin/database/types - -include_bitcoin_database_types_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/types/association.hpp \ - ${srcdir}/../../include/bitcoin/database/types/associations.hpp \ - ${srcdir}/../../include/bitcoin/database/types/block_state.hpp \ - ${srcdir}/../../include/bitcoin/database/types/fee_rate.hpp \ - ${srcdir}/../../include/bitcoin/database/types/header_state.hpp \ - ${srcdir}/../../include/bitcoin/database/types/history.hpp \ - ${srcdir}/../../include/bitcoin/database/types/multisig_view.hpp \ - ${srcdir}/../../include/bitcoin/database/types/point_set.hpp \ - ${srcdir}/../../include/bitcoin/database/types/position.hpp \ - ${srcdir}/../../include/bitcoin/database/types/span.hpp \ - ${srcdir}/../../include/bitcoin/database/types/tx_state.hpp \ - ${srcdir}/../../include/bitcoin/database/types/type.hpp \ - ${srcdir}/../../include/bitcoin/database/types/types.hpp \ - ${srcdir}/../../include/bitcoin/database/types/unspent.hpp - -# Tests. -#============================================================================== -# Target test 'test/libbitcoin-database-test' -#------------------------------------------------------------------------------ -if WITH_TESTS - -check_PROGRAMS = test/libbitcoin-database-test - -test_libbitcoin_database_test_CPPFLAGS = \ - ${boost_BUILD_CPPFLAGS} \ - ${boost_unit_test_framework_BUILD_CPPFLAGS} \ - ${src_libbitcoin_database_la_CPPFLAGS} - -test_libbitcoin_database_test_LDFLAGS = \ - ${boost_LDFLAGS} \ - ${boost_unit_test_framework_LDFLAGS} \ - ${src_libbitcoin_database_la_LDFLAGS} - -test_libbitcoin_database_test_LDADD = \ - ${boost_LIBS} \ - ${boost_unit_test_framework_LIBS} \ - ${src_libbitcoin_database_la_LIBS} \ - ${src_libbitcoin_database_la_LIBADD} - -test_libbitcoin_database_test_SOURCES = \ - ${srcdir}/../../test/error.cpp \ - ${srcdir}/../../test/main.cpp \ - ${srcdir}/../../test/settings.cpp \ - ${srcdir}/../../test/test.cpp \ - ${srcdir}/../../test/file/rotator.cpp \ - ${srcdir}/../../test/file/utilities.cpp \ - ${srcdir}/../../test/locks/file_lock.cpp \ - ${srcdir}/../../test/locks/flush_lock.cpp \ - ${srcdir}/../../test/locks/interprocess_lock.cpp \ - ${srcdir}/../../test/memory/accessor.cpp \ - ${srcdir}/../../test/memory/mmap.cpp \ - ${srcdir}/../../test/memory/utilities.cpp \ - ${srcdir}/../../test/mocks/blocks.cpp \ - ${srcdir}/../../test/primitives/arrayhead.cpp \ - ${srcdir}/../../test/primitives/arraymap.cpp \ - ${srcdir}/../../test/primitives/body.cpp \ - ${srcdir}/../../test/primitives/hashhead.cpp \ - ${srcdir}/../../test/primitives/hashmap.cpp \ - ${srcdir}/../../test/primitives/hashmaps.cpp \ - ${srcdir}/../../test/primitives/iterator.cpp \ - ${srcdir}/../../test/primitives/keys.cpp \ - ${srcdir}/../../test/primitives/linkage.cpp \ - ${srcdir}/../../test/primitives/nohead.cpp \ - ${srcdir}/../../test/primitives/nomap.cpp \ - ${srcdir}/../../test/query/amounts.cpp \ - ${srcdir}/../../test/query/confirmed.cpp \ - ${srcdir}/../../test/query/extent.cpp \ - ${srcdir}/../../test/query/fee_rate.cpp \ - ${srcdir}/../../test/query/filters.cpp \ - ${srcdir}/../../test/query/height.cpp \ - ${srcdir}/../../test/query/initialize.cpp \ - ${srcdir}/../../test/query/locator.cpp \ - ${srcdir}/../../test/query/merkle.cpp \ - ${srcdir}/../../test/query/properties_block.cpp \ - ${srcdir}/../../test/query/properties_tx.cpp \ - ${srcdir}/../../test/query/sequences.cpp \ - ${srcdir}/../../test/query/sizes.cpp \ - ${srcdir}/../../test/query/address/address_balance.cpp \ - ${srcdir}/../../test/query/address/address_history.cpp \ - ${srcdir}/../../test/query/address/address_outpoints.cpp \ - ${srcdir}/../../test/query/address/address_unspent.cpp \ - ${srcdir}/../../test/query/archive/chain_reader.cpp \ - ${srcdir}/../../test/query/archive/chain_writer.cpp \ - ${srcdir}/../../test/query/archive/wire_reader.cpp \ - ${srcdir}/../../test/query/archive/wire_writer.cpp \ - ${srcdir}/../../test/query/batch/ecdsa.cpp \ - ${srcdir}/../../test/query/batch/prevalid.cpp \ - ${srcdir}/../../test/query/batch/schnorr.cpp \ - ${srcdir}/../../test/query/batch/silent.cpp \ - ${srcdir}/../../test/query/consensus/consensus_block.cpp \ - ${srcdir}/../../test/query/consensus/consensus_chain_state.cpp \ - ${srcdir}/../../test/query/consensus/consensus_compact.cpp \ - ${srcdir}/../../test/query/consensus/consensus_forks.cpp \ - ${srcdir}/../../test/query/consensus/consensus_populate.cpp \ - ${srcdir}/../../test/query/consensus/consensus_prevouts.cpp \ - ${srcdir}/../../test/query/consensus/consensus_states.cpp \ - ${srcdir}/../../test/query/consensus/consensus_strong.cpp \ - ${srcdir}/../../test/query/consensus/consensus_transaction.cpp \ - ${srcdir}/../../test/query/consensus/consensus_work.cpp \ - ${srcdir}/../../test/query/navigate/navigate_arraymap.cpp \ - ${srcdir}/../../test/query/navigate/navigate_forward.cpp \ - ${srcdir}/../../test/query/navigate/navigate_hashmap.cpp \ - ${srcdir}/../../test/query/navigate/navigate_natural.cpp \ - ${srcdir}/../../test/query/navigate/navigate_reverse.cpp \ - ${srcdir}/../../test/store/store.cpp \ - ${srcdir}/../../test/store/store_backup.cpp \ - ${srcdir}/../../test/store/store_close.cpp \ - ${srcdir}/../../test/store/store_create.cpp \ - ${srcdir}/../../test/store/store_dump.cpp \ - ${srcdir}/../../test/store/store_events.cpp \ - ${srcdir}/../../test/store/store_open.cpp \ - ${srcdir}/../../test/store/store_open_load.cpp \ - ${srcdir}/../../test/store/store_prune.cpp \ - ${srcdir}/../../test/store/store_reload.cpp \ - ${srcdir}/../../test/store/store_report.cpp \ - ${srcdir}/../../test/store/store_restore.cpp \ - ${srcdir}/../../test/store/store_snapshot.cpp \ - ${srcdir}/../../test/store/store_tables.cpp \ - ${srcdir}/../../test/store/store_unload_close.cpp \ - ${srcdir}/../../test/tables/archives/header.cpp \ - ${srcdir}/../../test/tables/archives/input.cpp \ - ${srcdir}/../../test/tables/archives/ins.cpp \ - ${srcdir}/../../test/tables/archives/output.cpp \ - ${srcdir}/../../test/tables/archives/outs.cpp \ - ${srcdir}/../../test/tables/archives/transaction.cpp \ - ${srcdir}/../../test/tables/archives/txs.cpp \ - ${srcdir}/../../test/tables/caches/duplicate.cpp \ - ${srcdir}/../../test/tables/caches/ecdsa.cpp \ - ${srcdir}/../../test/tables/caches/prevalid.cpp \ - ${srcdir}/../../test/tables/caches/prevout.cpp \ - ${srcdir}/../../test/tables/caches/schnorr.cpp \ - ${srcdir}/../../test/tables/caches/silent.cpp \ - ${srcdir}/../../test/tables/caches/validated_bk.cpp \ - ${srcdir}/../../test/tables/caches/validated_tx.cpp \ - ${srcdir}/../../test/tables/indexes/height.cpp \ - ${srcdir}/../../test/tables/indexes/strong_tx.cpp \ - ${srcdir}/../../test/tables/optional/address.cpp \ - ${srcdir}/../../test/tables/optional/filter_bk.cpp \ - ${srcdir}/../../test/tables/optional/filter_tx.cpp \ - ${srcdir}/../../test/types/history.cpp \ - ${srcdir}/../../test/types/span.cpp \ - ${srcdir}/../../test/types/unspent.cpp - -TESTS = test_runner.sh - -endif WITH_TESTS - -# Binaries. -#============================================================================== -# Target binary 'tools/initchain/initchain' -#------------------------------------------------------------------------------ -if WITH_TOOLS - -noinst_PROGRAMS = tools/initchain/initchain - -tools_initchain_initchain_CPPFLAGS = \ - ${src_libbitcoin_database_la_CPPFLAGS} - -tools_initchain_initchain_LDFLAGS = \ - ${src_libbitcoin_database_la_LDFLAGS} - -tools_initchain_initchain_LDADD = \ - ${src_libbitcoin_database_la_LIBS} \ - ${src_libbitcoin_database_la_LIBADD} - -tools_initchain_initchain_SOURCES = \ - ${srcdir}/../../tools/initchain/initchain.cpp - -target_tools = tools/initchain/initchain - -tools: ${target_tools} - -endif WITH_TOOLS +############################################################################### +# Copyright (c) 2014-2026 libbitcoin-database developers (see COPYING). +# +# GENERATED SOURCE CODE, DO NOT EDIT EXCEPT EXPERIMENTALLY +# +############################################################################### + +# Automake settings. +#============================================================================== +# Look for macros in the 'm4' subdirectory. +#------------------------------------------------------------------------------ +ACLOCAL_AMFLAGS = -I m4 + +# Distribute data. +#============================================================================== +# files => ${pkgconfigdir} +#------------------------------------------------------------------------------ +pkgconfig_DATA = \ + libbitcoin-database.pc + +# files => ${docdir} +#------------------------------------------------------------------------------ +doc_DATA = \ + AUTHORS \ + ../../COPYING \ + ChangeLog \ + INSTALL \ + NEWS \ + README + +# Libraries. +#============================================================================== +# Target library 'src/libbitcoin-database.la' +#------------------------------------------------------------------------------ +lib_LTLIBRARIES = src/libbitcoin-database.la + +src_libbitcoin_database_la_LIBS = src/libbitcoin-database.la + +src_libbitcoin_database_la_CPPFLAGS = \ + -I${srcdir}/../../include \ + ${libbitcoin_system_BUILD_CPPFLAGS} + +src_libbitcoin_database_la_LDFLAGS = \ + ${libbitcoin_system_LDFLAGS} + +src_libbitcoin_database_la_LIBADD = \ + ${libbitcoin_system_LIBS} + +src_libbitcoin_database_la_SOURCES = \ + ${srcdir}/../../src/define.cpp \ + ${srcdir}/../../src/error.cpp \ + ${srcdir}/../../src/settings.cpp \ + ${srcdir}/../../src/file/rotator.cpp \ + ${srcdir}/../../src/file/utilities.cpp \ + ${srcdir}/../../src/locks/file_lock.cpp \ + ${srcdir}/../../src/locks/flush_lock.cpp \ + ${srcdir}/../../src/locks/interprocess_lock.cpp \ + ${srcdir}/../../src/memory/mman.cpp \ + ${srcdir}/../../src/memory/mstage.cpp \ + ${srcdir}/../../src/memory/utilities.cpp \ + ${srcdir}/../../src/types/history.cpp \ + ${srcdir}/../../src/types/multisig_view.cpp \ + ${srcdir}/../../src/types/unspent.cpp + +include_bitcoindir = \ + ${includedir}/bitcoin + +include_bitcoin_HEADERS = \ + ${srcdir}/../../include/bitcoin/database.hpp + +include_bitcoin_databasedir = \ + ${includedir}/bitcoin/database + +include_bitcoin_database_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/boost.hpp \ + ${srcdir}/../../include/bitcoin/database/define.hpp \ + ${srcdir}/../../include/bitcoin/database/error.hpp \ + ${srcdir}/../../include/bitcoin/database/query.hpp \ + ${srcdir}/../../include/bitcoin/database/settings.hpp \ + ${srcdir}/../../include/bitcoin/database/store.hpp \ + ${srcdir}/../../include/bitcoin/database/version.hpp + +include_bitcoin_database_filedir = \ + ${includedir}/bitcoin/database/file + +include_bitcoin_database_file_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/file/file.hpp \ + ${srcdir}/../../include/bitcoin/database/file/rotator.hpp \ + ${srcdir}/../../include/bitcoin/database/file/utilities.hpp + +include_bitcoin_database_impl_memorydir = \ + ${includedir}/bitcoin/database/impl/memory + +include_bitcoin_database_impl_memory_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_dispatch.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_private.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_staging.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_storage.ipp + +include_bitcoin_database_impl_primitivesdir = \ + ${includedir}/bitcoin/database/impl/primitives + +include_bitcoin_database_impl_primitives_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/arrayhead.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/arraymap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/body.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/hashhead.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/hashmap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/hashmaps.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/iterator.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/keys.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/linkage.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/nohead.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/nomap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/nomaps.ipp + +include_bitcoin_database_impl_querydir = \ + ${includedir}/bitcoin/database/impl/query + +include_bitcoin_database_impl_query_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/amounts.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/confirmed.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/extent.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/fee_rate.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/filters.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/height.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/initialize.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/locator.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/merkle.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/properties_block.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/properties_tx.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/query.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/sequences.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/sizes.ipp + +include_bitcoin_database_impl_query_addressdir = \ + ${includedir}/bitcoin/database/impl/query/address + +include_bitcoin_database_impl_query_address_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/address/address_balance.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/address/address_history.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/address/address_outpoints.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/address/address_unspent.ipp + +include_bitcoin_database_impl_query_archivedir = \ + ${includedir}/bitcoin/database/impl/query/archive + +include_bitcoin_database_impl_query_archive_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/archive/chain_reader.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/archive/chain_writer.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/archive/wire_reader.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/archive/wire_writer.ipp + +include_bitcoin_database_impl_query_batchdir = \ + ${includedir}/bitcoin/database/impl/query/batch + +include_bitcoin_database_impl_query_batch_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/batch/ecdsa.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/batch/prevalid.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/batch/schnorr.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/batch/silent.ipp + +include_bitcoin_database_impl_query_consensusdir = \ + ${includedir}/bitcoin/database/impl/query/consensus + +include_bitcoin_database_impl_query_consensus_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_block.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_chain_state.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_compact.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_forks.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_populate.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_prevouts.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_states.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_strong.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_transaction.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_work.ipp + +include_bitcoin_database_impl_query_navigatedir = \ + ${includedir}/bitcoin/database/impl/query/navigate + +include_bitcoin_database_impl_query_navigate_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_arraymap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_forward.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_hashmap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_natural.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_reverse.ipp + +include_bitcoin_database_impl_storedir = \ + ${includedir}/bitcoin/database/impl/store + +include_bitcoin_database_impl_store_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/store/store.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_backup.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_close.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_create.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_dump.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_events.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_open.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_open_load.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_prune.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_reload.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_report.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_restore.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_snapshot.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_tables.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_unload_close.ipp + +include_bitcoin_database_locksdir = \ + ${includedir}/bitcoin/database/locks + +include_bitcoin_database_locks_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/locks/file_lock.hpp \ + ${srcdir}/../../include/bitcoin/database/locks/flush_lock.hpp \ + ${srcdir}/../../include/bitcoin/database/locks/interprocess_lock.hpp \ + ${srcdir}/../../include/bitcoin/database/locks/locks.hpp + +include_bitcoin_database_memorydir = \ + ${includedir}/bitcoin/database/memory + +include_bitcoin_database_memory_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/memory/accessor.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/finalizer.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/memory.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/mman.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/mmap.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/mmaps.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/mstage.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/reader.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/streamers.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/utilities.hpp + +include_bitcoin_database_memory_interfacesdir = \ + ${includedir}/bitcoin/database/memory/interfaces + +include_bitcoin_database_memory_interfaces_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/memory/interfaces/storage.hpp + +include_bitcoin_database_primitivesdir = \ + ${includedir}/bitcoin/database/primitives + +include_bitcoin_database_primitives_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/primitives/arrayhead.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/arraymap.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/body.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/column.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/hashhead.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/hashmap.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/hashmaps.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/iterator.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/keys.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/linkage.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/nohead.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/nomap.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/nomaps.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/primitives.hpp + +include_bitcoin_database_tablesdir = \ + ${includedir}/bitcoin/database/tables + +include_bitcoin_database_tables_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/context.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/event.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/names.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/schema.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/table.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/tables.hpp + +include_bitcoin_database_tables_archivesdir = \ + ${includedir}/bitcoin/database/tables/archives + +include_bitcoin_database_tables_archives_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/archives/header.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/input.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/ins.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/output.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/outs.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/transaction.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/txs.hpp + +include_bitcoin_database_tables_cachesdir = \ + ${includedir}/bitcoin/database/tables/caches + +include_bitcoin_database_tables_caches_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/caches/duplicate.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/ecdsa.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/prevalid.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/prevout.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/schnorr.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/silent.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/validated_bk.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/validated_tx.hpp + +include_bitcoin_database_tables_indexesdir = \ + ${includedir}/bitcoin/database/tables/indexes + +include_bitcoin_database_tables_indexes_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/indexes/height.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/indexes/strong_tx.hpp + +include_bitcoin_database_tables_optionalsdir = \ + ${includedir}/bitcoin/database/tables/optionals + +include_bitcoin_database_tables_optionals_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/optionals/address.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/optionals/filter_bk.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/optionals/filter_tx.hpp + +include_bitcoin_database_typesdir = \ + ${includedir}/bitcoin/database/types + +include_bitcoin_database_types_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/types/association.hpp \ + ${srcdir}/../../include/bitcoin/database/types/associations.hpp \ + ${srcdir}/../../include/bitcoin/database/types/block_state.hpp \ + ${srcdir}/../../include/bitcoin/database/types/fee_rate.hpp \ + ${srcdir}/../../include/bitcoin/database/types/header_state.hpp \ + ${srcdir}/../../include/bitcoin/database/types/history.hpp \ + ${srcdir}/../../include/bitcoin/database/types/multisig_view.hpp \ + ${srcdir}/../../include/bitcoin/database/types/point_set.hpp \ + ${srcdir}/../../include/bitcoin/database/types/position.hpp \ + ${srcdir}/../../include/bitcoin/database/types/span.hpp \ + ${srcdir}/../../include/bitcoin/database/types/tx_state.hpp \ + ${srcdir}/../../include/bitcoin/database/types/type.hpp \ + ${srcdir}/../../include/bitcoin/database/types/types.hpp \ + ${srcdir}/../../include/bitcoin/database/types/unspent.hpp + +# Tests. +#============================================================================== +# Target test 'test/libbitcoin-database-test' +#------------------------------------------------------------------------------ +if WITH_TESTS + +check_PROGRAMS = test/libbitcoin-database-test + +test_libbitcoin_database_test_CPPFLAGS = \ + ${boost_BUILD_CPPFLAGS} \ + ${boost_unit_test_framework_BUILD_CPPFLAGS} \ + ${src_libbitcoin_database_la_CPPFLAGS} + +test_libbitcoin_database_test_LDFLAGS = \ + ${boost_LDFLAGS} \ + ${boost_unit_test_framework_LDFLAGS} \ + ${src_libbitcoin_database_la_LDFLAGS} + +test_libbitcoin_database_test_LDADD = \ + ${boost_LIBS} \ + ${boost_unit_test_framework_LIBS} \ + ${src_libbitcoin_database_la_LIBS} \ + ${src_libbitcoin_database_la_LIBADD} + +test_libbitcoin_database_test_SOURCES = \ + ${srcdir}/../../test/error.cpp \ + ${srcdir}/../../test/main.cpp \ + ${srcdir}/../../test/settings.cpp \ + ${srcdir}/../../test/test.cpp \ + ${srcdir}/../../test/file/rotator.cpp \ + ${srcdir}/../../test/file/utilities.cpp \ + ${srcdir}/../../test/locks/file_lock.cpp \ + ${srcdir}/../../test/locks/flush_lock.cpp \ + ${srcdir}/../../test/locks/interprocess_lock.cpp \ + ${srcdir}/../../test/memory/accessor.cpp \ + ${srcdir}/../../test/memory/mmap.cpp \ + ${srcdir}/../../test/memory/utilities.cpp \ + ${srcdir}/../../test/mocks/blocks.cpp \ + ${srcdir}/../../test/primitives/arrayhead.cpp \ + ${srcdir}/../../test/primitives/arraymap.cpp \ + ${srcdir}/../../test/primitives/body.cpp \ + ${srcdir}/../../test/primitives/hashhead.cpp \ + ${srcdir}/../../test/primitives/hashmap.cpp \ + ${srcdir}/../../test/primitives/hashmaps.cpp \ + ${srcdir}/../../test/primitives/iterator.cpp \ + ${srcdir}/../../test/primitives/keys.cpp \ + ${srcdir}/../../test/primitives/linkage.cpp \ + ${srcdir}/../../test/primitives/nohead.cpp \ + ${srcdir}/../../test/primitives/nomap.cpp \ + ${srcdir}/../../test/query/amounts.cpp \ + ${srcdir}/../../test/query/confirmed.cpp \ + ${srcdir}/../../test/query/extent.cpp \ + ${srcdir}/../../test/query/fee_rate.cpp \ + ${srcdir}/../../test/query/filters.cpp \ + ${srcdir}/../../test/query/height.cpp \ + ${srcdir}/../../test/query/initialize.cpp \ + ${srcdir}/../../test/query/locator.cpp \ + ${srcdir}/../../test/query/merkle.cpp \ + ${srcdir}/../../test/query/properties_block.cpp \ + ${srcdir}/../../test/query/properties_tx.cpp \ + ${srcdir}/../../test/query/sequences.cpp \ + ${srcdir}/../../test/query/sizes.cpp \ + ${srcdir}/../../test/query/address/address_balance.cpp \ + ${srcdir}/../../test/query/address/address_history.cpp \ + ${srcdir}/../../test/query/address/address_outpoints.cpp \ + ${srcdir}/../../test/query/address/address_unspent.cpp \ + ${srcdir}/../../test/query/archive/chain_reader.cpp \ + ${srcdir}/../../test/query/archive/chain_writer.cpp \ + ${srcdir}/../../test/query/archive/wire_reader.cpp \ + ${srcdir}/../../test/query/archive/wire_writer.cpp \ + ${srcdir}/../../test/query/batch/ecdsa.cpp \ + ${srcdir}/../../test/query/batch/prevalid.cpp \ + ${srcdir}/../../test/query/batch/schnorr.cpp \ + ${srcdir}/../../test/query/batch/silent.cpp \ + ${srcdir}/../../test/query/consensus/consensus_block.cpp \ + ${srcdir}/../../test/query/consensus/consensus_chain_state.cpp \ + ${srcdir}/../../test/query/consensus/consensus_compact.cpp \ + ${srcdir}/../../test/query/consensus/consensus_forks.cpp \ + ${srcdir}/../../test/query/consensus/consensus_populate.cpp \ + ${srcdir}/../../test/query/consensus/consensus_prevouts.cpp \ + ${srcdir}/../../test/query/consensus/consensus_states.cpp \ + ${srcdir}/../../test/query/consensus/consensus_strong.cpp \ + ${srcdir}/../../test/query/consensus/consensus_transaction.cpp \ + ${srcdir}/../../test/query/consensus/consensus_work.cpp \ + ${srcdir}/../../test/query/navigate/navigate_arraymap.cpp \ + ${srcdir}/../../test/query/navigate/navigate_forward.cpp \ + ${srcdir}/../../test/query/navigate/navigate_hashmap.cpp \ + ${srcdir}/../../test/query/navigate/navigate_natural.cpp \ + ${srcdir}/../../test/query/navigate/navigate_reverse.cpp \ + ${srcdir}/../../test/store/store.cpp \ + ${srcdir}/../../test/store/store_backup.cpp \ + ${srcdir}/../../test/store/store_close.cpp \ + ${srcdir}/../../test/store/store_create.cpp \ + ${srcdir}/../../test/store/store_dump.cpp \ + ${srcdir}/../../test/store/store_events.cpp \ + ${srcdir}/../../test/store/store_open.cpp \ + ${srcdir}/../../test/store/store_open_load.cpp \ + ${srcdir}/../../test/store/store_prune.cpp \ + ${srcdir}/../../test/store/store_reload.cpp \ + ${srcdir}/../../test/store/store_report.cpp \ + ${srcdir}/../../test/store/store_restore.cpp \ + ${srcdir}/../../test/store/store_snapshot.cpp \ + ${srcdir}/../../test/store/store_tables.cpp \ + ${srcdir}/../../test/store/store_unload_close.cpp \ + ${srcdir}/../../test/tables/archives/header.cpp \ + ${srcdir}/../../test/tables/archives/input.cpp \ + ${srcdir}/../../test/tables/archives/ins.cpp \ + ${srcdir}/../../test/tables/archives/output.cpp \ + ${srcdir}/../../test/tables/archives/outs.cpp \ + ${srcdir}/../../test/tables/archives/transaction.cpp \ + ${srcdir}/../../test/tables/archives/txs.cpp \ + ${srcdir}/../../test/tables/caches/duplicate.cpp \ + ${srcdir}/../../test/tables/caches/ecdsa.cpp \ + ${srcdir}/../../test/tables/caches/prevalid.cpp \ + ${srcdir}/../../test/tables/caches/prevout.cpp \ + ${srcdir}/../../test/tables/caches/schnorr.cpp \ + ${srcdir}/../../test/tables/caches/silent.cpp \ + ${srcdir}/../../test/tables/caches/validated_bk.cpp \ + ${srcdir}/../../test/tables/caches/validated_tx.cpp \ + ${srcdir}/../../test/tables/indexes/height.cpp \ + ${srcdir}/../../test/tables/indexes/strong_tx.cpp \ + ${srcdir}/../../test/tables/optional/address.cpp \ + ${srcdir}/../../test/tables/optional/filter_bk.cpp \ + ${srcdir}/../../test/tables/optional/filter_tx.cpp \ + ${srcdir}/../../test/types/history.cpp \ + ${srcdir}/../../test/types/span.cpp \ + ${srcdir}/../../test/types/unspent.cpp + +TESTS = test_runner.sh + +endif WITH_TESTS + +# Binaries. +#============================================================================== +# Target binary 'tools/initchain/initchain' +#------------------------------------------------------------------------------ +if WITH_TOOLS + +noinst_PROGRAMS = tools/initchain/initchain + +tools_initchain_initchain_CPPFLAGS = \ + ${src_libbitcoin_database_la_CPPFLAGS} + +tools_initchain_initchain_LDFLAGS = \ + ${src_libbitcoin_database_la_LDFLAGS} + +tools_initchain_initchain_LDADD = \ + ${src_libbitcoin_database_la_LIBS} \ + ${src_libbitcoin_database_la_LIBADD} + +tools_initchain_initchain_SOURCES = \ + ${srcdir}/../../tools/initchain/initchain.cpp + +target_tools = tools/initchain/initchain + +tools: ${target_tools} + +endif WITH_TOOLS diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj index f6378c742..19300d399 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj @@ -1,332 +1,335 @@ - - - - - v143 - {62D7FBEE-4D52-424A-8938-59756E13D9F5} - libbitcoin-database - - - - DebugDLL - ARM - - - ReleaseDLL - ARM - - - DebugLTCG - ARM - - - ReleaseLTCG - ARM - - - DebugLIB - ARM - - - ReleaseLIB - ARM - - - DebugDLL - ARM64 - - - ReleaseDLL - ARM64 - - - DebugLTCG - ARM64 - - - ReleaseLTCG - ARM64 - - - DebugLIB - ARM64 - - - ReleaseLIB - ARM64 - - - DebugDLL - Win32 - - - ReleaseDLL - Win32 - - - DebugLTCG - Win32 - - - ReleaseLTCG - Win32 - - - DebugLIB - Win32 - - - ReleaseLIB - Win32 - - - DebugDLL - x64 - - - ReleaseDLL - x64 - - - DebugLTCG - x64 - - - ReleaseLTCG - x64 - - - DebugLIB - x64 - - - ReleaseLIB - x64 - - - - StaticLibrary - DynamicLibrary - - - - - - - - - - - - - - $(IntDir)src_file_utilities.obj - - - - - - - $(IntDir)src_memory_utilities.obj - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The Missing file is {0}. - - - - - - - - - - - - - - - - + + + + + v143 + {62D7FBEE-4D52-424A-8938-59756E13D9F5} + libbitcoin-database + + + + DebugDLL + ARM + + + ReleaseDLL + ARM + + + DebugLTCG + ARM + + + ReleaseLTCG + ARM + + + DebugLIB + ARM + + + ReleaseLIB + ARM + + + DebugDLL + ARM64 + + + ReleaseDLL + ARM64 + + + DebugLTCG + ARM64 + + + ReleaseLTCG + ARM64 + + + DebugLIB + ARM64 + + + ReleaseLIB + ARM64 + + + DebugDLL + Win32 + + + ReleaseDLL + Win32 + + + DebugLTCG + Win32 + + + ReleaseLTCG + Win32 + + + DebugLIB + Win32 + + + ReleaseLIB + Win32 + + + DebugDLL + x64 + + + ReleaseDLL + x64 + + + DebugLTCG + x64 + + + ReleaseLTCG + x64 + + + DebugLIB + x64 + + + ReleaseLIB + x64 + + + + StaticLibrary + DynamicLibrary + + + + + + + + + + + + + + $(IntDir)src_file_utilities.obj + + + + + + + + $(IntDir)src_memory_utilities.obj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The Missing file is {0}. + + + + + + + + + + + + + + + + diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters index caaa85efe..85293bd77 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -1,600 +1,609 @@ - - - - - - {62D7FBEE-4D52-424A-0000-000000000000} - - - {62D7FBEE-4D52-424A-0000-000000000001} - - - {62D7FBEE-4D52-424A-0000-000000000002} - - - {62D7FBEE-4D52-424A-0000-000000000003} - - - {62D7FBEE-4D52-424A-0000-000000000004} - - - {62D7FBEE-4D52-424A-0000-000000000005} - - - {62D7FBEE-4D52-424A-0000-000000000006} - - - {62D7FBEE-4D52-424A-0000-000000000007} - - - {62D7FBEE-4D52-424A-0000-000000000008} - - - {62D7FBEE-4D52-424A-0000-000000000009} - - - {62D7FBEE-4D52-424A-0000-00000000000A} - - - {62D7FBEE-4D52-424A-0000-00000000000B} - - - {62D7FBEE-4D52-424A-0000-00000000000C} - - - {62D7FBEE-4D52-424A-0000-00000000000D} - - - {62D7FBEE-4D52-424A-0000-00000000000E} - - - {62D7FBEE-4D52-424A-0000-00000000000F} - - - {62D7FBEE-4D52-424A-0000-000000000001} - - - {62D7FBEE-4D52-424A-0000-000000000002} - - - {62D7FBEE-4D52-424A-0000-000000000003} - - - {62D7FBEE-4D52-424A-0000-000000000004} - - - {62D7FBEE-4D52-424A-0000-000000000005} - - - {62D7FBEE-4D52-424A-0000-000000000006} - - - {62D7FBEE-4D52-424A-0000-000000000007} - - - {62D7FBEE-4D52-424A-0000-000000000008} - - - {62D7FBEE-4D52-424A-0000-000000000009} - - - {62D7FBEE-4D52-424A-0000-000000000010} - - - {62D7FBEE-4D52-424A-0000-0000000000A1} - - - {62D7FBEE-4D52-424A-0000-0000000000B1} - - - {62D7FBEE-4D52-424A-0000-0000000000C1} - - - - - src - - - src - - - src\file - - - src\file - - - src\locks - - - src\locks - - - src\locks - - - src\memory - - - src\memory - - - src - - - src\types - - - src\types - - - src\types - - - - - include\bitcoin - - - include\bitcoin\database - - - include\bitcoin\database - - - include\bitcoin\database - - - include\bitcoin\database\file - - - include\bitcoin\database\file - - - include\bitcoin\database\file - - - include\bitcoin\database\locks - - - include\bitcoin\database\locks - - - include\bitcoin\database\locks - - - include\bitcoin\database\locks - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory\interfaces - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database - - - include\bitcoin\database - - - include\bitcoin\database - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables\indexes - - - include\bitcoin\database\tables\indexes - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables\optionals - - - include\bitcoin\database\tables\optionals - - - include\bitcoin\database\tables\optionals - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database - - - - - include\bitcoin\database\impl\memory - - - include\bitcoin\database\impl\memory - - - include\bitcoin\database\impl\memory - - - include\bitcoin\database\impl\memory - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\query\address - - - include\bitcoin\database\impl\query\address - - - include\bitcoin\database\impl\query\address - - - include\bitcoin\database\impl\query\address - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query\archive - - - include\bitcoin\database\impl\query\archive - - - include\bitcoin\database\impl\query\archive - - - include\bitcoin\database\impl\query\archive - - - include\bitcoin\database\impl\query\batch - - - include\bitcoin\database\impl\query\batch - - - include\bitcoin\database\impl\query\batch - - - include\bitcoin\database\impl\query\batch - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - - - - - + + + + + + {62D7FBEE-4D52-424A-0000-000000000000} + + + {62D7FBEE-4D52-424A-0000-000000000001} + + + {62D7FBEE-4D52-424A-0000-000000000002} + + + {62D7FBEE-4D52-424A-0000-000000000003} + + + {62D7FBEE-4D52-424A-0000-000000000004} + + + {62D7FBEE-4D52-424A-0000-000000000005} + + + {62D7FBEE-4D52-424A-0000-000000000006} + + + {62D7FBEE-4D52-424A-0000-000000000007} + + + {62D7FBEE-4D52-424A-0000-000000000008} + + + {62D7FBEE-4D52-424A-0000-000000000009} + + + {62D7FBEE-4D52-424A-0000-00000000000A} + + + {62D7FBEE-4D52-424A-0000-00000000000B} + + + {62D7FBEE-4D52-424A-0000-00000000000C} + + + {62D7FBEE-4D52-424A-0000-00000000000D} + + + {62D7FBEE-4D52-424A-0000-00000000000E} + + + {62D7FBEE-4D52-424A-0000-00000000000F} + + + {62D7FBEE-4D52-424A-0000-000000000001} + + + {62D7FBEE-4D52-424A-0000-000000000002} + + + {62D7FBEE-4D52-424A-0000-000000000003} + + + {62D7FBEE-4D52-424A-0000-000000000004} + + + {62D7FBEE-4D52-424A-0000-000000000005} + + + {62D7FBEE-4D52-424A-0000-000000000006} + + + {62D7FBEE-4D52-424A-0000-000000000007} + + + {62D7FBEE-4D52-424A-0000-000000000008} + + + {62D7FBEE-4D52-424A-0000-000000000009} + + + {62D7FBEE-4D52-424A-0000-000000000010} + + + {62D7FBEE-4D52-424A-0000-0000000000A1} + + + {62D7FBEE-4D52-424A-0000-0000000000B1} + + + {62D7FBEE-4D52-424A-0000-0000000000C1} + + + + + src + + + src + + + src\file + + + src\file + + + src\locks + + + src\locks + + + src\locks + + + src\memory + + + src\memory + + + src\memory + + + src + + + src\types + + + src\types + + + src\types + + + + + include\bitcoin + + + include\bitcoin\database + + + include\bitcoin\database + + + include\bitcoin\database + + + include\bitcoin\database\file + + + include\bitcoin\database\file + + + include\bitcoin\database\file + + + include\bitcoin\database\locks + + + include\bitcoin\database\locks + + + include\bitcoin\database\locks + + + include\bitcoin\database\locks + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory\interfaces + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database + + + include\bitcoin\database + + + include\bitcoin\database + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables\indexes + + + include\bitcoin\database\tables\indexes + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables\optionals + + + include\bitcoin\database\tables\optionals + + + include\bitcoin\database\tables\optionals + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database + + + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\query\address + + + include\bitcoin\database\impl\query\address + + + include\bitcoin\database\impl\query\address + + + include\bitcoin\database\impl\query\address + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query\archive + + + include\bitcoin\database\impl\query\archive + + + include\bitcoin\database\impl\query\archive + + + include\bitcoin\database\impl\query\archive + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + + + + + diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj index a44a1e8e6..49769e11e 100644 --- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj @@ -1,332 +1,335 @@ - - - - - v145 - {62D7FBEE-4D52-424A-8938-59756E13D9F5} - libbitcoin-database - - - - DebugDLL - ARM - - - ReleaseDLL - ARM - - - DebugLTCG - ARM - - - ReleaseLTCG - ARM - - - DebugLIB - ARM - - - ReleaseLIB - ARM - - - DebugDLL - ARM64 - - - ReleaseDLL - ARM64 - - - DebugLTCG - ARM64 - - - ReleaseLTCG - ARM64 - - - DebugLIB - ARM64 - - - ReleaseLIB - ARM64 - - - DebugDLL - Win32 - - - ReleaseDLL - Win32 - - - DebugLTCG - Win32 - - - ReleaseLTCG - Win32 - - - DebugLIB - Win32 - - - ReleaseLIB - Win32 - - - DebugDLL - x64 - - - ReleaseDLL - x64 - - - DebugLTCG - x64 - - - ReleaseLTCG - x64 - - - DebugLIB - x64 - - - ReleaseLIB - x64 - - - - StaticLibrary - DynamicLibrary - - - - - - - - - - - - - - $(IntDir)src_file_utilities.obj - - - - - - - $(IntDir)src_memory_utilities.obj - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The Missing file is {0}. - - - - - - - - - - - - - - - - + + + + + v145 + {62D7FBEE-4D52-424A-8938-59756E13D9F5} + libbitcoin-database + + + + DebugDLL + ARM + + + ReleaseDLL + ARM + + + DebugLTCG + ARM + + + ReleaseLTCG + ARM + + + DebugLIB + ARM + + + ReleaseLIB + ARM + + + DebugDLL + ARM64 + + + ReleaseDLL + ARM64 + + + DebugLTCG + ARM64 + + + ReleaseLTCG + ARM64 + + + DebugLIB + ARM64 + + + ReleaseLIB + ARM64 + + + DebugDLL + Win32 + + + ReleaseDLL + Win32 + + + DebugLTCG + Win32 + + + ReleaseLTCG + Win32 + + + DebugLIB + Win32 + + + ReleaseLIB + Win32 + + + DebugDLL + x64 + + + ReleaseDLL + x64 + + + DebugLTCG + x64 + + + ReleaseLTCG + x64 + + + DebugLIB + x64 + + + ReleaseLIB + x64 + + + + StaticLibrary + DynamicLibrary + + + + + + + + + + + + + + $(IntDir)src_file_utilities.obj + + + + + + + + $(IntDir)src_memory_utilities.obj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The Missing file is {0}. + + + + + + + + + + + + + + + + diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters index caaa85efe..85293bd77 100644 --- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -1,600 +1,609 @@ - - - - - - {62D7FBEE-4D52-424A-0000-000000000000} - - - {62D7FBEE-4D52-424A-0000-000000000001} - - - {62D7FBEE-4D52-424A-0000-000000000002} - - - {62D7FBEE-4D52-424A-0000-000000000003} - - - {62D7FBEE-4D52-424A-0000-000000000004} - - - {62D7FBEE-4D52-424A-0000-000000000005} - - - {62D7FBEE-4D52-424A-0000-000000000006} - - - {62D7FBEE-4D52-424A-0000-000000000007} - - - {62D7FBEE-4D52-424A-0000-000000000008} - - - {62D7FBEE-4D52-424A-0000-000000000009} - - - {62D7FBEE-4D52-424A-0000-00000000000A} - - - {62D7FBEE-4D52-424A-0000-00000000000B} - - - {62D7FBEE-4D52-424A-0000-00000000000C} - - - {62D7FBEE-4D52-424A-0000-00000000000D} - - - {62D7FBEE-4D52-424A-0000-00000000000E} - - - {62D7FBEE-4D52-424A-0000-00000000000F} - - - {62D7FBEE-4D52-424A-0000-000000000001} - - - {62D7FBEE-4D52-424A-0000-000000000002} - - - {62D7FBEE-4D52-424A-0000-000000000003} - - - {62D7FBEE-4D52-424A-0000-000000000004} - - - {62D7FBEE-4D52-424A-0000-000000000005} - - - {62D7FBEE-4D52-424A-0000-000000000006} - - - {62D7FBEE-4D52-424A-0000-000000000007} - - - {62D7FBEE-4D52-424A-0000-000000000008} - - - {62D7FBEE-4D52-424A-0000-000000000009} - - - {62D7FBEE-4D52-424A-0000-000000000010} - - - {62D7FBEE-4D52-424A-0000-0000000000A1} - - - {62D7FBEE-4D52-424A-0000-0000000000B1} - - - {62D7FBEE-4D52-424A-0000-0000000000C1} - - - - - src - - - src - - - src\file - - - src\file - - - src\locks - - - src\locks - - - src\locks - - - src\memory - - - src\memory - - - src - - - src\types - - - src\types - - - src\types - - - - - include\bitcoin - - - include\bitcoin\database - - - include\bitcoin\database - - - include\bitcoin\database - - - include\bitcoin\database\file - - - include\bitcoin\database\file - - - include\bitcoin\database\file - - - include\bitcoin\database\locks - - - include\bitcoin\database\locks - - - include\bitcoin\database\locks - - - include\bitcoin\database\locks - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory\interfaces - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\memory - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database\primitives - - - include\bitcoin\database - - - include\bitcoin\database - - - include\bitcoin\database - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\archives - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables\caches - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables\indexes - - - include\bitcoin\database\tables\indexes - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables\optionals - - - include\bitcoin\database\tables\optionals - - - include\bitcoin\database\tables\optionals - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables - - - include\bitcoin\database\tables - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database\types - - - include\bitcoin\database - - - - - include\bitcoin\database\impl\memory - - - include\bitcoin\database\impl\memory - - - include\bitcoin\database\impl\memory - - - include\bitcoin\database\impl\memory - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\primitives - - - include\bitcoin\database\impl\query\address - - - include\bitcoin\database\impl\query\address - - - include\bitcoin\database\impl\query\address - - - include\bitcoin\database\impl\query\address - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query\archive - - - include\bitcoin\database\impl\query\archive - - - include\bitcoin\database\impl\query\archive - - - include\bitcoin\database\impl\query\archive - - - include\bitcoin\database\impl\query\batch - - - include\bitcoin\database\impl\query\batch - - - include\bitcoin\database\impl\query\batch - - - include\bitcoin\database\impl\query\batch - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query\consensus - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query\navigate - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\query - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - include\bitcoin\database\impl\store - - - - - - - + + + + + + {62D7FBEE-4D52-424A-0000-000000000000} + + + {62D7FBEE-4D52-424A-0000-000000000001} + + + {62D7FBEE-4D52-424A-0000-000000000002} + + + {62D7FBEE-4D52-424A-0000-000000000003} + + + {62D7FBEE-4D52-424A-0000-000000000004} + + + {62D7FBEE-4D52-424A-0000-000000000005} + + + {62D7FBEE-4D52-424A-0000-000000000006} + + + {62D7FBEE-4D52-424A-0000-000000000007} + + + {62D7FBEE-4D52-424A-0000-000000000008} + + + {62D7FBEE-4D52-424A-0000-000000000009} + + + {62D7FBEE-4D52-424A-0000-00000000000A} + + + {62D7FBEE-4D52-424A-0000-00000000000B} + + + {62D7FBEE-4D52-424A-0000-00000000000C} + + + {62D7FBEE-4D52-424A-0000-00000000000D} + + + {62D7FBEE-4D52-424A-0000-00000000000E} + + + {62D7FBEE-4D52-424A-0000-00000000000F} + + + {62D7FBEE-4D52-424A-0000-000000000001} + + + {62D7FBEE-4D52-424A-0000-000000000002} + + + {62D7FBEE-4D52-424A-0000-000000000003} + + + {62D7FBEE-4D52-424A-0000-000000000004} + + + {62D7FBEE-4D52-424A-0000-000000000005} + + + {62D7FBEE-4D52-424A-0000-000000000006} + + + {62D7FBEE-4D52-424A-0000-000000000007} + + + {62D7FBEE-4D52-424A-0000-000000000008} + + + {62D7FBEE-4D52-424A-0000-000000000009} + + + {62D7FBEE-4D52-424A-0000-000000000010} + + + {62D7FBEE-4D52-424A-0000-0000000000A1} + + + {62D7FBEE-4D52-424A-0000-0000000000B1} + + + {62D7FBEE-4D52-424A-0000-0000000000C1} + + + + + src + + + src + + + src\file + + + src\file + + + src\locks + + + src\locks + + + src\locks + + + src\memory + + + src\memory + + + src\memory + + + src + + + src\types + + + src\types + + + src\types + + + + + include\bitcoin + + + include\bitcoin\database + + + include\bitcoin\database + + + include\bitcoin\database + + + include\bitcoin\database\file + + + include\bitcoin\database\file + + + include\bitcoin\database\file + + + include\bitcoin\database\locks + + + include\bitcoin\database\locks + + + include\bitcoin\database\locks + + + include\bitcoin\database\locks + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory\interfaces + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\memory + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database\primitives + + + include\bitcoin\database + + + include\bitcoin\database + + + include\bitcoin\database + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\archives + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables\caches + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables\indexes + + + include\bitcoin\database\tables\indexes + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables\optionals + + + include\bitcoin\database\tables\optionals + + + include\bitcoin\database\tables\optionals + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables + + + include\bitcoin\database\tables + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database\types + + + include\bitcoin\database + + + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\memory + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\primitives + + + include\bitcoin\database\impl\query\address + + + include\bitcoin\database\impl\query\address + + + include\bitcoin\database\impl\query\address + + + include\bitcoin\database\impl\query\address + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query\archive + + + include\bitcoin\database\impl\query\archive + + + include\bitcoin\database\impl\query\archive + + + include\bitcoin\database\impl\query\archive + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query\batch + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query\consensus + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query\navigate + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\query + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + include\bitcoin\database\impl\store + + + + + + + diff --git a/include/bitcoin/database.hpp b/include/bitcoin/database.hpp index 8770fb51c..485a1cd0f 100644 --- a/include/bitcoin/database.hpp +++ b/include/bitcoin/database.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bitcoin/database/impl/memory/mmap.ipp b/include/bitcoin/database/impl/memory/mmap.ipp index 621ef877c..65ee5c3e9 100644 --- a/include/bitcoin/database/impl/memory/mmap.ipp +++ b/include/bitcoin/database/impl/memory/mmap.ipp @@ -1,124 +1,124 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_MEMORY_MMAP_IPP -#define LIBBITCOIN_DATABASE_MEMORY_MMAP_IPP - -#include -#include -#include -#include -#include - -namespace libbitcoin { -namespace database { - -// Constructors. -// ---------------------------------------------------------------------------- - -TEMPLATE -CLASS::mmap(const path& filename, size_t minimum, size_t expansion, - bool random, bool staged) NOEXCEPT - requires (is_one(columns)) - : filenames_{ filename }, - minimum_(to_rows(minimum)), - expansion_(expansion), - random_(random), - staged_(staged), - opened_{ file::invalid } -{ -} - -TEMPLATE -CLASS::mmap(const paths& filenames, size_t minimum, size_t expansion, - bool random, bool staged) NOEXCEPT - requires (columns > one) - : filenames_(filenames), - minimum_(to_rows(minimum)), - expansion_(expansion), - random_(random), - staged_(staged), - opened_{} -{ - opened_.fill(file::invalid); -} - -TEMPLATE -CLASS::~mmap() NOEXCEPT -{ - BC_ASSERT(!loaded_.load()); - BC_ASSERT(is_zero(logical_.load())); - BC_ASSERT(is_zero(capacity_.load())); - BC_ASSERT(std::ranges::all_of(memory_map_, - [](auto map) NOEXCEPT { return is_null(map); })); - BC_ASSERT(std::ranges::all_of(opened_, - [](auto opened) NOEXCEPT { return opened == file::invalid; })); -#if defined(HAVE_STAGING) - BC_ASSERT(std::ranges::all_of(reserved_, - [](auto reserved) NOEXCEPT { return is_zero(reserved); })); -#endif -} - -TEMPLATE -bool CLASS::is_open() const NOEXCEPT -{ - std::shared_lock field_lock(field_mutex_); - return opened_.front() != file::invalid; -} - -TEMPLATE -bool CLASS::is_loaded() const NOEXCEPT -{ - return loaded_.load(); -} - -// protected -// ---------------------------------------------------------------------------- - -TEMPLATE -size_t CLASS::to_capacity(size_t required) const NOEXCEPT -{ - // Covert required rows to capacity-padded rows. - using namespace system; - const auto growth = ceilinged_multiply(required, expansion_) / 100u; - return std::max(minimum_, ceilinged_add(required, growth)); -} - -// Write-write protected by remap_mutex. -TEMPLATE -void CLASS::set_first_code(const error::error_t& ec) NOEXCEPT -{ - if (!fault_.load()) - { - fault_.store(true); - - // error is atomic for public read exposure. - error_.store(ec); - } -} - -TEMPLATE -void CLASS::set_disk_space(size_t required) NOEXCEPT -{ - space_.store(required); -} - -} // namespace database -} // namespace libbitcoin - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_MEMORY_MMAP_IPP +#define LIBBITCOIN_DATABASE_MEMORY_MMAP_IPP + +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace database { + +// Constructors. +// ---------------------------------------------------------------------------- + +TEMPLATE +CLASS::mmap(const path& filename, size_t minimum, size_t expansion, + bool random, bool staged) NOEXCEPT + requires (is_one(columns)) + : filenames_{ filename }, + minimum_(to_rows(minimum)), + expansion_(expansion), + random_(random), + staged_(staged), + opened_{ file::invalid } +{ +} + +TEMPLATE +CLASS::mmap(const paths& filenames, size_t minimum, size_t expansion, + bool random, bool staged) NOEXCEPT + requires (columns > one) + : filenames_(filenames), + minimum_(to_rows(minimum)), + expansion_(expansion), + random_(random), + staged_(staged), + opened_{} +{ + opened_.fill(file::invalid); +} + +TEMPLATE +CLASS::~mmap() NOEXCEPT +{ + BC_ASSERT(!loaded_.load()); + BC_ASSERT(is_zero(logical_.load())); + BC_ASSERT(is_zero(capacity_.load())); + BC_ASSERT(std::ranges::all_of(memory_map_, + [](auto map) NOEXCEPT { return is_null(map); })); + BC_ASSERT(std::ranges::all_of(opened_, + [](auto opened) NOEXCEPT { return opened == file::invalid; })); +#if defined(MANAGE_STAGING) + BC_ASSERT(std::ranges::all_of(reserved_, + [](auto reserved) NOEXCEPT { return is_zero(reserved); })); +#endif +} + +TEMPLATE +bool CLASS::is_open() const NOEXCEPT +{ + std::shared_lock field_lock(field_mutex_); + return opened_.front() != file::invalid; +} + +TEMPLATE +bool CLASS::is_loaded() const NOEXCEPT +{ + return loaded_.load(); +} + +// protected +// ---------------------------------------------------------------------------- + +TEMPLATE +size_t CLASS::to_capacity(size_t required) const NOEXCEPT +{ + // Covert required rows to capacity-padded rows. + using namespace system; + const auto growth = ceilinged_multiply(required, expansion_) / 100u; + return std::max(minimum_, ceilinged_add(required, growth)); +} + +// Write-write protected by remap_mutex. +TEMPLATE +void CLASS::set_first_code(const error::error_t& ec) NOEXCEPT +{ + if (!fault_.load()) + { + fault_.store(true); + + // error is atomic for public read exposure. + error_.store(ec); + } +} + +TEMPLATE +void CLASS::set_disk_space(size_t required) NOEXCEPT +{ + space_.store(required); +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/memory/mmap_private.ipp b/include/bitcoin/database/impl/memory/mmap_private.ipp index adf69d79b..4e2e45435 100644 --- a/include/bitcoin/database/impl/memory/mmap_private.ipp +++ b/include/bitcoin/database/impl/memory/mmap_private.ipp @@ -24,6 +24,7 @@ #include #include #include +#include namespace libbitcoin { namespace database { @@ -43,12 +44,12 @@ TEMPLATE template bool CLASS::map_all_(std::index_sequence) NOEXCEPT { -#if defined(HAVE_STAGING) - // Get page size (usually 4KB), one bit ensures a power of two as required. - const int page_size = ::sysconf(_SC_PAGESIZE); - const auto page = system::possible_narrow_sign_cast(page_size); +#if defined(MANAGE_STAGING) + using namespace system; + const auto page = possible_narrow_sign_cast(page_size()); - if (page_size == fail || !is_one(system::ones_count(page))) + // Page size must be a power of two. + if (is_zero(page) || !is_one(ones_count(page))) { set_first_code(error::sysconf_failure); capacity_.store(zero); @@ -56,9 +57,11 @@ bool CLASS::map_all_(std::index_sequence) NOEXCEPT } page_ = page; - - // File content is fully flushed by definition at load. + cursor_ = zero; + ring_head_ = zero; + ring_size_ = zero; settled_.store(staged_ ? logical_.load() : zero); + frontier_.store(staged_ ? logical_.load() : zero); #endif if (!(map_() && ...)) @@ -79,8 +82,12 @@ bool CLASS::unmap_all_(std::index_sequence) NOEXCEPT const auto success = (unmap_(capacity) && ...); capacity_.store(zero); -#if defined(HAVE_STAGING) +#if defined(MANAGE_STAGING) + cursor_ = zero; + ring_head_ = zero; + ring_size_ = zero; settled_.store(zero); + frontier_.store(zero); #endif return success; @@ -108,12 +115,12 @@ bool CLASS::remap_all_(size_t capacity, std::index_sequence) NOEXCEPT TEMPLATE template bool CLASS::flush_(size_t - #if defined(HAVE_STAGING) || defined(HAVE_MSC) + #if defined(MANAGE_STAGING) || defined(HAVE_MSC) rows #endif ) NOEXCEPT { -#if defined(HAVE_STAGING) +#if defined(MANAGE_STAGING) // Transfer unflushed rows from anonymous memory to the file. Settled rows // are already on disk (staged); unstaged content is written in full. const auto from = to_width(staged_ ? settled_.load() : zero); @@ -180,14 +187,14 @@ bool CLASS::release_(size_t size) NOEXCEPT TEMPLATE template bool CLASS::unmap_(size_t - #if !defined(HAVE_STAGING) + #if !defined(MANAGE_STAGING) size #endif ) NOEXCEPT { const auto logical = to_width(logical_.load()); -#if defined(HAVE_STAGING) +#if defined(MANAGE_STAGING) // Persist unflushed rows, trim preallocation to logical, sync to disk. const auto from = to_width(staged_ ? settled_.load() : zero); const auto transferred = @@ -244,7 +251,7 @@ TEMPLATE template bool CLASS::map_() NOEXCEPT { -#if defined(HAVE_STAGING) +#if defined(MANAGE_STAGING) return stage_(); #else auto size = logical_.load(); @@ -274,7 +281,7 @@ bool CLASS::remap_(size_t size) NOEXCEPT if (is_zero(size)) size = minimum_; -#if defined(HAVE_STAGING) +#if defined(MANAGE_STAGING) // The file is preallocated to capacity, preserving disk full detection at // allocation, and growth commits reserved anonymous pages in place, so no // mapping is released and the map base is stable within the reservation. @@ -326,7 +333,7 @@ bool CLASS::remap_(size_t size) NOEXCEPT #endif return finalize_(size); -#endif // HAVE_STAGING +#endif // MANAGE_STAGING } // disk_full: space is set but no code is set with false return. @@ -421,313 +428,6 @@ bool CLASS::finalize_(size_t return true; } -#if defined(HAVE_STAGING) - -// staging dispatch, not thread safe. -// ---------------------------------------------------------------------------- -// private - -TEMPLATE -template -bool CLASS::settle_all_(size_t rows, std::index_sequence) NOEXCEPT -{ - const auto from = settled_.load(); - if (!(settle_(from, rows) && ...)) - return false; - - settled_.store(rows); - return true; -} - -TEMPLATE -template -bool CLASS::unsettle_all_(size_t rows, std::index_sequence) NOEXCEPT -{ - if (!(unsettle_(rows) && ...)) - return false; - - settled_.store(rows); - return true; -} - -// staging wrappers, not thread safe. -// ---------------------------------------------------------------------------- -// private - -// Stage failure results in unmapped. -// Staging has no effect on logical size, commits max(logical, min) capacity. -TEMPLATE -template -bool CLASS::stage_() NOEXCEPT -{ - auto size = logical_.load(); - - // Cannot map empty file, and want minimum capacity, so expand as required. - // disk_full: space is set but no code is set with false return. - if ((size < minimum_) && !resize_(size = minimum_)) - return false; - - // Reserve address space with generous multiple of capacity (costless). - const auto reserved = page_ceiling(to_width(to_reservation(size))); - const auto base = mmap_reserve(reserved); - - if (base == MAP_FAILED) - { - set_first_code(error::mmap_failure); - return false; - } - - memory_map_[Column] = system::pointer_cast(base); - reserved_[Column] = reserved; - - // Commit anonymous pages above the settle boundary page floor. - const auto settled = page_floor(to_width(settled_.load())); - const auto target = to_width(size); - - if ((target > settled) && (mmap_commit(std::next(memory_map_[Column], - settled), target - settled) == fail)) - { - teardown_(error::mmap_failure); - return false; - } - - // Populate anonymous memory from the file (unstaged content in full, - // staged only the settle boundary page remainder). - const auto logical = to_width(logical_.load()); - - if ((settled < logical) && !pread_all(opened_[Column], - std::next(memory_map_[Column], settled), logical - settled, settled)) - { - teardown_(error::fsync_failure); - return false; - } - - // Convert the settled prefix to a read-only file mapping. - if (!settle_(zero, settled_.load())) - return false; - - loaded_.store(true); - return true; -} - -// Commit failure results in unmapped. -// Growth within the reservation commits pages in place (stable map base); an -// exhausted reservation is replaced and its unsettled content copied, under -// the exclusive remap lock held by the caller. -TEMPLATE -template -bool CLASS::commit_(size_t size) NOEXCEPT -{ - const auto target = to_width(size); - - if (target <= reserved_[Column]) - { - // Never commit below the settle boundary page floor (the settled - // prefix is a read-only file mapping); recommit is idempotent. - const auto settled = page_floor(to_width(settled_.load())); - const auto current = page_floor(to_width(capacity_.load())); - const auto from = std::max(settled, current); - - if ((target > from) && (mmap_commit(std::next(memory_map_[Column], - from), target - from) == fail)) - { - teardown_(error::mmap_failure); - return false; - } - - return true; - } - - // Reservation exhausted, so reserve larger and migrate (rare by sizing). - const auto reserved = page_ceiling(to_width(to_reservation(size))); - const auto replace = mmap_reserve(reserved); - - if (replace == MAP_FAILED) - { - teardown_(error::mmap_failure); - return false; - } - - const auto base = system::pointer_cast(replace); - const auto settled = page_floor(to_width(settled_.load())); - - if (mmap_commit(std::next(base, settled), target - settled) == fail) - { - ::munmap(replace, reserved); - teardown_(error::mmap_failure); - return false; - } - - // Copy unsettled content (writes are excluded by the remap lock). - const auto logical = to_width(logical_.load()); - - if (settled < logical) - std::copy_n(std::next(memory_map_[Column], settled), - logical - settled, std::next(base, settled)); - - // Convert the settled prefix on the replacement reservation. - if (!is_zero(settled) && - (mmap_settle(replace, settled, opened_[Column], zero) == fail)) - { - ::munmap(replace, reserved); - teardown_(error::mmap_failure); - return false; - } - -#if !defined(WITHOUT_MADVISE) - if (!is_zero(settled) && !advise_(base, settled)) - { - ::munmap(replace, reserved); - teardown_(error::madvise_failure); - return false; - } -#endif - - // Release the exhausted reservation and adopt the replacement. - const auto released = ::munmap(memory_map_[Column], - reserved_[Column]) != fail; - - memory_map_[Column] = base; - reserved_[Column] = reserved; - - if (!released) - { - set_first_code(error::munmap_failure); - return false; - } - - return true; -} - -// Convert flushed rows [from, to) to a read-only shared file mapping, page -// floored so the settle boundary page remains anonymous with its settled -// bytes retained. Releases the covered anonymous pages. Failure results in -// unmapped. -TEMPLATE -template -bool CLASS::settle_(size_t from, size_t to) NOEXCEPT -{ - if (!staged_) - return true; - - const auto begin = page_floor(to_width(from)); - const auto end = page_floor(to_width(to)); - - if (begin == end) - return true; - - const auto address = std::next(memory_map_[Column], begin); - - if (mmap_settle(address, end - begin, opened_[Column], begin) == fail) - { - teardown_(error::mmap_failure); - return false; - } - -#if !defined(WITHOUT_MADVISE) - if (!advise_(address, end - begin)) - { - teardown_(error::madvise_failure); - return false; - } -#endif - - return true; -} - -// Revert settled pages at/above rows to committed anonymous memory and -// restore the retained bytes below rows from the file (truncation below the -// settle boundary). Failure results in unmapped. -TEMPLATE -template -bool CLASS::unsettle_(size_t rows) NOEXCEPT -{ - const auto bytes = to_width(rows); - const auto begin = page_floor(bytes); - const auto end = page_floor(to_width(settled_.load())); - - if (begin == end) - return true; - - const auto address = std::next(memory_map_[Column], begin); - - if (mmap_unsettle(address, end - begin) == fail) - { - teardown_(error::mmap_failure); - return false; - } - - if ((begin < bytes) && !pread_all(opened_[Column], address, bytes - begin, - begin)) - { - teardown_(error::fsync_failure); - return false; - } - - return true; -} - -// Teardown results in unmapped (release failure is not further reported). -TEMPLATE -template -void CLASS::teardown_(const error::error_t& ec) NOEXCEPT -{ - set_first_code(ec); - - if (!is_null(memory_map_[Column])) - ::munmap(memory_map_[Column], reserved_[Column]); - - memory_map_[Column] = {}; - reserved_[Column] = zero; - loaded_.store(false); -} - -// staging utilities, not thread safe. -// ---------------------------------------------------------------------------- -// private - -#if !defined(WITHOUT_MADVISE) -TEMPLATE -bool CLASS::advise_(uint8_t* map, size_t size) const NOEXCEPT -{ - // Use 1GB chunks to avoid large-length issues. - constexpr auto chunk = system::power2(30u); - const auto advice = random_ ? MADV_RANDOM : MADV_SEQUENTIAL; - - for (auto offset = zero; offset < size; offset += chunk) - { - const auto length = std::min(chunk, size - offset); - if (::madvise(std::next(map, offset), length, advice) == fail) - return false; - } - - return true; -} -#endif // WITHOUT_MADVISE - -// Reservation is address space only (costless), so multiply for headroom; -// exhaustion is handled by reservation replacement. -TEMPLATE -size_t CLASS::to_reservation(size_t rows) const NOEXCEPT -{ - constexpr size_t headroom = 4; - return system::ceilinged_multiply(to_capacity(std::max(rows, minimum_)), - headroom); -} - -TEMPLATE -size_t CLASS::page_floor(size_t bytes) const NOEXCEPT -{ - return system::bit_and(bytes, system::bit_not(sub1(page_))); -} - -TEMPLATE -size_t CLASS::page_ceiling(size_t bytes) const NOEXCEPT -{ - return page_floor(system::ceilinged_add(bytes, sub1(page_))); -} - -#endif // HAVE_STAGING } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/memory/mmap_staging.ipp b/include/bitcoin/database/impl/memory/mmap_staging.ipp new file mode 100644 index 000000000..d12b1e359 --- /dev/null +++ b/include/bitcoin/database/impl/memory/mmap_staging.ipp @@ -0,0 +1,438 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_MEMORY_MMAP_STAGING_IPP +#define LIBBITCOIN_DATABASE_MEMORY_MMAP_STAGING_IPP + +#include +#include +#include +#include + +namespace libbitcoin { +namespace database { + +// Write-completion accounting (staged instances). Extents chase completions, +// closing the gaps, settling the completed prefix. + +TEMPLATE +void CLASS::complete(size_t + #if defined(MANAGE_STAGING) + offset + #endif + , size_t + #if defined(MANAGE_STAGING) + count + #endif +) NOEXCEPT +{ +#if defined(MANAGE_STAGING) + std::unique_lock extent_lock(extent_mutex_); + + if (!staged_ || is_zero(ring_size_) || is_zero(count)) + return; + + // Cursor probe: completions cluster at the frontier, so the walk from the + // remembered extent (falling back to a full scan) is amortized O(1). + auto found = extents; + auto index = cursor_; + for (size_t probe{}; probe < ring_size_; ++probe) + { + const auto& record = ring_.at(index); + if ((offset >= record.start) && + (offset < system::ceilinged_add(record.start, record.count))) + { + found = index; + break; + } + + index = (index + one) % extents; + if (index == ((ring_head_ + ring_size_) % extents)) + index = ring_head_; + } + + if (found == extents) + return; + + cursor_ = found; + auto& record = ring_.at(found); + record.outstanding = system::floored_subtract(record.outstanding, count); + + // Pop completed extents from the head, advancing the frontier. + while (!is_zero(ring_size_) && is_zero(ring_.at(ring_head_).outstanding)) + { + ring_head_ = (ring_head_ + one) % extents; + --ring_size_; + } + + frontier_.store(is_zero(ring_size_) ? logical_.load() : + ring_.at(ring_head_).start); + + // Reset a cursor left pointing at a popped (dead) slot. + const auto live = !is_zero(ring_size_) && + (((found + extents - ring_head_) % extents) < ring_size_); + cursor_ = live ? found : ring_head_; +#endif +} + +TEMPLATE +size_t CLASS::frontier() const NOEXCEPT +{ +#if defined(MANAGE_STAGING) + if (staged_) + return frontier_.load(); +#endif + + return size(); +} + +#if defined(MANAGE_STAGING) + +TEMPLATE +void CLASS::record_(size_t start, size_t count) NOEXCEPT +{ + if (!staged_ || is_zero(count)) + return; + + std::unique_lock extent_lock(extent_mutex_); + + // Saturation stalls the frontier (conservative, safe); asserts in debug. + BC_ASSERT(ring_size_ < extents); + if (ring_size_ == extents) + return; + + const auto tail = (ring_head_ + ring_size_) % extents; + ring_.at(tail) = { start, count, count * columns }; + + if (is_zero(ring_size_++)) + { + frontier_.store(start); + cursor_ = ring_head_; + } +} + +// staging dispatch, not thread safe. +// ---------------------------------------------------------------------------- +// private + +TEMPLATE +template +bool CLASS::settle_all_(size_t rows, std::index_sequence) NOEXCEPT +{ + const auto from = settled_.load(); + if (!(settle_(from, rows) && ...)) + return false; + + settled_.store(rows); + return true; +} + +TEMPLATE +template +bool CLASS::unsettle_all_(size_t rows, std::index_sequence) NOEXCEPT +{ + if (!(unsettle_(rows) && ...)) + return false; + + settled_.store(rows); + return true; +} + +// staging wrappers, not thread safe. +// ---------------------------------------------------------------------------- +// private + +// Stage failure results in unmapped. +// Staging has no effect on logical size, commits max(logical, min) capacity. +TEMPLATE +template +bool CLASS::stage_() NOEXCEPT +{ + auto size = logical_.load(); + + // Cannot map empty file, and want minimum capacity, so expand as required. + // disk_full: space is set but no code is set with false return. + if ((size < minimum_) && !resize_(size = minimum_)) + return false; + + // Reserve address space with generous multiple of capacity (costless). + const auto reserved = page_ceiling(to_width(to_reservation(size))); + const auto base = mmap_reserve(reserved); + + if (base == MAP_FAILED) + { + set_first_code(error::mmap_failure); + return false; + } + + memory_map_[Column] = system::pointer_cast(base); + reserved_[Column] = reserved; + + // Commit anonymous pages above the settle boundary page floor. + const auto settled = page_floor(to_width(settled_.load())); + const auto target = to_width(size); + + if ((target > settled) && (mmap_commit(std::next(memory_map_[Column], + settled), target - settled) == fail)) + { + teardown_(error::mmap_failure); + return false; + } + + // Populate anonymous memory from the file (unstaged content in full, + // staged only the settle boundary page remainder). + const auto logical = to_width(logical_.load()); + + if ((settled < logical) && !pread_all(opened_[Column], + std::next(memory_map_[Column], settled), logical - settled, settled)) + { + teardown_(error::fsync_failure); + return false; + } + + // Convert the settled prefix to a read-only file mapping. + if (!settle_(zero, settled_.load())) + return false; + + loaded_.store(true); + return true; +} + +// Commit failure results in unmapped. +// Growth within the reservation commits pages in place (stable map base); an +// exhausted reservation is replaced and its unsettled content copied, under +// the exclusive remap lock held by the caller. +TEMPLATE +template +bool CLASS::commit_(size_t size) NOEXCEPT +{ + const auto target = to_width(size); + + if (target <= reserved_[Column]) + { + // Never commit below the settle boundary page floor (the settled + // prefix is a read-only file mapping); recommit is idempotent. + const auto settled = page_floor(to_width(settled_.load())); + const auto current = page_floor(to_width(capacity_.load())); + const auto from = std::max(settled, current); + + if ((target > from) && (mmap_commit(std::next(memory_map_[Column], + from), target - from) == fail)) + { + teardown_(error::mmap_failure); + return false; + } + + return true; + } + + // Reservation exhausted, so reserve larger and migrate (rare by sizing). + const auto reserved = page_ceiling(to_width(to_reservation(size))); + const auto replace = mmap_reserve(reserved); + + if (replace == MAP_FAILED) + { + teardown_(error::mmap_failure); + return false; + } + + const auto base = system::pointer_cast(replace); + const auto settled = page_floor(to_width(settled_.load())); + + if (mmap_commit(std::next(base, settled), target - settled) == fail) + { + ::munmap(replace, reserved); + teardown_(error::mmap_failure); + return false; + } + + // Copy unsettled content (writes are excluded by the remap lock). + const auto logical = to_width(logical_.load()); + + if (settled < logical) + std::copy_n(std::next(memory_map_[Column], settled), + logical - settled, std::next(base, settled)); + + // Convert the settled prefix on the replacement reservation. + if (!is_zero(settled) && + (mmap_settle(replace, settled, opened_[Column], zero) == fail)) + { + ::munmap(replace, reserved); + teardown_(error::mmap_failure); + return false; + } + +#if !defined(WITHOUT_MADVISE) + if (!is_zero(settled) && !advise_(base, settled)) + { + ::munmap(replace, reserved); + teardown_(error::madvise_failure); + return false; + } +#endif + + // Release the exhausted reservation and adopt the replacement. + const auto released = ::munmap(memory_map_[Column], + reserved_[Column]) != fail; + + memory_map_[Column] = base; + reserved_[Column] = reserved; + + if (!released) + { + set_first_code(error::munmap_failure); + return false; + } + + return true; +} + +// Convert flushed rows [from, to) to a read-only shared file mapping, page +// floored so the settle boundary page remains anonymous with its settled +// bytes retained. Releases the covered anonymous pages. Failure results in +// unmapped. +TEMPLATE +template +bool CLASS::settle_(size_t from, size_t to) NOEXCEPT +{ + if (!staged_) + return true; + + const auto begin = page_floor(to_width(from)); + const auto end = page_floor(to_width(to)); + + if (begin == end) + return true; + + const auto address = std::next(memory_map_[Column], begin); + + if (mmap_settle(address, end - begin, opened_[Column], begin) == fail) + { + teardown_(error::mmap_failure); + return false; + } + +#if !defined(WITHOUT_MADVISE) + if (!advise_(address, end - begin)) + { + teardown_(error::madvise_failure); + return false; + } +#endif + + return true; +} + +// Revert settled pages at/above rows to committed anonymous memory and +// restore the retained bytes below rows from the file (truncation below the +// settle boundary). Failure results in unmapped. +TEMPLATE +template +bool CLASS::unsettle_(size_t rows) NOEXCEPT +{ + const auto bytes = to_width(rows); + const auto begin = page_floor(bytes); + const auto end = page_floor(to_width(settled_.load())); + + if (begin == end) + return true; + + const auto address = std::next(memory_map_[Column], begin); + + if (mmap_unsettle(address, end - begin) == fail) + { + teardown_(error::mmap_failure); + return false; + } + + if ((begin < bytes) && !pread_all(opened_[Column], address, bytes - begin, + begin)) + { + teardown_(error::fsync_failure); + return false; + } + + return true; +} + +// Teardown results in unmapped (release failure is not further reported). +TEMPLATE +template +void CLASS::teardown_(const error::error_t& ec) NOEXCEPT +{ + set_first_code(ec); + + if (!is_null(memory_map_[Column])) + ::munmap(memory_map_[Column], reserved_[Column]); + + memory_map_[Column] = {}; + reserved_[Column] = zero; + loaded_.store(false); +} + +// staging utilities, not thread safe. +// ---------------------------------------------------------------------------- +// private + +#if !defined(WITHOUT_MADVISE) +TEMPLATE +bool CLASS::advise_(uint8_t* map, size_t size) const NOEXCEPT +{ + // Use 1GB chunks to avoid large-length issues. + constexpr auto chunk = system::power2(30u); + const auto advice = random_ ? MADV_RANDOM : MADV_SEQUENTIAL; + + for (auto offset = zero; offset < size; offset += chunk) + { + const auto length = std::min(chunk, size - offset); + if (::madvise(std::next(map, offset), length, advice) == fail) + return false; + } + + return true; +} +#endif // WITHOUT_MADVISE + +// Reservation is address space only (costless), so multiply for headroom; +// exhaustion is handled by reservation replacement. +TEMPLATE +size_t CLASS::to_reservation(size_t rows) const NOEXCEPT +{ + constexpr size_t headroom = 4; + return system::ceilinged_multiply(to_capacity(std::max(rows, minimum_)), + headroom); +} + +TEMPLATE +size_t CLASS::page_floor(size_t bytes) const NOEXCEPT +{ + return system::bit_and(bytes, system::bit_not(sub1(page_))); +} + +TEMPLATE +size_t CLASS::page_ceiling(size_t bytes) const NOEXCEPT +{ + return page_floor(system::ceilinged_add(bytes, sub1(page_))); +} + +#endif // MANAGE_STAGING + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/memory/mmap_storage.ipp b/include/bitcoin/database/impl/memory/mmap_storage.ipp index 54868179d..8798ae764 100644 --- a/include/bitcoin/database/impl/memory/mmap_storage.ipp +++ b/include/bitcoin/database/impl/memory/mmap_storage.ipp @@ -19,6 +19,7 @@ #ifndef LIBBITCOIN_DATABASE_MEMORY_MMAP_STORAGE_IPP #define LIBBITCOIN_DATABASE_MEMORY_MMAP_STORAGE_IPP +#include #include #include #include @@ -193,7 +194,7 @@ code CLASS::flush() NOEXCEPT return error::flush_failure; } -#if defined(HAVE_STAGING) +#if defined(MANAGE_STAGING) // Convert flushed rows to read-only file mappings, releasing their // anonymous pages to clean file cache (excludes accessors, briefly). if (staged_) @@ -308,7 +309,7 @@ bool CLASS::truncate(size_t count) NOEXCEPT if (count > logical_.load()) return false; -#if defined(HAVE_STAGING) +#if defined(MANAGE_STAGING) // Truncation below the settle boundary reverts settled rows to anonymous // memory, as append-only bodies may re-append after reorganization. if (staged_ && loaded_.load() && (count < settled_.load())) @@ -318,6 +319,34 @@ bool CLASS::truncate(size_t count) NOEXCEPT if (!unsettle_all_(count, sequence{})) return false; } + + // Discard extents above the truncation and clamp any overlap. + if (staged_) + { + std::unique_lock extent_lock(extent_mutex_); + while (!is_zero(ring_size_)) + { + auto& tail = ring_.at((ring_head_ + ring_size_ - one) % extents); + if (tail.start >= count) + { + --ring_size_; + continue; + } + + if (system::ceilinged_add(tail.start, tail.count) > count) + { + tail.count = count - tail.start; + tail.outstanding = std::min(tail.outstanding, + tail.count * columns); + } + + break; + } + + cursor_ = ring_head_; + frontier_.store(is_zero(ring_size_) ? count : + ring_.at(ring_head_).start); + } #endif logical_.store(count); @@ -393,7 +422,12 @@ size_t CLASS::allocate(size_t count) NOEXCEPT break; if (logical_.compare_exchange_weak(start, start + count)) + { +#if defined(MANAGE_STAGING) + record_(start, count); +#endif return start; + } } // Slow path: serialize capacity growth (at most one grower). Fast paths @@ -409,7 +443,12 @@ size_t CLASS::allocate(size_t count) NOEXCEPT if (end <= capacity_.load()) { if (logical_.compare_exchange_weak(start, end)) + { +#if defined(MANAGE_STAGING) + record_(start, count); +#endif return start; + } continue; } diff --git a/include/bitcoin/database/impl/primitives/arraymap.ipp b/include/bitcoin/database/impl/primitives/arraymap.ipp index 2e92c5105..9c88e2ff6 100644 --- a/include/bitcoin/database/impl/primitives/arraymap.ipp +++ b/include/bitcoin/database/impl/primitives/arraymap.ipp @@ -1,214 +1,218 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_ARRAYMAP_IPP -#define LIBBITCOIN_DATABASE_PRIMITIVES_ARRAYMAP_IPP - -#include -#include - -namespace libbitcoin { -namespace database { - -TEMPLATE -CLASS::arraymap(storage& header, storage& body, const Link& buckets) NOEXCEPT - : head_(header, buckets), body_(body) -{ -} - -// not thread safe -// ---------------------------------------------------------------------------- - -TEMPLATE -bool CLASS::create() NOEXCEPT -{ - Link count{}; - return head_.create() && head_.get_body_count(count) && - body_.truncate(count); -} - -TEMPLATE -bool CLASS::close() NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::clear() NOEXCEPT -{ - // Head is nullified and its body reference is zeroized. Body memory - // recovery requires truncate/unload/load, which should be preceded by a - // snapshot as all existing snapshots will be invalidated by the truncate. - // An intervening snapshot will capture the zero count body reference (and - // null head links) and will therefore be recoverable whether or not the - // truncation succeeds. - return head_.clear(); -} - -TEMPLATE -bool CLASS::backup(bool prune) NOEXCEPT -{ - return head_.set_body_count(prune ? Link{ 0 } : body_.count()); -} - -TEMPLATE -bool CLASS::restore() NOEXCEPT -{ - Link count{}; - return head_.verify() && head_.get_body_count(count) && - body_.truncate(count); -} - -TEMPLATE -bool CLASS::verify() const NOEXCEPT -{ - Link count{}; - return head_.verify() && head_.get_body_count(count) && - (count == body_.count()); -} - -// sizing -// ---------------------------------------------------------------------------- - -TEMPLATE -bool CLASS::enabled() const NOEXCEPT -{ - return head_.enabled(); -} - -TEMPLATE -size_t CLASS::buckets() const NOEXCEPT -{ - return head_.buckets(); -} - -TEMPLATE -size_t CLASS::head_size() const NOEXCEPT -{ - return head_.size(); -} - -TEMPLATE -size_t CLASS::body_size() const NOEXCEPT -{ - return body_.size(); -} - -TEMPLATE -size_t CLASS::capacity() const NOEXCEPT -{ - return body_.capacity(); -} - -TEMPLATE -Link CLASS::count() const NOEXCEPT -{ - return body_.count(); -} - -TEMPLATE -bool CLASS::expand(const Link& count) NOEXCEPT -{ - return body_.expand(count); -} - -// query interface -// ---------------------------------------------------------------------------- - -TEMPLATE -code CLASS::get_fault() const NOEXCEPT -{ - return body_.get_fault(); -} - -TEMPLATE -size_t CLASS::get_space() const NOEXCEPT -{ - return body_.get_space(); -} - -TEMPLATE -code CLASS::reload() NOEXCEPT -{ - return body_.reload(); -} - -// query interface -// ---------------------------------------------------------------------------- - -TEMPLATE -inline Link CLASS::at(size_t key) const NOEXCEPT -{ - return head_.at(key); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::at(size_t key, Element& element) const NOEXCEPT -{ - return get(at(key), element); -} - -TEMPLATE -inline Link CLASS::exists(size_t key) const NOEXCEPT -{ - return !at(key).is_terminal(); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT -{ - const auto ptr = body_.get(link); - if (!ptr) - return false; - - using namespace system; - iostream stream{ ptr }; - reader source{ stream }; - - if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(RowSize * element.count());) } - return element.from_data(source); -} - -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::put(size_t key, const Element& element) NOEXCEPT -{ - // Avoid setting at/above terminal sentinel into a bucket position. - if (key >= Link::terminal) - return false; - - const auto link = body_.allocate(element.count()); - const auto ptr = body_.get(link); - if (!ptr) - return false; - - // iostream.flush is a nop (direct copy). - using namespace system; - iostream stream{ ptr }; - finalizer sink{ stream }; - - if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) } - return element.to_data(sink) && head_.push(link, head_.index(key)); -} - -} // namespace database -} // namespace libbitcoin - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_ARRAYMAP_IPP +#define LIBBITCOIN_DATABASE_PRIMITIVES_ARRAYMAP_IPP + +#include +#include + +namespace libbitcoin { +namespace database { + +TEMPLATE +CLASS::arraymap(storage& header, storage& body, const Link& buckets) NOEXCEPT + : head_(header, buckets), body_(body) +{ +} + +// not thread safe +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::create() NOEXCEPT +{ + Link count{}; + return head_.create() && head_.get_body_count(count) && + body_.truncate(count); +} + +TEMPLATE +bool CLASS::close() NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::clear() NOEXCEPT +{ + // Head is nullified and its body reference is zeroized. Body memory + // recovery requires truncate/unload/load, which should be preceded by a + // snapshot as all existing snapshots will be invalidated by the truncate. + // An intervening snapshot will capture the zero count body reference (and + // null head links) and will therefore be recoverable whether or not the + // truncation succeeds. + return head_.clear(); +} + +TEMPLATE +bool CLASS::backup(bool prune) NOEXCEPT +{ + return head_.set_body_count(prune ? Link{ 0 } : body_.count()); +} + +TEMPLATE +bool CLASS::restore() NOEXCEPT +{ + Link count{}; + return head_.verify() && head_.get_body_count(count) && + body_.truncate(count); +} + +TEMPLATE +bool CLASS::verify() const NOEXCEPT +{ + Link count{}; + return head_.verify() && head_.get_body_count(count) && + (count == body_.count()); +} + +// sizing +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::enabled() const NOEXCEPT +{ + return head_.enabled(); +} + +TEMPLATE +size_t CLASS::buckets() const NOEXCEPT +{ + return head_.buckets(); +} + +TEMPLATE +size_t CLASS::head_size() const NOEXCEPT +{ + return head_.size(); +} + +TEMPLATE +size_t CLASS::body_size() const NOEXCEPT +{ + return body_.size(); +} + +TEMPLATE +size_t CLASS::capacity() const NOEXCEPT +{ + return body_.capacity(); +} + +TEMPLATE +Link CLASS::count() const NOEXCEPT +{ + return body_.count(); +} + +TEMPLATE +bool CLASS::expand(const Link& count) NOEXCEPT +{ + return body_.expand(count); +} + +// query interface +// ---------------------------------------------------------------------------- + +TEMPLATE +code CLASS::get_fault() const NOEXCEPT +{ + return body_.get_fault(); +} + +TEMPLATE +size_t CLASS::get_space() const NOEXCEPT +{ + return body_.get_space(); +} + +TEMPLATE +code CLASS::reload() NOEXCEPT +{ + return body_.reload(); +} + +// query interface +// ---------------------------------------------------------------------------- + +TEMPLATE +inline Link CLASS::at(size_t key) const NOEXCEPT +{ + return head_.at(key); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::at(size_t key, Element& element) const NOEXCEPT +{ + return get(at(key), element); +} + +TEMPLATE +inline Link CLASS::exists(size_t key) const NOEXCEPT +{ + return !at(key).is_terminal(); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT +{ + const auto ptr = body_.get(link); + if (!ptr) + return false; + + using namespace system; + iostream stream{ ptr }; + reader source{ stream }; + + if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(RowSize * element.count());) } + return element.from_data(source); +} + +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::put(size_t key, const Element& element) NOEXCEPT +{ + // Avoid setting at/above terminal sentinel into a bucket position. + if (key >= Link::terminal) + return false; + + const auto link = body_.allocate(element.count()); + const auto ptr = body_.get(link); + if (!ptr) + return false; + + // iostream.flush is a nop (direct copy). + using namespace system; + iostream stream{ ptr }; + finalizer sink{ stream }; + + if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) } + if (!element.to_data(sink) || !head_.push(link, head_.index(key))) + return false; + + body_.complete(link, element.count()); + return true; +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/primitives/body.ipp b/include/bitcoin/database/impl/primitives/body.ipp index b30fff1aa..1c3fa7b4b 100644 --- a/include/bitcoin/database/impl/primitives/body.ipp +++ b/include/bitcoin/database/impl/primitives/body.ipp @@ -1,296 +1,302 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_BODY_IPP -#define LIBBITCOIN_DATABASE_PRIMITIVES_BODY_IPP - -#include - -namespace libbitcoin { -namespace database { - -TEMPLATE -template -inline memory CLASS::get() const NOEXCEPT -{ - if constexpr (is_one(columns)) - return files_.get(); - else - return files_.get_at(Column); -} - -TEMPLATE -template -inline memory CLASS::get(const Link& link) const NOEXCEPT -{ - if (link.is_terminal()) - return {}; - - const auto position = link_to_position(link); - - // memory.size() may be negative (stream treats as exhausted). - if constexpr (is_one(columns)) - return files_.get(position); - else - return files_.get_at(Column, position); -} - -TEMPLATE -template -inline memory::iterator CLASS::get_raw1(const Link& link) const NOEXCEPT -{ - if (link.is_terminal()) - return {}; - - const auto position = link_to_position(link); - - // memory.size() may be negative (stream treats as exhausted). - if constexpr (is_one(columns)) - return files_.get_raw(position); - else - return files_.get_raw_at(Column, position); -} - -TEMPLATE -template > -inline memory CLASS::get_capacity(const Link& link) const NOEXCEPT -{ - if (link.is_terminal()) - return {}; - - return files_.get_capacity(link_to_position(link)); -} - -TEMPLATE -CLASS::bodys(storage& body) NOEXCEPT - : files_(body) -{ -} - -TEMPLATE -inline size_t CLASS::size() const NOEXCEPT -{ - if constexpr (is_one(columns)) - return files_.size(); - else - return files_.size() * strides(std::make_index_sequence{}); -} - -TEMPLATE -inline size_t CLASS::capacity() const NOEXCEPT -{ - if constexpr (is_one(columns)) - return files_.capacity(); - else - return files_.capacity() * strides(std::make_index_sequence{}); -} - -TEMPLATE -inline Link CLASS::count() const NOEXCEPT -{ - return elements_to_link(files_.size()); -} - -TEMPLATE -bool CLASS::truncate(const Link& count) NOEXCEPT -{ - if (count.is_terminal()) - return false; - - // Truncate to count visible records (absolute, shared row count). - return files_.truncate(link_to_elements(count)); -} - -TEMPLATE -bool CLASS::expand(const Link& count) NOEXCEPT -{ - if (count.is_terminal()) - return false; - - // Expand to count visible records (absolute, shared row count). - return files_.expand(link_to_elements(count)); -} - -TEMPLATE -bool CLASS::reserve(const Link& count) NOEXCEPT -{ - if (count.is_terminal()) - return false; - - // Expand by count invisible records (relative, shared row count). - return files_.reserve(link_to_elements(count)); -} - -TEMPLATE -Link CLASS::allocate(const Link& count) NOEXCEPT -{ - if (count.is_terminal()) - return count; - - // One shared allocation across all columns (one lock). Expand by count - // visible records (relative), return absolute record link of first. - const auto start = files_.allocate(link_to_elements(count)); - - // Guards addition overflow in cast_link (start must be valid). The eof - // sentinel is in file denomination, so must be detected before conversion. - if (start == storage::eof) - return Link::terminal; - - // Callers (nomaps) handle terminal allocation. - return elements_to_link(start); -} - -// Errors. -// ---------------------------------------------------------------------------- - -TEMPLATE -code CLASS::get_fault() const NOEXCEPT -{ - return files_.get_fault(); -} - -TEMPLATE -size_t CLASS::get_space() const NOEXCEPT -{ - return files_.get_space(); -} - -TEMPLATE -code CLASS::reload() NOEXCEPT -{ - return files_.load(); -} - -// static -// ---------------------------------------------------------------------------- - -// private -TEMPLATE -template -constexpr size_t CLASS::stride() NOEXCEPT -{ - using namespace system; - constexpr auto size = std::get(sizes); - - if constexpr (size == max_size_t) - { - // Slab: link/key incorporated into size (byte-addressed map). - return size; - } - else if constexpr (is_zero(Column) && is_nonzero(key_size)) - { - // Spine of a keyed map: link/key precede the record. - return Link::size + key_size + size; - } - else - { - // Keyless (nomap/arraymap) or non-spine column: pure record. - static_assert(is_nonzero(size)); - return size; - } -} - -// private -TEMPLATE -template -constexpr size_t CLASS::strides(std::index_sequence) NOEXCEPT -{ - return (stride() + ...); -} - -TEMPLATE -template -constexpr size_t CLASS::link_to_position(const Link& link) NOEXCEPT -{ - using namespace system; - const auto value = possible_narrow_cast(link.value); - constexpr auto element_size = stride(); - - if constexpr (is_slab) - { - // Slab implies link/key incorporated into size. - return value; - } - else - { - // Record (keyed spine or keyless) implies fixed element stride. - BC_ASSERT(!is_multiply_overflow(value, element_size)); - return value * element_size; - } -} - -TEMPLATE -template -constexpr Link CLASS::position_to_link(size_t position) NOEXCEPT -{ - using namespace system; - constexpr auto element_size = stride(); - - if constexpr (is_slab) - { - // Slab implies link/key incorporated into size. - return { cast_link(position) }; - } - else - { - static_assert(is_nonzero(element_size)); - return { cast_link(position / element_size) }; - } -} - -// private -TEMPLATE -constexpr size_t CLASS::link_to_elements(const Link& link) NOEXCEPT -{ - using namespace system; - - // Single column: file elements are bytes (width one). Records convert - // by stride (slab passes through, slab links are already byte offsets). - // Aggregate: file rows are the shared record count (no conversion). - if constexpr (is_one(columns)) - return link_to_position(link); - else - return possible_narrow_cast(link.value); -} - -// private -TEMPLATE -constexpr Link CLASS::elements_to_link(size_t elements) NOEXCEPT -{ - // Inverse of link_to_elements (slab and aggregate pass through). - if constexpr (is_one(columns)) - return position_to_link(elements); - else - return { cast_link(elements) }; -} - -TEMPLATE -constexpr CLASS::integer CLASS::cast_link(size_t link) NOEXCEPT -{ - using namespace system; - constexpr auto terminal = Link::terminal; - - // link limit is sub1(terminal), where terminal is 2^((8*Link::bytes)-1). - // It is ok for the payload to exceed link limit (link is identity only). - return link >= terminal ? terminal : possible_narrow_cast(link); -} - -} // namespace database -} // namespace libbitcoin - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_BODY_IPP +#define LIBBITCOIN_DATABASE_PRIMITIVES_BODY_IPP + +#include + +namespace libbitcoin { +namespace database { + +TEMPLATE +template +inline memory CLASS::get() const NOEXCEPT +{ + if constexpr (is_one(columns)) + return files_.get(); + else + return files_.get_at(Column); +} + +TEMPLATE +template +inline memory CLASS::get(const Link& link) const NOEXCEPT +{ + if (link.is_terminal()) + return {}; + + const auto position = link_to_position(link); + + // memory.size() may be negative (stream treats as exhausted). + if constexpr (is_one(columns)) + return files_.get(position); + else + return files_.get_at(Column, position); +} + +TEMPLATE +template +inline memory::iterator CLASS::get_raw1(const Link& link) const NOEXCEPT +{ + if (link.is_terminal()) + return {}; + + const auto position = link_to_position(link); + + // memory.size() may be negative (stream treats as exhausted). + if constexpr (is_one(columns)) + return files_.get_raw(position); + else + return files_.get_raw_at(Column, position); +} + +TEMPLATE +template > +inline memory CLASS::get_capacity(const Link& link) const NOEXCEPT +{ + if (link.is_terminal()) + return {}; + + return files_.get_capacity(link_to_position(link)); +} + +TEMPLATE +CLASS::bodys(storage& body) NOEXCEPT + : files_(body) +{ +} + +TEMPLATE +inline size_t CLASS::size() const NOEXCEPT +{ + if constexpr (is_one(columns)) + return files_.size(); + else + return files_.size() * strides(std::make_index_sequence{}); +} + +TEMPLATE +inline size_t CLASS::capacity() const NOEXCEPT +{ + if constexpr (is_one(columns)) + return files_.capacity(); + else + return files_.capacity() * strides(std::make_index_sequence{}); +} + +TEMPLATE +inline Link CLASS::count() const NOEXCEPT +{ + return elements_to_link(files_.size()); +} + +TEMPLATE +bool CLASS::truncate(const Link& count) NOEXCEPT +{ + if (count.is_terminal()) + return false; + + // Truncate to count visible records (absolute, shared row count). + return files_.truncate(link_to_elements(count)); +} + +TEMPLATE +bool CLASS::expand(const Link& count) NOEXCEPT +{ + if (count.is_terminal()) + return false; + + // Expand to count visible records (absolute, shared row count). + return files_.expand(link_to_elements(count)); +} + +TEMPLATE +bool CLASS::reserve(const Link& count) NOEXCEPT +{ + if (count.is_terminal()) + return false; + + // Expand by count invisible records (relative, shared row count). + return files_.reserve(link_to_elements(count)); +} + +TEMPLATE +Link CLASS::allocate(const Link& count) NOEXCEPT +{ + if (count.is_terminal()) + return count; + + // One shared allocation across all columns (one lock). Expand by count + // visible records (relative), return absolute record link of first. + const auto start = files_.allocate(link_to_elements(count)); + + // Guards addition overflow in cast_link (start must be valid). The eof + // sentinel is in file denomination, so must be detected before conversion. + if (start == storage::eof) + return Link::terminal; + + // Callers (nomaps) handle terminal allocation. + return elements_to_link(start); +} + +TEMPLATE +void CLASS::complete(const Link& link, const Link& count) NOEXCEPT +{ + files_.complete(link_to_elements(link), link_to_elements(count)); +} + +// Errors. +// ---------------------------------------------------------------------------- + +TEMPLATE +code CLASS::get_fault() const NOEXCEPT +{ + return files_.get_fault(); +} + +TEMPLATE +size_t CLASS::get_space() const NOEXCEPT +{ + return files_.get_space(); +} + +TEMPLATE +code CLASS::reload() NOEXCEPT +{ + return files_.load(); +} + +// static +// ---------------------------------------------------------------------------- + +// private +TEMPLATE +template +constexpr size_t CLASS::stride() NOEXCEPT +{ + using namespace system; + constexpr auto size = std::get(sizes); + + if constexpr (size == max_size_t) + { + // Slab: link/key incorporated into size (byte-addressed map). + return size; + } + else if constexpr (is_zero(Column) && is_nonzero(key_size)) + { + // Spine of a keyed map: link/key precede the record. + return Link::size + key_size + size; + } + else + { + // Keyless (nomap/arraymap) or non-spine column: pure record. + static_assert(is_nonzero(size)); + return size; + } +} + +// private +TEMPLATE +template +constexpr size_t CLASS::strides(std::index_sequence) NOEXCEPT +{ + return (stride() + ...); +} + +TEMPLATE +template +constexpr size_t CLASS::link_to_position(const Link& link) NOEXCEPT +{ + using namespace system; + const auto value = possible_narrow_cast(link.value); + constexpr auto element_size = stride(); + + if constexpr (is_slab) + { + // Slab implies link/key incorporated into size. + return value; + } + else + { + // Record (keyed spine or keyless) implies fixed element stride. + BC_ASSERT(!is_multiply_overflow(value, element_size)); + return value * element_size; + } +} + +TEMPLATE +template +constexpr Link CLASS::position_to_link(size_t position) NOEXCEPT +{ + using namespace system; + constexpr auto element_size = stride(); + + if constexpr (is_slab) + { + // Slab implies link/key incorporated into size. + return { cast_link(position) }; + } + else + { + static_assert(is_nonzero(element_size)); + return { cast_link(position / element_size) }; + } +} + +// private +TEMPLATE +constexpr size_t CLASS::link_to_elements(const Link& link) NOEXCEPT +{ + using namespace system; + + // Single column: file elements are bytes (width one). Records convert + // by stride (slab passes through, slab links are already byte offsets). + // Aggregate: file rows are the shared record count (no conversion). + if constexpr (is_one(columns)) + return link_to_position(link); + else + return possible_narrow_cast(link.value); +} + +// private +TEMPLATE +constexpr Link CLASS::elements_to_link(size_t elements) NOEXCEPT +{ + // Inverse of link_to_elements (slab and aggregate pass through). + if constexpr (is_one(columns)) + return position_to_link(elements); + else + return { cast_link(elements) }; +} + +TEMPLATE +constexpr CLASS::integer CLASS::cast_link(size_t link) NOEXCEPT +{ + using namespace system; + constexpr auto terminal = Link::terminal; + + // link limit is sub1(terminal), where terminal is 2^((8*Link::bytes)-1). + // It is ok for the payload to exceed link limit (link is identity only). + return link >= terminal ? terminal : possible_narrow_cast(link); +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/primitives/hashmap.ipp b/include/bitcoin/database/impl/primitives/hashmap.ipp index 3d1087c65..fad23f867 100644 --- a/include/bitcoin/database/impl/primitives/hashmap.ipp +++ b/include/bitcoin/database/impl/primitives/hashmap.ipp @@ -1,567 +1,577 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAP_IPP -#define LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAP_IPP - -#include -#include -#include - -namespace libbitcoin { -namespace database { - -TEMPLATE -CLASS::hashmap(storage& header, storage& body, const Link& buckets) NOEXCEPT - : head_(header, buckets), body_(body) -{ -} - -// not thread safe -// ---------------------------------------------------------------------------- - -TEMPLATE -bool CLASS::create() NOEXCEPT -{ - Link count{}; - return head_.create() && head_.get_body_count(count) && - body_.truncate(count); -} - -TEMPLATE -bool CLASS::close() NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::backup(bool) NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::restore() NOEXCEPT -{ - Link count{}; - return head_.verify() && head_.get_body_count(count) && - body_.truncate(count); -} - -TEMPLATE -bool CLASS::verify() const NOEXCEPT -{ - Link count{}; - return head_.verify() && head_.get_body_count(count) && - (count == body_.count()); -} - -// sizing -// ---------------------------------------------------------------------------- - -TEMPLATE -bool CLASS::enabled() const NOEXCEPT -{ - return head_.buckets() > one; -} - -TEMPLATE -size_t CLASS::buckets() const NOEXCEPT -{ - return head_.buckets(); -} - -TEMPLATE -size_t CLASS::head_size() const NOEXCEPT -{ - return head_.size(); -} - -TEMPLATE -size_t CLASS::body_size() const NOEXCEPT -{ - return body_.size(); -} - -TEMPLATE -size_t CLASS::capacity() const NOEXCEPT -{ - return body_.capacity(); -} - -TEMPLATE -Link CLASS::count() const NOEXCEPT -{ - return body_.count(); -} - -TEMPLATE -bool CLASS::expand(const Link& count) NOEXCEPT -{ - return body_.expand(count); -} - -// diagnostic counters -// ---------------------------------------------------------------------------- - -TEMPLATE -size_t CLASS::positive_search_count() const NOEXCEPT -{ - return positive_.load(std::memory_order_relaxed); -} - -TEMPLATE -size_t CLASS::negative_search_count() const NOEXCEPT -{ - return negative_.load(std::memory_order_relaxed); -} - -// query interface -// ---------------------------------------------------------------------------- - -TEMPLATE -code CLASS::get_fault() const NOEXCEPT -{ - return body_.get_fault(); -} - -TEMPLATE -size_t CLASS::get_space() const NOEXCEPT -{ - return body_.get_space(); -} - -TEMPLATE -code CLASS::reload() NOEXCEPT -{ - return body_.reload(); -} - -// query interface -// ---------------------------------------------------------------------------- - -TEMPLATE -inline Link CLASS::top(const Link& link) const NOEXCEPT -{ - if (link >= head_.buckets()) - return {}; - - return head_.top(link); -} - -TEMPLATE -inline bool CLASS::exists(const memory& ptr, const Key& key) const NOEXCEPT -{ - return !first(ptr, key).is_terminal(); -} - -TEMPLATE -inline bool CLASS::exists(const Key& key) const NOEXCEPT -{ - return !first(key).is_terminal(); -} - -TEMPLATE -inline Link CLASS::first(const memory& ptr, const Key& key) const NOEXCEPT -{ - return first(ptr, head_.top(key), key); -} - -TEMPLATE -inline Link CLASS::first(const Key& key) const NOEXCEPT -{ - return first(get_memory(), key); -} - -TEMPLATE -inline typename CLASS::iterator CLASS::it(Key&& key) const NOEXCEPT -{ - const auto top = head_.top(key); - return { get_memory(), top, std::forward(key) }; -} - -TEMPLATE -inline typename CLASS::iterator CLASS::it(const Key& key) const NOEXCEPT -{ - return { get_memory(), head_.top(key), key }; -} - -TEMPLATE -inline Link CLASS::allocate(const Link& size) NOEXCEPT -{ - return body_.allocate(size); -} - -TEMPLATE -inline memory CLASS::get_memory() const NOEXCEPT -{ - return body_.get(); -} - -TEMPLATE -Key CLASS::get_key(const Link& link) NOEXCEPT -{ - using namespace system; - const auto ptr = body_.get(link); - if (!ptr || is_lesser(ptr.size(), index_size)) - return {}; - - return unsafe_array_cast(std::next(ptr.begin(), - Link::size)); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::find(const Key& key, Element& element) const NOEXCEPT -{ - return !find_link(key, element).is_terminal(); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline Link CLASS::find_link(const Key& key, Element& element) const NOEXCEPT -{ - // This override avoids duplicated memory construct in get(first()). - const auto ptr = get_memory(); - const auto link = first(ptr, head_.top(key), key); - if (link.is_terminal()) - return {}; - - return read(ptr, link, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT -{ - // This override is the normal form. - return read(get_memory(), link, element); -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const memory& ptr, const Link& link, - Element& element) NOEXCEPT -{ - return read(ptr, link, element); -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const iterator& it, Element& element) NOEXCEPT -{ - // This override avoids deadlock when holding iterator to the same table. - return read(it.ptr(), *it, element); -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const iterator& it, const Link& link, - Element& element) NOEXCEPT -{ - // This override avoids deadlock when holding iterator to the same table. - return read(it.ptr(), link, element); -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::set(const memory& ptr, const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr) - return false; - - const auto start = body::link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - // Stream starts at record and the index is skipped for reader convenience. - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - // Set element search key. - unsafe_array_cast(std::next(offset, - Link::size)) = key; - - iostream stream{ offset, size - position }; - finalizer sink{ stream }; - sink.skip_bytes(index_size); - - if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) } - return element.to_data(sink); -} - -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::set(const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - return set(get_memory(), link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline Link CLASS::set_link(const Key& key, const Element& element) NOEXCEPT -{ - Link link{}; - if (!set_link(link, key, element)) - return {}; - - return link; -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::set_link(Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - link = allocate(element.count()); - return set(link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline Link CLASS::put_link(const Key& key, const Element& element) NOEXCEPT -{ - Link link{}; - if (!put_link(link, key, element)) - return {}; - - return link; -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put_link(Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - link = allocate(element.count()); - return put(link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put(const Key& key, const Element& element) NOEXCEPT -{ - return !put_link(key, element).is_terminal(); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put(const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - // This override is the normal form. - return write(get_memory(), link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put(const memory& ptr, const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - return write(ptr, link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put(bool& duplicate, const memory& ptr, - const Link& link, const Key& key, const Element& element) NOEXCEPT -{ - Link previous{}; - if (!write(previous, ptr, link, key, element)) - return false; - - if (previous.is_terminal()) - { - duplicate = false; - ////negative_.fetch_add(one, std::memory_order_relaxed); - } - else - { - // Search the previous conflicts to determine if actual duplicate. - duplicate = !first(ptr, previous, key).is_terminal(); - ////positive_.fetch_add(one, std::memory_order_relaxed); - } - - return true; -} - -TEMPLATE -inline Link CLASS::commit_link(const Link& link, const Key& key) NOEXCEPT -{ - if (!commit(link, key)) - return {}; - - return link; -} - -TEMPLATE -inline bool CLASS::commit(const Link& link, const Key& key) NOEXCEPT -{ - return commit(get_memory(), link, key); -} - -TEMPLATE -bool CLASS::commit(const memory& ptr, const Link& link, - const Key& key) NOEXCEPT -{ - using namespace system; - if (!ptr) - return false; - - // get element offset (fault) - const auto offset = ptr.offset(body::link_to_position(link)); - if (is_null(offset)) - return false; - - // Commit element to search index (terminal is a valid bucket index). - auto& next = unsafe_array_cast(offset); - return head_.push(link, next, key); -} - -// protected -// ---------------------------------------------------------------------------- - -// static -TEMPLATE -Link CLASS::first(const memory& ptr, const Link& link, const Key& key) NOEXCEPT -{ - using namespace system; - if (!ptr) - return {}; - - auto next = link; - while (!next.is_terminal()) - { - // get element offset (fault) - const auto offset = ptr.offset(body::link_to_position(next)); - if (is_null(offset)) - return {}; - - // element key matches (found) - if (keys::compare(unsafe_array_cast( - std::next(offset, Link::size)), key)) - return next; - - // set next element link (loop) - next = unsafe_array_cast(offset); - } - - return next; -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::read(const memory& ptr, const Link& link, Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr || link.is_terminal()) - return false; - - const auto start = body::link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - // Stream starts at record and the index is skipped for reader convenience. - iostream stream{ offset, size - position }; - reader source{ stream }; - source.skip_bytes(index_size); - - if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(RowSize * element.count());) } - return element.from_data(source); -} - -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::write(const memory& ptr, const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - Link unused{}; - return write(unused, ptr, link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::write(Link& previous, const memory& ptr, const Link& link, - const Key& key, const Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr || link.is_terminal()) - return false; - - const auto start = body::link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - // iostream.flush is a nop (direct copy). - iostream stream{ offset, size - position }; - finalizer sink{ stream }; - sink.skip_bytes(Link::size); - keys::write(sink, key); - - // Commit element to body. - if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) } - auto& next = unsafe_array_cast(offset); - if (!element.to_data(sink)) - return false; - - // Commit element to search (terminal is a valid bucket index). - bool search{}; - if (!head_.push(search, link, next, key)) - return false; - - // If collision set previous stack head for conflict resolution search. - previous = search ? Link{ next } : Link{}; - return true; -} - -} // namespace database -} // namespace libbitcoin - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAP_IPP +#define LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAP_IPP + +#include +#include +#include + +namespace libbitcoin { +namespace database { + +TEMPLATE +CLASS::hashmap(storage& header, storage& body, const Link& buckets) NOEXCEPT + : head_(header, buckets), body_(body) +{ +} + +// not thread safe +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::create() NOEXCEPT +{ + Link count{}; + return head_.create() && head_.get_body_count(count) && + body_.truncate(count); +} + +TEMPLATE +bool CLASS::close() NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::backup(bool) NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::restore() NOEXCEPT +{ + Link count{}; + return head_.verify() && head_.get_body_count(count) && + body_.truncate(count); +} + +TEMPLATE +bool CLASS::verify() const NOEXCEPT +{ + Link count{}; + return head_.verify() && head_.get_body_count(count) && + (count == body_.count()); +} + +// sizing +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::enabled() const NOEXCEPT +{ + return head_.buckets() > one; +} + +TEMPLATE +size_t CLASS::buckets() const NOEXCEPT +{ + return head_.buckets(); +} + +TEMPLATE +size_t CLASS::head_size() const NOEXCEPT +{ + return head_.size(); +} + +TEMPLATE +size_t CLASS::body_size() const NOEXCEPT +{ + return body_.size(); +} + +TEMPLATE +size_t CLASS::capacity() const NOEXCEPT +{ + return body_.capacity(); +} + +TEMPLATE +Link CLASS::count() const NOEXCEPT +{ + return body_.count(); +} + +TEMPLATE +bool CLASS::expand(const Link& count) NOEXCEPT +{ + return body_.expand(count); +} + +// diagnostic counters +// ---------------------------------------------------------------------------- + +TEMPLATE +size_t CLASS::positive_search_count() const NOEXCEPT +{ + return positive_.load(std::memory_order_relaxed); +} + +TEMPLATE +size_t CLASS::negative_search_count() const NOEXCEPT +{ + return negative_.load(std::memory_order_relaxed); +} + +// query interface +// ---------------------------------------------------------------------------- + +TEMPLATE +code CLASS::get_fault() const NOEXCEPT +{ + return body_.get_fault(); +} + +TEMPLATE +size_t CLASS::get_space() const NOEXCEPT +{ + return body_.get_space(); +} + +TEMPLATE +code CLASS::reload() NOEXCEPT +{ + return body_.reload(); +} + +// query interface +// ---------------------------------------------------------------------------- + +TEMPLATE +inline Link CLASS::top(const Link& link) const NOEXCEPT +{ + if (link >= head_.buckets()) + return {}; + + return head_.top(link); +} + +TEMPLATE +inline bool CLASS::exists(const memory& ptr, const Key& key) const NOEXCEPT +{ + return !first(ptr, key).is_terminal(); +} + +TEMPLATE +inline bool CLASS::exists(const Key& key) const NOEXCEPT +{ + return !first(key).is_terminal(); +} + +TEMPLATE +inline Link CLASS::first(const memory& ptr, const Key& key) const NOEXCEPT +{ + return first(ptr, head_.top(key), key); +} + +TEMPLATE +inline Link CLASS::first(const Key& key) const NOEXCEPT +{ + return first(get_memory(), key); +} + +TEMPLATE +inline typename CLASS::iterator CLASS::it(Key&& key) const NOEXCEPT +{ + const auto top = head_.top(key); + return { get_memory(), top, std::forward(key) }; +} + +TEMPLATE +inline typename CLASS::iterator CLASS::it(const Key& key) const NOEXCEPT +{ + return { get_memory(), head_.top(key), key }; +} + +TEMPLATE +inline Link CLASS::allocate(const Link& size) NOEXCEPT +{ + return body_.allocate(size); +} + +TEMPLATE +inline memory CLASS::get_memory() const NOEXCEPT +{ + return body_.get(); +} + +TEMPLATE +Key CLASS::get_key(const Link& link) NOEXCEPT +{ + using namespace system; + const auto ptr = body_.get(link); + if (!ptr || is_lesser(ptr.size(), index_size)) + return {}; + + return unsafe_array_cast(std::next(ptr.begin(), + Link::size)); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::find(const Key& key, Element& element) const NOEXCEPT +{ + return !find_link(key, element).is_terminal(); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline Link CLASS::find_link(const Key& key, Element& element) const NOEXCEPT +{ + // This override avoids duplicated memory construct in get(first()). + const auto ptr = get_memory(); + const auto link = first(ptr, head_.top(key), key); + if (link.is_terminal()) + return {}; + + return read(ptr, link, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT +{ + // This override is the normal form. + return read(get_memory(), link, element); +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const memory& ptr, const Link& link, + Element& element) NOEXCEPT +{ + return read(ptr, link, element); +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const iterator& it, Element& element) NOEXCEPT +{ + // This override avoids deadlock when holding iterator to the same table. + return read(it.ptr(), *it, element); +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const iterator& it, const Link& link, + Element& element) NOEXCEPT +{ + // This override avoids deadlock when holding iterator to the same table. + return read(it.ptr(), link, element); +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::set(const memory& ptr, const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr) + return false; + + const auto start = body::link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + // Stream starts at record and the index is skipped for reader convenience. + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + // Set element search key. + unsafe_array_cast(std::next(offset, + Link::size)) = key; + + iostream stream{ offset, size - position }; + finalizer sink{ stream }; + sink.skip_bytes(index_size); + + // Due to accounting, record hashmaps are limited to set(1). + if constexpr (!is_slab) { BC_ASSERT(is_one(element.count())); } + if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize);) } + return element.to_data(sink); +} + +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::set(const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + return set(get_memory(), link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline Link CLASS::set_link(const Key& key, const Element& element) NOEXCEPT +{ + Link link{}; + if (!set_link(link, key, element)) + return {}; + + return link; +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::set_link(Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + link = allocate(element.count()); + return set(link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline Link CLASS::put_link(const Key& key, const Element& element) NOEXCEPT +{ + Link link{}; + if (!put_link(link, key, element)) + return {}; + + return link; +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put_link(Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + link = allocate(element.count()); + return put(link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put(const Key& key, const Element& element) NOEXCEPT +{ + return !put_link(key, element).is_terminal(); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put(const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + // This override is the normal form. + return write(get_memory(), link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put(const memory& ptr, const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + return write(ptr, link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put(bool& duplicate, const memory& ptr, + const Link& link, const Key& key, const Element& element) NOEXCEPT +{ + Link previous{}; + if (!write(previous, ptr, link, key, element)) + return false; + + if (previous.is_terminal()) + { + duplicate = false; + ////negative_.fetch_add(one, std::memory_order_relaxed); + } + else + { + // Search the previous conflicts to determine if actual duplicate. + duplicate = !first(ptr, previous, key).is_terminal(); + ////positive_.fetch_add(one, std::memory_order_relaxed); + } + + return true; +} + +TEMPLATE +inline Link CLASS::commit_link(const Link& link, const Key& key) NOEXCEPT +{ + if (!commit(link, key)) + return {}; + + return link; +} + +TEMPLATE +inline bool CLASS::commit(const Link& link, const Key& key) NOEXCEPT +{ + return commit(get_memory(), link, key); +} + +TEMPLATE +bool CLASS::commit(const memory& ptr, const Link& link, + const Key& key) NOEXCEPT +{ + using namespace system; + if (!ptr) + return false; + + // get element offset (fault) + const auto offset = ptr.offset(body::link_to_position(link)); + if (is_null(offset)) + return false; + + // Commit element to search index (terminal is a valid bucket index). + auto& next = unsafe_array_cast(offset); + if (!head_.push(link, next, key)) + return false; + + // set/commit is limited to record tables, one row at a time. + body_.complete(link, one); + return true; +} + +// protected +// ---------------------------------------------------------------------------- + +// static +TEMPLATE +Link CLASS::first(const memory& ptr, const Link& link, const Key& key) NOEXCEPT +{ + using namespace system; + if (!ptr) + return {}; + + auto next = link; + while (!next.is_terminal()) + { + // get element offset (fault) + const auto offset = ptr.offset(body::link_to_position(next)); + if (is_null(offset)) + return {}; + + // element key matches (found) + if (keys::compare(unsafe_array_cast( + std::next(offset, Link::size)), key)) + return next; + + // set next element link (loop) + next = unsafe_array_cast(offset); + } + + return next; +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::read(const memory& ptr, const Link& link, Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr || link.is_terminal()) + return false; + + const auto start = body::link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + // Stream starts at record and the index is skipped for reader convenience. + iostream stream{ offset, size - position }; + reader source{ stream }; + source.skip_bytes(index_size); + + if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(RowSize * element.count());) } + return element.from_data(source); +} + +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::write(const memory& ptr, const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + Link unused{}; + return write(unused, ptr, link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::write(Link& previous, const memory& ptr, const Link& link, + const Key& key, const Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr || link.is_terminal()) + return false; + + const auto start = body::link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + // iostream.flush is a nop (direct copy). + iostream stream{ offset, size - position }; + finalizer sink{ stream }; + sink.skip_bytes(Link::size); + keys::write(sink, key); + + // Commit element to body. + if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) } + auto& next = unsafe_array_cast(offset); + if (!element.to_data(sink)) + return false; + + // Commit element to search (terminal is a valid bucket index). + bool search{}; + if (!head_.push(search, link, next, key)) + return false; + + // If collision set previous stack head for conflict resolution search. + previous = search ? Link{ next } : Link{}; + + // Report element write completion (count matches its allocation). + body_.complete(link, element.count()); + return true; +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/primitives/hashmaps.ipp b/include/bitcoin/database/impl/primitives/hashmaps.ipp index 48567e855..a2ae94263 100644 --- a/include/bitcoin/database/impl/primitives/hashmaps.ipp +++ b/include/bitcoin/database/impl/primitives/hashmaps.ipp @@ -1,631 +1,645 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAPS_IPP -#define LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAPS_IPP - -#include - -namespace libbitcoin { -namespace database { - -TEMPLATE -CLASS::hashmaps(storage& header, storage& body, const Link& buckets) NOEXCEPT - : head_(header, buckets), body_(body) -{ -} - -// not thread safe -// ---------------------------------------------------------------------------- - -TEMPLATE -bool CLASS::create() NOEXCEPT -{ - Link count{}; - return head_.create() && head_.get_body_count(count) && - body_.truncate(count); -} - -TEMPLATE -bool CLASS::close() NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::backup(bool) NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::restore() NOEXCEPT -{ - Link count{}; - return head_.verify() && head_.get_body_count(count) && - body_.truncate(count); -} - -TEMPLATE -bool CLASS::verify() const NOEXCEPT -{ - Link count{}; - return head_.verify() && head_.get_body_count(count) && - (count == body_.count()); -} - -// sizing -// ---------------------------------------------------------------------------- - -TEMPLATE -bool CLASS::enabled() const NOEXCEPT -{ - return head_.buckets() > one; -} - -TEMPLATE -size_t CLASS::buckets() const NOEXCEPT -{ - return head_.buckets(); -} - -TEMPLATE -size_t CLASS::head_size() const NOEXCEPT -{ - return head_.size(); -} - -TEMPLATE -size_t CLASS::body_size() const NOEXCEPT -{ - return body_.size(); -} - -TEMPLATE -size_t CLASS::capacity() const NOEXCEPT -{ - return body_.capacity(); -} - -TEMPLATE -Link CLASS::count() const NOEXCEPT -{ - return body_.count(); -} - -// diagnostic counters -// ---------------------------------------------------------------------------- - -TEMPLATE -size_t CLASS::positive_search_count() const NOEXCEPT -{ - return positive_.load(std::memory_order_relaxed); -} - -TEMPLATE -size_t CLASS::negative_search_count() const NOEXCEPT -{ - return negative_.load(std::memory_order_relaxed); -} - -// error condition -// ---------------------------------------------------------------------------- - -TEMPLATE -code CLASS::get_fault() const NOEXCEPT -{ - return body_.get_fault(); -} - -TEMPLATE -size_t CLASS::get_space() const NOEXCEPT -{ - return body_.get_space(); -} - -TEMPLATE -code CLASS::reload() NOEXCEPT -{ - return body_.reload(); -} - -// query interface -// ---------------------------------------------------------------------------- - -TEMPLATE -inline Link CLASS::top(const Link& link) const NOEXCEPT -{ - if (link >= head_.buckets()) - return {}; - - return head_.top(link); -} - -TEMPLATE -inline bool CLASS::exists(const memory& ptr, const Key& key) const NOEXCEPT -{ - return !first(ptr, key).is_terminal(); -} - -TEMPLATE -inline bool CLASS::exists(const Key& key) const NOEXCEPT -{ - return !first(key).is_terminal(); -} - -TEMPLATE -inline Link CLASS::first(const memory& ptr, const Key& key) const NOEXCEPT -{ - return first(ptr, head_.top(key), key); -} - -TEMPLATE -inline Link CLASS::first(const Key& key) const NOEXCEPT -{ - return first(get_memory(), key); -} - -TEMPLATE -inline typename CLASS::iterator CLASS::it(Key&& key) const NOEXCEPT -{ - const auto top = head_.top(key); - return { get_memory(), top, std::forward(key) }; -} - -TEMPLATE -inline typename CLASS::iterator CLASS::it(const Key& key) const NOEXCEPT -{ - return { get_memory(), head_.top(key), key }; -} - -TEMPLATE -inline Link CLASS::allocate(const Link& size) NOEXCEPT -{ - return body_.allocate(size); -} - -TEMPLATE -template -inline memory CLASS::get_memory() const NOEXCEPT -{ - return body_.template get(); -} - -TEMPLATE -Key CLASS::get_key(const Link& link) NOEXCEPT -{ - using namespace system; - const auto ptr = body_.get(link); - if (!ptr || is_lesser(ptr.size(), index_size)) - return {}; - - return unsafe_array_cast(std::next(ptr.begin(), - Link::size)); -} - -// spine (column zero) -// ---------------------------------------------------------------------------- - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::find(const Key& key, Element& element) const NOEXCEPT -{ - return !find_link(key, element).is_terminal(); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline Link CLASS::find_link(const Key& key, Element& element) const NOEXCEPT -{ - // This override avoids duplicated memory construct in get(first()). - const auto ptr = get_memory(); - const auto link = first(ptr, head_.top(key), key); - if (link.is_terminal()) - return {}; - - return read(ptr, link, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT -{ - // This override is the normal form. - return read(get_memory(), link, element); -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const memory& ptr, const Link& link, - Element& element) NOEXCEPT -{ - return read(ptr, link, element); -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const iterator& it, Element& element) NOEXCEPT -{ - // This override avoids deadlock when holding iterator to the same table. - return read(it.ptr(), *it, element); -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::get(const iterator& it, const Link& link, - Element& element) NOEXCEPT -{ - // This override avoids deadlock when holding iterator to the same table. - return read(it.ptr(), link, element); -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::set(const memory& ptr, const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr) - return false; - - const auto start = body::link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - // Stream starts at record and the index is skipped for reader convenience. - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - // Set element search key. - unsafe_array_cast(std::next(offset, - Link::size)) = key; - - iostream stream{ offset, size - position }; - finalizer sink{ stream }; - sink.skip_bytes(index_size); - - if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) } - return element.to_data(sink); -} - -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::set(const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - return set(get_memory(), link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline Link CLASS::set_link(const Key& key, const Element& element) NOEXCEPT -{ - Link link{}; - if (!set_link(link, key, element)) - return {}; - - return link; -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::set_link(Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - link = allocate(element.count()); - return set(link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline Link CLASS::put_link(const Key& key, const Element& element) NOEXCEPT -{ - Link link{}; - if (!put_link(link, key, element)) - return {}; - - return link; -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put_link(Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - link = allocate(element.count()); - return put(link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put(const Key& key, const Element& element) NOEXCEPT -{ - return !put_link(key, element).is_terminal(); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put(const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - // This override is the normal form. - return write(get_memory(), link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put(const memory& ptr, const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - return write(ptr, link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -inline bool CLASS::put(bool& duplicate, const memory& ptr, - const Link& link, const Key& key, const Element& element) NOEXCEPT -{ - Link previous{}; - if (!write(previous, ptr, link, key, element)) - return false; - - if (previous.is_terminal()) - { - duplicate = false; - ////negative_.fetch_add(one, std::memory_order_relaxed); - } - else - { - // Search the previous conflicts to determine if actual duplicate. - duplicate = !first(ptr, previous, key).is_terminal(); - ////positive_.fetch_add(one, std::memory_order_relaxed); - } - - return true; -} - -TEMPLATE -inline Link CLASS::commit_link(const Link& link, const Key& key) NOEXCEPT -{ - if (!commit(link, key)) - return {}; - - return link; -} - -TEMPLATE -inline bool CLASS::commit(const Link& link, const Key& key) NOEXCEPT -{ - return commit(get_memory(), link, key); -} - -TEMPLATE -bool CLASS::commit(const memory& ptr, const Link& link, - const Key& key) NOEXCEPT -{ - using namespace system; - if (!ptr) - return false; - - // get element offset (fault) - const auto offset = ptr.offset(body::link_to_position(link)); - if (is_null(offset)) - return false; - - // Commit element to search index (terminal is a valid bucket index). - auto& next = unsafe_array_cast(offset); - return head_.push(link, next, key); -} - -// satellites (nonzero Column) -// ---------------------------------------------------------------------------- - -// static -TEMPLATE -template -bool CLASS::get(const memory& ptr, const Link& link, Element& element) NOEXCEPT -{ - static_assert(is_nonzero(Column), "column zero is the keyed spine"); - static_assert(Element::size == width); - if (!ptr || link.is_terminal()) - return false; - - using namespace system; - const auto start = body::template link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - iostream stream{ offset, size - position }; - reader source{ stream }; - - BC_DEBUG_ONLY(source.set_limit(width * element.count());) - return element.from_data(source); -} - -TEMPLATE -template -bool CLASS::get(const Link& link, Element& element) const NOEXCEPT -{ - return get(get_memory(), link, element); -} - -TEMPLATE -template -bool CLASS::put(const Link& link, const Element& element) NOEXCEPT -{ - const auto ptr = body_.template get_raw1(link); - return put(ptr, element); -} - -// protected (unguarded memory access) -TEMPLATE -template -bool CLASS::put(memory::iterator it, const Element& element) NOEXCEPT -{ - static_assert(is_nonzero(Column), "column zero is the keyed spine"); - static_assert(Element::size == width); - if (is_null(it)) - return false; - - using namespace system; - const auto bytes = width * element.count(); - iostream stream{ it, possible_narrow_and_sign_cast(bytes) }; - flipper sink{ stream }; - - BC_DEBUG_ONLY(sink.set_limit(width * element.count());) - return element.to_data(sink); -} - -// protected -// ---------------------------------------------------------------------------- - -// static -TEMPLATE -Link CLASS::first(const memory& ptr, const Link& link, const Key& key) NOEXCEPT -{ - using namespace system; - if (!ptr) - return {}; - - auto next = link; - while (!next.is_terminal()) - { - // get element offset (fault) - const auto offset = ptr.offset(body::link_to_position(next)); - if (is_null(offset)) - return {}; - - // element key matches (found) - if (keys::compare(unsafe_array_cast( - std::next(offset, Link::size)), key)) - return next; - - // set next element link (loop) - next = unsafe_array_cast(offset); - } - - return next; -} - -// static -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::read(const memory& ptr, const Link& link, Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr || link.is_terminal()) - return false; - - const auto start = body::link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - // Stream starts at record and the index is skipped for reader convenience. - iostream stream{ offset, size - position }; - reader source{ stream }; - source.skip_bytes(index_size); - - if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(RowSize * element.count());) } - return element.from_data(source); -} - -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::write(const memory& ptr, const Link& link, const Key& key, - const Element& element) NOEXCEPT -{ - Link unused{}; - return write(unused, ptr, link, key, element); -} - -TEMPLATE -ELEMENT_CONSTRAINT -bool CLASS::write(Link& previous, const memory& ptr, const Link& link, - const Key& key, const Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr || link.is_terminal()) - return false; - - const auto start = body::link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - // iostream.flush is a nop (direct copy). - iostream stream{ offset, size - position }; - finalizer sink{ stream }; - sink.skip_bytes(Link::size); - keys::write(sink, key); - - // Commit element to body. - if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) } - auto& next = unsafe_array_cast(offset); - if (!element.to_data(sink)) - return false; - - // Commit element to search (terminal is a valid bucket index). - bool search{}; - if (!head_.push(search, link, next, key)) - return false; - - // If collision set previous stack head for conflict resolution search. - previous = search ? Link{ next } : Link{}; - return true; -} - -} // namespace database -} // namespace libbitcoin - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAPS_IPP +#define LIBBITCOIN_DATABASE_PRIMITIVES_HASHMAPS_IPP + +#include + +namespace libbitcoin { +namespace database { + +TEMPLATE +CLASS::hashmaps(storage& header, storage& body, const Link& buckets) NOEXCEPT + : head_(header, buckets), body_(body) +{ +} + +// not thread safe +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::create() NOEXCEPT +{ + Link count{}; + return head_.create() && head_.get_body_count(count) && + body_.truncate(count); +} + +TEMPLATE +bool CLASS::close() NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::backup(bool) NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::restore() NOEXCEPT +{ + Link count{}; + return head_.verify() && head_.get_body_count(count) && + body_.truncate(count); +} + +TEMPLATE +bool CLASS::verify() const NOEXCEPT +{ + Link count{}; + return head_.verify() && head_.get_body_count(count) && + (count == body_.count()); +} + +// sizing +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::enabled() const NOEXCEPT +{ + return head_.buckets() > one; +} + +TEMPLATE +size_t CLASS::buckets() const NOEXCEPT +{ + return head_.buckets(); +} + +TEMPLATE +size_t CLASS::head_size() const NOEXCEPT +{ + return head_.size(); +} + +TEMPLATE +size_t CLASS::body_size() const NOEXCEPT +{ + return body_.size(); +} + +TEMPLATE +size_t CLASS::capacity() const NOEXCEPT +{ + return body_.capacity(); +} + +TEMPLATE +Link CLASS::count() const NOEXCEPT +{ + return body_.count(); +} + +// diagnostic counters +// ---------------------------------------------------------------------------- + +TEMPLATE +size_t CLASS::positive_search_count() const NOEXCEPT +{ + return positive_.load(std::memory_order_relaxed); +} + +TEMPLATE +size_t CLASS::negative_search_count() const NOEXCEPT +{ + return negative_.load(std::memory_order_relaxed); +} + +// error condition +// ---------------------------------------------------------------------------- + +TEMPLATE +code CLASS::get_fault() const NOEXCEPT +{ + return body_.get_fault(); +} + +TEMPLATE +size_t CLASS::get_space() const NOEXCEPT +{ + return body_.get_space(); +} + +TEMPLATE +code CLASS::reload() NOEXCEPT +{ + return body_.reload(); +} + +// query interface +// ---------------------------------------------------------------------------- + +TEMPLATE +inline Link CLASS::top(const Link& link) const NOEXCEPT +{ + if (link >= head_.buckets()) + return {}; + + return head_.top(link); +} + +TEMPLATE +inline bool CLASS::exists(const memory& ptr, const Key& key) const NOEXCEPT +{ + return !first(ptr, key).is_terminal(); +} + +TEMPLATE +inline bool CLASS::exists(const Key& key) const NOEXCEPT +{ + return !first(key).is_terminal(); +} + +TEMPLATE +inline Link CLASS::first(const memory& ptr, const Key& key) const NOEXCEPT +{ + return first(ptr, head_.top(key), key); +} + +TEMPLATE +inline Link CLASS::first(const Key& key) const NOEXCEPT +{ + return first(get_memory(), key); +} + +TEMPLATE +inline typename CLASS::iterator CLASS::it(Key&& key) const NOEXCEPT +{ + const auto top = head_.top(key); + return { get_memory(), top, std::forward(key) }; +} + +TEMPLATE +inline typename CLASS::iterator CLASS::it(const Key& key) const NOEXCEPT +{ + return { get_memory(), head_.top(key), key }; +} + +TEMPLATE +inline Link CLASS::allocate(const Link& size) NOEXCEPT +{ + return body_.allocate(size); +} + +TEMPLATE +template +inline memory CLASS::get_memory() const NOEXCEPT +{ + return body_.template get(); +} + +TEMPLATE +Key CLASS::get_key(const Link& link) NOEXCEPT +{ + using namespace system; + const auto ptr = body_.get(link); + if (!ptr || is_lesser(ptr.size(), index_size)) + return {}; + + return unsafe_array_cast(std::next(ptr.begin(), + Link::size)); +} + +// spine (column zero) +// ---------------------------------------------------------------------------- + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::find(const Key& key, Element& element) const NOEXCEPT +{ + return !find_link(key, element).is_terminal(); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline Link CLASS::find_link(const Key& key, Element& element) const NOEXCEPT +{ + // This override avoids duplicated memory construct in get(first()). + const auto ptr = get_memory(); + const auto link = first(ptr, head_.top(key), key); + if (link.is_terminal()) + return {}; + + return read(ptr, link, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT +{ + // This override is the normal form. + return read(get_memory(), link, element); +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const memory& ptr, const Link& link, + Element& element) NOEXCEPT +{ + return read(ptr, link, element); +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const iterator& it, Element& element) NOEXCEPT +{ + // This override avoids deadlock when holding iterator to the same table. + return read(it.ptr(), *it, element); +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::get(const iterator& it, const Link& link, + Element& element) NOEXCEPT +{ + // This override avoids deadlock when holding iterator to the same table. + return read(it.ptr(), link, element); +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::set(const memory& ptr, const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr) + return false; + + const auto start = body::link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + // Stream starts at record and the index is skipped for reader convenience. + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + // Set element search key. + unsafe_array_cast(std::next(offset, + Link::size)) = key; + + iostream stream{ offset, size - position }; + finalizer sink{ stream }; + sink.skip_bytes(index_size); + + // Due to accounting, record hashmaps are limited to set(1). + if constexpr (!is_slab) { BC_ASSERT(is_one(element.count())); } + if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize);) } + return element.to_data(sink); +} + +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::set(const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + return set(get_memory(), link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline Link CLASS::set_link(const Key& key, const Element& element) NOEXCEPT +{ + Link link{}; + if (!set_link(link, key, element)) + return {}; + + return link; +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::set_link(Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + link = allocate(element.count()); + return set(link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline Link CLASS::put_link(const Key& key, const Element& element) NOEXCEPT +{ + Link link{}; + if (!put_link(link, key, element)) + return {}; + + return link; +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put_link(Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + link = allocate(element.count()); + return put(link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put(const Key& key, const Element& element) NOEXCEPT +{ + return !put_link(key, element).is_terminal(); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put(const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + // This override is the normal form. + return write(get_memory(), link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put(const memory& ptr, const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + return write(ptr, link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +inline bool CLASS::put(bool& duplicate, const memory& ptr, + const Link& link, const Key& key, const Element& element) NOEXCEPT +{ + Link previous{}; + if (!write(previous, ptr, link, key, element)) + return false; + + if (previous.is_terminal()) + { + duplicate = false; + ////negative_.fetch_add(one, std::memory_order_relaxed); + } + else + { + // Search the previous conflicts to determine if actual duplicate. + duplicate = !first(ptr, previous, key).is_terminal(); + ////positive_.fetch_add(one, std::memory_order_relaxed); + } + + return true; +} + +TEMPLATE +inline Link CLASS::commit_link(const Link& link, const Key& key) NOEXCEPT +{ + if (!commit(link, key)) + return {}; + + return link; +} + +TEMPLATE +inline bool CLASS::commit(const Link& link, const Key& key) NOEXCEPT +{ + return commit(get_memory(), link, key); +} + +TEMPLATE +bool CLASS::commit(const memory& ptr, const Link& link, + const Key& key) NOEXCEPT +{ + using namespace system; + if (!ptr) + return false; + + // get element offset (fault) + const auto offset = ptr.offset(body::link_to_position(link)); + if (is_null(offset)) + return false; + + // Commit element to search index (terminal is a valid bucket index). + auto& next = unsafe_array_cast(offset); + if (!head_.push(link, next, key)) + return false; + + // set/commit is limited to record tables, one row at a time. + body_.complete(link, one); + return true; +} + +// satellites (nonzero Column) +// ---------------------------------------------------------------------------- + +// static +TEMPLATE +template +bool CLASS::get(const memory& ptr, const Link& link, Element& element) NOEXCEPT +{ + static_assert(is_nonzero(Column), "column zero is the keyed spine"); + static_assert(Element::size == width); + if (!ptr || link.is_terminal()) + return false; + + using namespace system; + const auto start = body::template link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + iostream stream{ offset, size - position }; + reader source{ stream }; + + BC_DEBUG_ONLY(source.set_limit(width * element.count());) + return element.from_data(source); +} + +TEMPLATE +template +bool CLASS::get(const Link& link, Element& element) const NOEXCEPT +{ + return get(get_memory(), link, element); +} + +TEMPLATE +template +bool CLASS::put(const Link& link, const Element& element) NOEXCEPT +{ + const auto ptr = body_.template get_raw1(link); + if (!put(ptr, element)) + return false; + + body_.complete(link, element.count()); + return true; +} + +// protected (unguarded memory access) +TEMPLATE +template +bool CLASS::put(memory::iterator it, const Element& element) NOEXCEPT +{ + static_assert(is_nonzero(Column), "column zero is the keyed spine"); + static_assert(Element::size == width); + if (is_null(it)) + return false; + + using namespace system; + const auto bytes = width * element.count(); + iostream stream{ it, possible_narrow_and_sign_cast(bytes) }; + flipper sink{ stream }; + + BC_DEBUG_ONLY(sink.set_limit(width * element.count());) + return element.to_data(sink); +} + +// protected +// ---------------------------------------------------------------------------- + +// static +TEMPLATE +Link CLASS::first(const memory& ptr, const Link& link, const Key& key) NOEXCEPT +{ + using namespace system; + if (!ptr) + return {}; + + auto next = link; + while (!next.is_terminal()) + { + // get element offset (fault) + const auto offset = ptr.offset(body::link_to_position(next)); + if (is_null(offset)) + return {}; + + // element key matches (found) + if (keys::compare(unsafe_array_cast( + std::next(offset, Link::size)), key)) + return next; + + // set next element link (loop) + next = unsafe_array_cast(offset); + } + + return next; +} + +// static +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::read(const memory& ptr, const Link& link, Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr || link.is_terminal()) + return false; + + const auto start = body::link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + // Stream starts at record and the index is skipped for reader convenience. + iostream stream{ offset, size - position }; + reader source{ stream }; + source.skip_bytes(index_size); + + if constexpr (!is_slab) { BC_DEBUG_ONLY(source.set_limit(RowSize * element.count());) } + return element.from_data(source); +} + +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::write(const memory& ptr, const Link& link, const Key& key, + const Element& element) NOEXCEPT +{ + Link unused{}; + return write(unused, ptr, link, key, element); +} + +TEMPLATE +ELEMENT_CONSTRAINT +bool CLASS::write(Link& previous, const memory& ptr, const Link& link, + const Key& key, const Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr || link.is_terminal()) + return false; + + const auto start = body::link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + // iostream.flush is a nop (direct copy). + iostream stream{ offset, size - position }; + finalizer sink{ stream }; + sink.skip_bytes(Link::size); + keys::write(sink, key); + + // Commit element to body. + if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) } + auto& next = unsafe_array_cast(offset); + if (!element.to_data(sink)) + return false; + + // Commit element to search (terminal is a valid bucket index). + bool search{}; + if (!head_.push(search, link, next, key)) + return false; + + // If collision set previous stack head for conflict resolution search. + previous = search ? Link{ next } : Link{}; + + // Report element write completion (count matches its allocation). + body_.complete(link, element.count()); + return true; +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/primitives/nomap.ipp b/include/bitcoin/database/impl/primitives/nomap.ipp index 31a5ba17a..d2a53d16a 100644 --- a/include/bitcoin/database/impl/primitives/nomap.ipp +++ b/include/bitcoin/database/impl/primitives/nomap.ipp @@ -1,311 +1,324 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_NOMAP_IPP -#define LIBBITCOIN_DATABASE_PRIMITIVES_NOMAP_IPP - -#include - -namespace libbitcoin { -namespace database { - -TEMPLATE -CLASS::nomap(storage& header, storage& body) NOEXCEPT - : head_(header, 0), body_(body) -{ -} - -// not thread safe -// ---------------------------------------------------------------------------- - -TEMPLATE -bool CLASS::create() NOEXCEPT -{ - Link count{}; - return head_.create() && - head_.get_body_count(count) && body_.truncate(count); -} - -TEMPLATE -bool CLASS::close() NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::backup(bool) NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::restore() NOEXCEPT -{ - Link count{}; - return head_.verify() && - head_.get_body_count(count) && body_.truncate(count); -} - -TEMPLATE -bool CLASS::verify() const NOEXCEPT -{ - Link count{}; - return head_.verify() && - head_.get_body_count(count) && count == body_.count(); -} - -// sizing -// ---------------------------------------------------------------------------- - -TEMPLATE -size_t CLASS::buckets() const NOEXCEPT -{ - return head_.buckets(); -} - -TEMPLATE -size_t CLASS::head_size() const NOEXCEPT -{ - return head_.size(); -} - -TEMPLATE -size_t CLASS::body_size() const NOEXCEPT -{ - return body_.size(); -} - -TEMPLATE -size_t CLASS::capacity() const NOEXCEPT -{ - return body_.capacity(); -} - -TEMPLATE -Link CLASS::count() const NOEXCEPT -{ - return body_.count(); -} - -TEMPLATE -bool CLASS::truncate(const Link& count) NOEXCEPT -{ - return body_.truncate(count); -} - -TEMPLATE -bool CLASS::expand(const Link& count) NOEXCEPT -{ - return body_.expand(count); -} - -TEMPLATE -bool CLASS::drop() NOEXCEPT -{ - return body_.truncate(0) && backup(); -} - -TEMPLATE -bool CLASS::reserve(const Link& size) NOEXCEPT -{ - // Not writer-writer thread safe (two writers may share reserve). - return body_.reserve(size); -} - -TEMPLATE -Link CLASS::allocate(const Link& size) NOEXCEPT -{ - return body_.allocate(size); -} - -TEMPLATE -memory CLASS::get_memory() const NOEXCEPT -{ - return body_.get(); -} - -// error condition -// ---------------------------------------------------------------------------- - -TEMPLATE -code CLASS::get_fault() const NOEXCEPT -{ - return body_.get_fault(); -} - -TEMPLATE -size_t CLASS::get_space() const NOEXCEPT -{ - return body_.get_space(); -} - -TEMPLATE -code CLASS::reload() NOEXCEPT -{ - return body_.reload(); -} - -// query interface -// ---------------------------------------------------------------------------- - -// static -TEMPLATE -template > -bool CLASS::get(const memory& ptr, const Link& link, Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr || link.is_terminal()) - return false; - - const auto start = body::link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - iostream stream{ offset, size - position }; - reader source{ stream }; - - if constexpr (!is_slab) - { - BC_DEBUG_ONLY(source.set_limit(Size * element.count());) - } - - return element.from_data(source); -} - -TEMPLATE -template > -inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT -{ - return get(get_memory(), link, element); -} - -TEMPLATE -template > -inline bool CLASS::put(const Element& element) NOEXCEPT -{ - Link link{}; - return put_link(link, element); -} - -TEMPLATE -template > -bool CLASS::put(const Link& link, const Element& element) NOEXCEPT -{ - using namespace system; - const auto ptr = body_.get(link); - return put(ptr, element); -} - -TEMPLATE -template > -bool CLASS::put(const memory& ptr, const Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr) - return false; - - iostream stream{ ptr }; - flipper sink{ stream }; - - if constexpr (!is_slab) - { - BC_DEBUG_ONLY(sink.set_limit(Size * element.count());) - } - - return element.to_data(sink); -} - -TEMPLATE -template > -bool CLASS::put(const memory& ptr, const Link& link, - const Element& element) NOEXCEPT -{ - using namespace system; - if (!ptr || link.is_terminal()) - return false; - - const auto start = body::link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - iostream stream{ offset, size - position }; - flipper sink{ stream }; - - if constexpr (!is_slab) - { - BC_DEBUG_ONLY(sink.set_limit(Size * element.count());) - } - - return element.to_data(sink); -} - -TEMPLATE -template > -inline bool CLASS::put_link(Link& link, const Element& element) NOEXCEPT -{ - const auto count = element.count(); - link = body_.allocate(count); - return put(link, element); -} - -TEMPLATE -template > -inline Link CLASS::put_link(const Element& element) NOEXCEPT -{ - Link link{}; - return put_link(link, element) ? link : Link{}; -} - -// NOT THREAD SAFE (used only for height index with writer ordering). -TEMPLATE -template > -inline bool CLASS::commit(const Element& element) NOEXCEPT -{ - // Zero allocation provides link of next (presumably reserved) element. - const auto link = body_.allocate(0); - - // Write element into reserved but unallocated space. - if (!put(body_.get_capacity(link), element)) - return false; - - // Allocate reserved and written element (exposes logically). - return !body_.allocate(element.count()).is_terminal(); -} - -} // namespace database -} // namespace libbitcoin - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_NOMAP_IPP +#define LIBBITCOIN_DATABASE_PRIMITIVES_NOMAP_IPP + +#include + +namespace libbitcoin { +namespace database { + +TEMPLATE +CLASS::nomap(storage& header, storage& body) NOEXCEPT + : head_(header, 0), body_(body) +{ +} + +// not thread safe +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::create() NOEXCEPT +{ + Link count{}; + return head_.create() && + head_.get_body_count(count) && body_.truncate(count); +} + +TEMPLATE +bool CLASS::close() NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::backup(bool) NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::restore() NOEXCEPT +{ + Link count{}; + return head_.verify() && + head_.get_body_count(count) && body_.truncate(count); +} + +TEMPLATE +bool CLASS::verify() const NOEXCEPT +{ + Link count{}; + return head_.verify() && + head_.get_body_count(count) && count == body_.count(); +} + +// sizing +// ---------------------------------------------------------------------------- + +TEMPLATE +size_t CLASS::buckets() const NOEXCEPT +{ + return head_.buckets(); +} + +TEMPLATE +size_t CLASS::head_size() const NOEXCEPT +{ + return head_.size(); +} + +TEMPLATE +size_t CLASS::body_size() const NOEXCEPT +{ + return body_.size(); +} + +TEMPLATE +size_t CLASS::capacity() const NOEXCEPT +{ + return body_.capacity(); +} + +TEMPLATE +Link CLASS::count() const NOEXCEPT +{ + return body_.count(); +} + +TEMPLATE +bool CLASS::truncate(const Link& count) NOEXCEPT +{ + return body_.truncate(count); +} + +TEMPLATE +bool CLASS::expand(const Link& count) NOEXCEPT +{ + return body_.expand(count); +} + +TEMPLATE +bool CLASS::drop() NOEXCEPT +{ + return body_.truncate(0) && backup(); +} + +TEMPLATE +bool CLASS::reserve(const Link& size) NOEXCEPT +{ + // Not writer-writer thread safe (two writers may share reserve). + return body_.reserve(size); +} + +TEMPLATE +Link CLASS::allocate(const Link& size) NOEXCEPT +{ + return body_.allocate(size); +} + +TEMPLATE +memory CLASS::get_memory() const NOEXCEPT +{ + return body_.get(); +} + +// error condition +// ---------------------------------------------------------------------------- + +TEMPLATE +code CLASS::get_fault() const NOEXCEPT +{ + return body_.get_fault(); +} + +TEMPLATE +size_t CLASS::get_space() const NOEXCEPT +{ + return body_.get_space(); +} + +TEMPLATE +code CLASS::reload() NOEXCEPT +{ + return body_.reload(); +} + +// query interface +// ---------------------------------------------------------------------------- + +// static +TEMPLATE +template > +bool CLASS::get(const memory& ptr, const Link& link, Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr || link.is_terminal()) + return false; + + const auto start = body::link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + iostream stream{ offset, size - position }; + reader source{ stream }; + + if constexpr (!is_slab) + { + BC_DEBUG_ONLY(source.set_limit(Size * element.count());) + } + + return element.from_data(source); +} + +TEMPLATE +template > +inline bool CLASS::get(const Link& link, Element& element) const NOEXCEPT +{ + return get(get_memory(), link, element); +} + +TEMPLATE +template > +inline bool CLASS::put(const Element& element) NOEXCEPT +{ + Link link{}; + return put_link(link, element); +} + +TEMPLATE +template > +bool CLASS::put(const Link& link, const Element& element) NOEXCEPT +{ + using namespace system; + const auto ptr = body_.get(link); + if (!put(ptr, element)) + return false; + + body_.complete(link, element.count()); + return true; +} + +TEMPLATE +template > +bool CLASS::put(const memory& ptr, const Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr) + return false; + + iostream stream{ ptr }; + flipper sink{ stream }; + + if constexpr (!is_slab) + { + BC_DEBUG_ONLY(sink.set_limit(Size * element.count());) + } + + return element.to_data(sink); +} + +TEMPLATE +template > +bool CLASS::put(const memory& ptr, const Link& link, + const Element& element) NOEXCEPT +{ + using namespace system; + if (!ptr || link.is_terminal()) + return false; + + const auto start = body::link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + iostream stream{ offset, size - position }; + flipper sink{ stream }; + + if constexpr (!is_slab) + { + BC_DEBUG_ONLY(sink.set_limit(Size * element.count());) + } + + if (!element.to_data(sink)) + return false; + + body_.complete(link, element.count()); + return true; +} + +TEMPLATE +template > +inline bool CLASS::put_link(Link& link, const Element& element) NOEXCEPT +{ + const auto count = element.count(); + link = body_.allocate(count); + return put(link, element); +} + +TEMPLATE +template > +inline Link CLASS::put_link(const Element& element) NOEXCEPT +{ + Link link{}; + return put_link(link, element) ? link : Link{}; +} + +// NOT THREAD SAFE (used only for height index with writer ordering). +TEMPLATE +template > +inline bool CLASS::commit(const Element& element) NOEXCEPT +{ + // Zero allocation provides link of next (presumably reserved) element. + const auto link = body_.allocate(0); + + // Write element into reserved but unallocated space. + if (!put(body_.get_capacity(link), element)) + return false; + + // Allocate reserved and written element (exposes logically). + if (body_.allocate(element.count()).is_terminal()) + return false; + + // Publication of the pre-written element is its completion. + body_.complete(link, element.count()); + return true; +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/impl/primitives/nomaps.ipp b/include/bitcoin/database/impl/primitives/nomaps.ipp index c896ff9c3..ff89f5a93 100644 --- a/include/bitcoin/database/impl/primitives/nomaps.ipp +++ b/include/bitcoin/database/impl/primitives/nomaps.ipp @@ -1,212 +1,216 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_NOMAPS_IPP -#define LIBBITCOIN_DATABASE_PRIMITIVES_NOMAPS_IPP - -#include -#include - -namespace libbitcoin { -namespace database { - -TEMPLATE -CLASS::nomaps(storage& header, storage& body) NOEXCEPT - : head_(header, 0), - body_(body) -{ -} - -// not thread safe -// ---------------------------------------------------------------------------- - -TEMPLATE -bool CLASS::create() NOEXCEPT -{ - Link count{}; - return head_.create() && - head_.get_body_count(count) && body_.truncate(count); -} - -TEMPLATE -bool CLASS::close() NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::backup(bool) NOEXCEPT -{ - return head_.set_body_count(body_.count()); -} - -TEMPLATE -bool CLASS::restore() NOEXCEPT -{ - Link count{}; - return head_.verify() && - head_.get_body_count(count) && body_.truncate(count); -} - -TEMPLATE -bool CLASS::verify() const NOEXCEPT -{ - Link count{}; - return head_.verify() && - head_.get_body_count(count) && count == body_.count(); -} - -// sizing -// ---------------------------------------------------------------------------- - -TEMPLATE -size_t CLASS::body_size() const NOEXCEPT -{ - return body_.size(); -} - -TEMPLATE -Link CLASS::count() const NOEXCEPT -{ - return body_.count(); -} - -TEMPLATE -Link CLASS::allocate(const Link& count) NOEXCEPT -{ - return body_.allocate(count); -} - -TEMPLATE -bool CLASS::truncate(const Link& count) NOEXCEPT -{ - return body_.truncate(count); -} - -TEMPLATE -bool CLASS::drop() NOEXCEPT -{ - return body_.truncate(0) && backup(); -} - -// Faults. -// ---------------------------------------------------------------------------- - -TEMPLATE -code CLASS::get_fault() const NOEXCEPT -{ - return body_.get_fault(); -} - -TEMPLATE -size_t CLASS::get_space() const NOEXCEPT -{ - return body_.get_space(); -} - -TEMPLATE -code CLASS::reload() NOEXCEPT -{ - return body_.reload(); -} - -// query interface -// ---------------------------------------------------------------------------- - -TEMPLATE -memory CLASS::guard() const NOEXCEPT -{ - return get_memory(); -} - -TEMPLATE -template -memory CLASS::get_memory() const NOEXCEPT -{ - return body_.template get(); -} - -// static -TEMPLATE -template -bool CLASS::get(const memory& ptr, const Link& link, Element& element) NOEXCEPT -{ - static_assert(Element::size == width); - if (!ptr || link.is_terminal()) - return false; - - using namespace system; - const auto start = body::template link_to_position(link); - if (is_limited(start)) - return false; - - const auto size = ptr.size(); - const auto position = possible_narrow_and_sign_cast(start); - if (position >= size) - return false; - - const auto offset = ptr.offset(start); - if (is_null(offset)) - return false; - - iostream stream{ offset, size - position }; - reader source{ stream }; - - BC_DEBUG_ONLY(source.set_limit(width * element.count());) - return element.from_data(source); -} - -// TODO: gets are not optimized for shared remap guard. -TEMPLATE -template -bool CLASS::get(const Link& link, Element& element) const NOEXCEPT -{ - return get(get_memory(), link, element); -} - -// TODO: puts are optimized for shared remap guard (required). -TEMPLATE -template -bool CLASS::put(const Link& link, const Element& element) NOEXCEPT -{ - const auto ptr = body_.template get_raw1(link); - return put(ptr, element); -} - -// protected (unguarded memory access) -TEMPLATE -template -bool CLASS::put(memory::iterator it, const Element& element) NOEXCEPT -{ - static_assert(Element::size == width); - if (is_null(it)) - return false; - - using namespace system; - const auto bytes = width * element.count(); - iostream stream{ it, possible_narrow_and_sign_cast(bytes) }; - flipper sink{ stream }; - - BC_DEBUG_ONLY(sink.set_limit(width * element.count());) - return element.to_data(sink); -} - -} // namespace database -} // namespace libbitcoin - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_NOMAPS_IPP +#define LIBBITCOIN_DATABASE_PRIMITIVES_NOMAPS_IPP + +#include +#include + +namespace libbitcoin { +namespace database { + +TEMPLATE +CLASS::nomaps(storage& header, storage& body) NOEXCEPT + : head_(header, 0), + body_(body) +{ +} + +// not thread safe +// ---------------------------------------------------------------------------- + +TEMPLATE +bool CLASS::create() NOEXCEPT +{ + Link count{}; + return head_.create() && + head_.get_body_count(count) && body_.truncate(count); +} + +TEMPLATE +bool CLASS::close() NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::backup(bool) NOEXCEPT +{ + return head_.set_body_count(body_.count()); +} + +TEMPLATE +bool CLASS::restore() NOEXCEPT +{ + Link count{}; + return head_.verify() && + head_.get_body_count(count) && body_.truncate(count); +} + +TEMPLATE +bool CLASS::verify() const NOEXCEPT +{ + Link count{}; + return head_.verify() && + head_.get_body_count(count) && count == body_.count(); +} + +// sizing +// ---------------------------------------------------------------------------- + +TEMPLATE +size_t CLASS::body_size() const NOEXCEPT +{ + return body_.size(); +} + +TEMPLATE +Link CLASS::count() const NOEXCEPT +{ + return body_.count(); +} + +TEMPLATE +Link CLASS::allocate(const Link& count) NOEXCEPT +{ + return body_.allocate(count); +} + +TEMPLATE +bool CLASS::truncate(const Link& count) NOEXCEPT +{ + return body_.truncate(count); +} + +TEMPLATE +bool CLASS::drop() NOEXCEPT +{ + return body_.truncate(0) && backup(); +} + +// Faults. +// ---------------------------------------------------------------------------- + +TEMPLATE +code CLASS::get_fault() const NOEXCEPT +{ + return body_.get_fault(); +} + +TEMPLATE +size_t CLASS::get_space() const NOEXCEPT +{ + return body_.get_space(); +} + +TEMPLATE +code CLASS::reload() NOEXCEPT +{ + return body_.reload(); +} + +// query interface +// ---------------------------------------------------------------------------- + +TEMPLATE +memory CLASS::guard() const NOEXCEPT +{ + return get_memory(); +} + +TEMPLATE +template +memory CLASS::get_memory() const NOEXCEPT +{ + return body_.template get(); +} + +// static +TEMPLATE +template +bool CLASS::get(const memory& ptr, const Link& link, Element& element) NOEXCEPT +{ + static_assert(Element::size == width); + if (!ptr || link.is_terminal()) + return false; + + using namespace system; + const auto start = body::template link_to_position(link); + if (is_limited(start)) + return false; + + const auto size = ptr.size(); + const auto position = possible_narrow_and_sign_cast(start); + if (position >= size) + return false; + + const auto offset = ptr.offset(start); + if (is_null(offset)) + return false; + + iostream stream{ offset, size - position }; + reader source{ stream }; + + BC_DEBUG_ONLY(source.set_limit(width * element.count());) + return element.from_data(source); +} + +// TODO: gets are not optimized for shared remap guard. +TEMPLATE +template +bool CLASS::get(const Link& link, Element& element) const NOEXCEPT +{ + return get(get_memory(), link, element); +} + +// TODO: puts are optimized for shared remap guard (required). +TEMPLATE +template +bool CLASS::put(const Link& link, const Element& element) NOEXCEPT +{ + const auto ptr = body_.template get_raw1(link); + if (!put(ptr, element)) + return false; + + body_.complete(link, element.count()); + return true; +} + +// protected (unguarded memory access) +TEMPLATE +template +bool CLASS::put(memory::iterator it, const Element& element) NOEXCEPT +{ + static_assert(Element::size == width); + if (is_null(it)) + return false; + + using namespace system; + const auto bytes = width * element.count(); + iostream stream{ it, possible_narrow_and_sign_cast(bytes) }; + flipper sink{ stream }; + + BC_DEBUG_ONLY(sink.set_limit(width * element.count());) + return element.to_data(sink); +} + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/memory/interfaces/storage.hpp b/include/bitcoin/database/memory/interfaces/storage.hpp index ad0cdd1e3..4ff45a869 100644 --- a/include/bitcoin/database/memory/interfaces/storage.hpp +++ b/include/bitcoin/database/memory/interfaces/storage.hpp @@ -1,121 +1,127 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_MEMORY_INTERFACES_STORAGE_HPP -#define LIBBITCOIN_DATABASE_MEMORY_INTERFACES_STORAGE_HPP - -#include -#include -#include - -namespace libbitcoin { -namespace database { - -/// Mapped memory interface. -/// A slab has a row width of 1, so "count" implies "bytes" for slabs below. -class storage -{ -public: - static constexpr auto eof = system::bit_all; - using path = std::filesystem::path; - - /// Get the fault condition. - virtual code get_fault() const NOEXCEPT = 0; - - /// Get the space required to clear the disk full condition. - virtual size_t get_space() const NOEXCEPT = 0; - - /// The filesystem path of the backing storage. - virtual const path& file() const NOEXCEPT = 0; - - /// Create empty file, must not exist. - virtual code create() const NOEXCEPT = 0; - - /// Open file, must be closed. - virtual code open() NOEXCEPT = 0; - - /// Close file, must be unloaded, idempotent. - virtual code close() NOEXCEPT = 0; - - /// Map file to memory, must be open and unloaded. - virtual code load() NOEXCEPT = 0; - - /// Clear disk full condition, fails if fault, must be loaded, idempotent. - virtual code reload() NOEXCEPT = 0; - - /// Flush memory map to disk, suspend writes for call, must be loaded. - virtual code flush() NOEXCEPT = 0; - - /// Flush, unmap and truncate to logical, restartable, idempotent. - virtual code unload() NOEXCEPT = 0; - - /// Unload and load, causing underyling map to shrink to logical size. - virtual code shrink() NOEXCEPT = 0; - - /// Dump current logical map to a new file in path, must not exist. - virtual code dump(const path& path) const NOEXCEPT = 0; - - /// Current of rows/bytes in map (zero if closed). - virtual size_t size() const NOEXCEPT = 0; - - /// The current count of rows/bytes in map (zero if closed). - virtual size_t capacity() const NOEXCEPT = 0; - - /// Reduce logical size to specified rows/bytes (false if exceeds logical). - virtual bool truncate(size_t count) NOEXCEPT = 0; - - /// Increase logical to specified rows/bytes as required (false if fails). - virtual bool expand(size_t count) NOEXCEPT = 0; - - /// Increase capacity by specified rows/bytes (false only if fails). - virtual bool reserve(size_t count) NOEXCEPT = 0; - - /// Increase logical by specified rows/bytes, return row of first (or eof). - virtual size_t allocate(size_t count) NOEXCEPT = 0; - - /// Get remap-protected r/w access to offset (or null) allocated to size. - virtual memory get_filled(size_t offset, size_t size, - uint8_t backfill) NOEXCEPT = 0; - - /// Get remap-protected r/w access to start/offset of memory map (or null). - /// Pointer is constrained to starting write within full capacity. - virtual memory get_capacity(size_t offset=zero) const NOEXCEPT = 0; - - /// Get unprotected r/w access to start/offset of memory map (or null). - /// Pointer is constrained to starting write within full capacity. - virtual memory::iterator get_raw(size_t offset=zero) const NOEXCEPT = 0; - - /// Get unprotected r/w access to start/offset of memory map (or null). - /// Pointer is constrained to starting write within full capacity. - virtual memory::iterator get_raw_at(size_t column, - size_t offset=zero) const NOEXCEPT = 0; - - /// Get remap-protected r/w access to start/offset of memory map (or null). - /// Pointer is constrained to starting write within logical allocation. - virtual memory get(size_t offset=zero) const NOEXCEPT = 0; - - /// Same as get() but within specified column (or null for invalid column). - /// Pointer is constrained to starting write within logical allocation. - virtual memory get_at(size_t column, - size_t offset=zero) const NOEXCEPT = 0; -}; - -} // namespace database -} // namespace libbitcoin - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_MEMORY_INTERFACES_STORAGE_HPP +#define LIBBITCOIN_DATABASE_MEMORY_INTERFACES_STORAGE_HPP + +#include +#include +#include + +namespace libbitcoin { +namespace database { + +/// Mapped memory interface. +/// A slab has a row width of 1, so "count" implies "bytes" for slabs below. +class storage +{ +public: + static constexpr auto eof = system::bit_all; + using path = std::filesystem::path; + + /// Get the fault condition. + virtual code get_fault() const NOEXCEPT = 0; + + /// Get the space required to clear the disk full condition. + virtual size_t get_space() const NOEXCEPT = 0; + + /// The filesystem path of the backing storage. + virtual const path& file() const NOEXCEPT = 0; + + /// Create empty file, must not exist. + virtual code create() const NOEXCEPT = 0; + + /// Open file, must be closed. + virtual code open() NOEXCEPT = 0; + + /// Close file, must be unloaded, idempotent. + virtual code close() NOEXCEPT = 0; + + /// Map file to memory, must be open and unloaded. + virtual code load() NOEXCEPT = 0; + + /// Clear disk full condition, fails if fault, must be loaded, idempotent. + virtual code reload() NOEXCEPT = 0; + + /// Flush memory map to disk, suspend writes for call, must be loaded. + virtual code flush() NOEXCEPT = 0; + + /// Flush, unmap and truncate to logical, restartable, idempotent. + virtual code unload() NOEXCEPT = 0; + + /// Unload and load, causing underyling map to shrink to logical size. + virtual code shrink() NOEXCEPT = 0; + + /// Dump current logical map to a new file in path, must not exist. + virtual code dump(const path& path) const NOEXCEPT = 0; + + /// Current of rows/bytes in map (zero if closed). + virtual size_t size() const NOEXCEPT = 0; + + /// The current count of rows/bytes in map (zero if closed). + virtual size_t capacity() const NOEXCEPT = 0; + + /// Reduce logical size to specified rows/bytes (false if exceeds logical). + virtual bool truncate(size_t count) NOEXCEPT = 0; + + /// Increase logical to specified rows/bytes as required (false if fails). + virtual bool expand(size_t count) NOEXCEPT = 0; + + /// Increase capacity by specified rows/bytes (false only if fails). + virtual bool reserve(size_t count) NOEXCEPT = 0; + + /// Increase logical by specified rows/bytes, return row of first (or eof). + virtual size_t allocate(size_t count) NOEXCEPT = 0; + + /// Report element write completion of count rows/bytes at offset. + virtual void complete(size_t offset, size_t count) NOEXCEPT = 0; + + /// Rows/bytes below which all writes are complete (size() when quiescent). + virtual size_t frontier() const NOEXCEPT = 0; + + /// Get remap-protected r/w access to offset (or null) allocated to size. + virtual memory get_filled(size_t offset, size_t size, + uint8_t backfill) NOEXCEPT = 0; + + /// Get remap-protected r/w access to start/offset of memory map (or null). + /// Pointer is constrained to starting write within full capacity. + virtual memory get_capacity(size_t offset=zero) const NOEXCEPT = 0; + + /// Get unprotected r/w access to start/offset of memory map (or null). + /// Pointer is constrained to starting write within full capacity. + virtual memory::iterator get_raw(size_t offset=zero) const NOEXCEPT = 0; + + /// Get unprotected r/w access to start/offset of memory map (or null). + /// Pointer is constrained to starting write within full capacity. + virtual memory::iterator get_raw_at(size_t column, + size_t offset=zero) const NOEXCEPT = 0; + + /// Get remap-protected r/w access to start/offset of memory map (or null). + /// Pointer is constrained to starting write within logical allocation. + virtual memory get(size_t offset=zero) const NOEXCEPT = 0; + + /// Same as get() but within specified column (or null for invalid column). + /// Pointer is constrained to starting write within logical allocation. + virtual memory get_at(size_t column, + size_t offset=zero) const NOEXCEPT = 0; +}; + +} // namespace database +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/database/memory/memory.hpp b/include/bitcoin/database/memory/memory.hpp index 074854c88..b60ba4102 100644 --- a/include/bitcoin/database/memory/memory.hpp +++ b/include/bitcoin/database/memory/memory.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #endif diff --git a/include/bitcoin/database/memory/mman.hpp b/include/bitcoin/database/memory/mman.hpp index dad52a15f..ea2b1766c 100644 --- a/include/bitcoin/database/memory/mman.hpp +++ b/include/bitcoin/database/memory/mman.hpp @@ -1,93 +1,61 @@ -// mman_win32 based on code.google.com/p/mman-win32 (MIT License). - -#ifndef LIBBITCOIN_DATABASE_MMAN_HPP -#define LIBBITCOIN_DATABASE_MMAN_HPP - -#include - -#if !defined(HAVE_MSC) - #include - #include - #include - #include -#endif - -#if defined(HAVE_MSC) - -typedef size_t oft__; - -#define PROT_NONE 0 -#define PROT_READ 1 -#define PROT_WRITE 2 -#define PROT_EXEC 4 - -#define MAP_FILE 0 -#define MAP_SYNC 0 -#define MAP_SHARED 1 -#define MAP_SHARED_VALIDATE MAP_SHARED -#define MAP_PRIVATE 2 -#define MAP_TYPE 0xf -#define MAP_FIXED 0x10 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS - -#define MAP_FAILED ((void*)-1) - -// Flags for msync. -#define MS_ASYNC 1 -#define MS_SYNC 2 -#define MS_INVALIDATE 4 - -void* mmap(void* addr, size_t len, int prot, int flags, int fd, - oft__ off) noexcept; -void* mremap_(void* addr, size_t old_size, size_t new_size, int prot, - int flags, int fd) noexcept; -int munmap(void* addr, size_t len) noexcept; -int madvise(void* addr, size_t len, int advice) noexcept; -int mprotect(void* addr, size_t len, int prot) noexcept; -int msync(void* addr, size_t len, int flags) noexcept; -int mlock(const void* addr, size_t len) noexcept; -int munlock(const void* addr, size_t len) noexcept; -int fsync(int fd) noexcept; -int fallocate(int fd, int mode, oft__ offset, oft__ size) noexcept; -int ftruncate(int fd, oft__ size) noexcept; - -#elif defined(HAVE_APPLE) - -int fallocate(int fd, int, off_t offset, off_t len) NOEXCEPT; - -#endif // HAVE_MSC - -// The anonymous staging backend accumulates writes in committed anonymous -// memory and transfers them to the backing file explicitly, so that no dirty -// file-backed page ever exists (posix kernels bound the dirty file page pool -// and force early writeback, untunably so on macOS). Default on macOS, which -// offers no writeback configuration; opt-in elsewhere (Linux exposes -// vm.dirty_* as an alternative). -#if !defined(HAVE_MSC) && !defined(WITHOUT_STAGING) && \ - (defined(HAVE_APPLE) || defined(WITH_STAGING)) - #define HAVE_STAGING -#endif - -#if defined(HAVE_STAGING) - -/// Reserve inaccessible anonymous address space (MAP_FAILED on failure). -void* mmap_reserve(size_t size) NOEXCEPT; - -/// Commit reserved pages as readable/writable anonymous memory. -int mmap_commit(void* address, size_t size) NOEXCEPT; - -/// Replace committed pages with a read-only shared mapping of the file. -int mmap_settle(void* address, size_t size, int fd, size_t offset) NOEXCEPT; - -/// Replace settled pages with committed anonymous memory (contents undefined). -int mmap_unsettle(void* address, size_t size) NOEXCEPT; - -/// Full-transfer positional file read/write (false on failure or early eof). -bool pread_all(int fd, uint8_t* to, size_t size, size_t offset) NOEXCEPT; -bool pwrite_all(int fd, const uint8_t* from, size_t size, - size_t offset) NOEXCEPT; - -#endif // HAVE_STAGING - -#endif +// mman_win32 based on code.google.com/p/mman-win32 (MIT License). + +#ifndef LIBBITCOIN_DATABASE_MMAN_HPP +#define LIBBITCOIN_DATABASE_MMAN_HPP + +#include + +#if !defined(HAVE_MSC) + #include + #include + #include + #include +#endif + +#if defined(HAVE_MSC) + +typedef size_t oft__; + +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 + +#define MAP_FILE 0 +#define MAP_SYNC 0 +#define MAP_SHARED 1 +#define MAP_SHARED_VALIDATE MAP_SHARED +#define MAP_PRIVATE 2 +#define MAP_TYPE 0xf +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS + +#define MAP_FAILED ((void*)-1) + +// Flags for msync. +#define MS_ASYNC 1 +#define MS_SYNC 2 +#define MS_INVALIDATE 4 + +void* mmap(void* addr, size_t len, int prot, int flags, int fd, + oft__ off) noexcept; +void* mremap_(void* addr, size_t old_size, size_t new_size, int prot, + int flags, int fd) noexcept; +int munmap(void* addr, size_t len) noexcept; +int madvise(void* addr, size_t len, int advice) noexcept; +int mprotect(void* addr, size_t len, int prot) noexcept; +int msync(void* addr, size_t len, int flags) noexcept; +int mlock(const void* addr, size_t len) noexcept; +int munlock(const void* addr, size_t len) noexcept; +int fsync(int fd) noexcept; +int fallocate(int fd, int mode, oft__ offset, oft__ size) noexcept; +int ftruncate(int fd, oft__ size) noexcept; + +#elif defined(HAVE_APPLE) + +int fallocate(int fd, int, off_t offset, off_t len) NOEXCEPT; + +#endif // HAVE_MSC + +#endif diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index 6abd7b3a0..db4dd812b 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include namespace libbitcoin { namespace database { @@ -141,6 +141,12 @@ class mmap /// Increase logical by specified rows/bytes, return row of first (or eof). size_t allocate(size_t count) NOEXCEPT override; + /// Report element write completion of count rows/bytes at offset. + void complete(size_t offset, size_t count) NOEXCEPT override; + + /// Rows/bytes below which all writes are complete (size() when quiescent). + size_t frontier() const NOEXCEPT override; + /// Remap-protected r/w access to offset (or null) allocated to size. memory get_filled(size_t offset, size_t size, uint8_t backfill) NOEXCEPT override; @@ -214,7 +220,7 @@ class mmap template bool finalize_(size_t size) NOEXCEPT; -#if defined(HAVE_STAGING) +#if defined(MANAGE_STAGING) // staging dispatch, not thread safe. template bool settle_all_(size_t rows, std::index_sequence) NOEXCEPT; @@ -234,48 +240,58 @@ class mmap void teardown_(const error::error_t& ec) NOEXCEPT; // staging utilities, not thread safe. + void record_(size_t start, size_t count) NOEXCEPT; bool advise_(uint8_t* map, size_t size) const NOEXCEPT; size_t to_reservation(size_t rows) const NOEXCEPT; size_t page_floor(size_t bytes) const NOEXCEPT; size_t page_ceiling(size_t bytes) const NOEXCEPT; -#endif // HAVE_STAGING +#endif // MANAGE_STAGING - // These are thread safe. + // These are thread safe (const). const paths filenames_; const size_t minimum_; const size_t expansion_; const bool random_; const bool staged_; - std::atomic space_{ zero }; - std::atomic error_{ error::success }; - // Scalar fields are atomic: size/capacity reads and the allocate fast - // path (bounded claim within published capacity) are lock-free. Capacity - // growth and compound transitions (open/close/load/unload/shrink/ - // truncate/expand/reserve/get_filled) are serialized by field_mutex_ - // exclusive. Shrinking transitions additionally rely on the documented - // suspend-writes contract (the fast path does not serialize with them). - // logical_ and capacity_ are row counts (byte count if width is one). - std::array opened_; + // These are thread safe (atomic). + std::atomic error_{ error::success }; + std::atomic space_{ zero }; std::atomic capacity_{}; std::atomic logical_{}; - std::atomic fault_{}; - std::atomic loaded_{}; + std::atomic_bool fault_{}; + std::atomic_bool loaded_{}; + + // This is protected by field_mutex_. + std::array opened_; mutable std::shared_mutex field_mutex_{}; - // These are protected by remap_mutex_. + // This is protected by remap_mutex_. std::array memory_map_{}; mutable std::shared_mutex remap_mutex_{}; -#if defined(HAVE_STAGING) - // settled_ is the count of rows flushed to disk (atomic for lock-free - // reads, updated only under remap_mutex_ exclusive). Rows below settled_ - // are backed by a read-only file mapping (staged instances); rows above - // are committed anonymous memory. The others are protected by remap_mutex_. +#if defined(MANAGE_STAGING) + static constexpr size_t extents = 4096; + struct extent + { + size_t start; + size_t count; + size_t outstanding; + }; + + // These are thread safe (atomic). std::atomic settled_{}; + std::atomic frontier_{}; + + // These are protected by extent_mutex_. std::array reserved_{}; + std::array ring_{}; + size_t ring_head_{}; + size_t ring_size_{}; + size_t cursor_{}; size_t page_{}; -#endif // HAVE_STAGING + mutable std::mutex extent_mutex_{}; +#endif // MANAGE_STAGING }; using map = mmap; @@ -291,6 +307,7 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) #include #include #include +#include #include BC_POP_WARNING() diff --git a/include/bitcoin/database/memory/mstage.hpp b/include/bitcoin/database/memory/mstage.hpp new file mode 100644 index 000000000..61c9f2d47 --- /dev/null +++ b/include/bitcoin/database/memory/mstage.hpp @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_MEMORY_MSTAGE_HPP +#define LIBBITCOIN_DATABASE_MEMORY_MSTAGE_HPP + +#include + +#if defined(HAVE_APPLE) + #define MANAGE_STAGING +#endif + +#if defined(MANAGE_STAGING) + +/// Reserve inaccessible anonymous address space (MAP_FAILED on failure). +void* mmap_reserve(size_t size) NOEXCEPT; + +/// Commit reserved pages as readable/writable anonymous memory. +int mmap_commit(void* address, size_t size) NOEXCEPT; + +/// Replace committed pages with a read-only shared mapping of the file. +int mmap_settle(void* address, size_t size, int fd, size_t offset) NOEXCEPT; + +/// Replace settled pages with committed anonymous memory (contents undefined). +int mmap_unsettle(void* address, size_t size) NOEXCEPT; + +/// Full-transfer positional file read/write (false on failure or early eof). +bool pread_all(int fd, uint8_t* to, size_t size, size_t offset) NOEXCEPT; +bool pwrite_all(int fd, const uint8_t* from, size_t size, + size_t offset) NOEXCEPT; + +#endif // MANAGE_STAGING + +#endif diff --git a/include/bitcoin/database/primitives/body.hpp b/include/bitcoin/database/primitives/body.hpp index 0b2067156..430851681 100644 --- a/include/bitcoin/database/primitives/body.hpp +++ b/include/bitcoin/database/primitives/body.hpp @@ -1,133 +1,136 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_BODY_HPP -#define LIBBITCOIN_DATABASE_PRIMITIVES_BODY_HPP - -#include -#include -#include -#include - -namespace libbitcoin { -namespace database { - -template -class bodys -{ -public: - using integer = typename Link::integer; - - template - static constexpr Link position_to_link(size_t position) NOEXCEPT; - template - static constexpr size_t link_to_position(const Link& link) NOEXCEPT; - static constexpr integer cast_link(size_t link) NOEXCEPT; - -public: - DEFAULT_COPY_MOVE_DESTRUCT(bodys); - - /// Return memory object for column full map (null only if oom or unloaded). - template - inline memory get() const NOEXCEPT; - - /// Return memory object for column record (null only if oom or unloaded). - /// Pointer is constrained to starting write within logical allocation. - template - inline memory get(const Link& link) const NOEXCEPT; - template - inline memory::iterator get_raw1(const Link& link) const NOEXCEPT; - - /// Return memory object (limited to AoS) within capacity. - template = true> - inline memory get_capacity(const Link& link) const NOEXCEPT; - - /// Manage shared multi-backed byte storage device (caller owns storage). - bodys(storage& body) NOEXCEPT; - - /// The aggregate logical byte size (cold size) across all columns. - inline size_t size() const NOEXCEPT; - - /// The aggregate byte capacity (hot size) across all columns. - inline size_t capacity() const NOEXCEPT; - - /// The logical record count (common across columns). - inline Link count() const NOEXCEPT; - - /// Reduce logical size to count records (false if exceeds logical). - bool truncate(const Link& count) NOEXCEPT; - - /// Increase logical size to count records as required (false if fails). - bool expand(const Link& count) NOEXCEPT; - - /// Thread safe but reservations do not accumulate (effectively unsafe). - /// Increase capacity by count records (false only if fails). - bool reserve(const Link& count) NOEXCEPT; - - /// Unified allocation across all columns (one lock). - /// Increase logical size by count records, return offset to first (or eof). - Link allocate(const Link& count) NOEXCEPT; - - /// Get the unified fault condition. - code get_fault() const NOEXCEPT; - - /// Get the unified space required to clear the disk full condition. - size_t get_space() const NOEXCEPT; - - /// Unified resume from disk full condition. - code reload() NOEXCEPT; - -private: - static constexpr auto columns = sizeof...(Sizes); - static constexpr auto key_size = keys::size(); - static constexpr std::array sizes{ Sizes... }; - static constexpr auto is_slab = (std::get(sizes) == max_size_t); - static_assert(!is_slab || is_one(columns), "slab implies single column"); - static_assert(!is_zero(columns), "requires at least one column"); - - template - static constexpr size_t stride() NOEXCEPT; - template - static constexpr size_t strides(std::index_sequence) NOEXCEPT; - - /// Convert between record links and the file's native denomination - /// (elements). Single column file elements are BYTES (width one), so - /// records convert by stride (slabs pass through, links are byte offsets). - /// Aggregate file elements are ROWS, which equal records (pass through). - static constexpr size_t link_to_elements(const Link& link) NOEXCEPT; - static constexpr Link elements_to_link(size_t elements) NOEXCEPT; - - // Thread and remap safe. - storage& files_; -}; - -template -using body = bodys; - -} // namespace database -} // namespace libbitcoin - -#define TEMPLATE template -#define CLASS bodys - -#include - -#undef CLASS -#undef TEMPLATE - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_PRIMITIVES_BODY_HPP +#define LIBBITCOIN_DATABASE_PRIMITIVES_BODY_HPP + +#include +#include +#include +#include + +namespace libbitcoin { +namespace database { + +template +class bodys +{ +public: + using integer = typename Link::integer; + + template + static constexpr Link position_to_link(size_t position) NOEXCEPT; + template + static constexpr size_t link_to_position(const Link& link) NOEXCEPT; + static constexpr integer cast_link(size_t link) NOEXCEPT; + +public: + DEFAULT_COPY_MOVE_DESTRUCT(bodys); + + /// Return memory object for column full map (null only if oom or unloaded). + template + inline memory get() const NOEXCEPT; + + /// Return memory object for column record (null only if oom or unloaded). + /// Pointer is constrained to starting write within logical allocation. + template + inline memory get(const Link& link) const NOEXCEPT; + template + inline memory::iterator get_raw1(const Link& link) const NOEXCEPT; + + /// Return memory object (limited to AoS) within capacity. + template = true> + inline memory get_capacity(const Link& link) const NOEXCEPT; + + /// Manage shared multi-backed byte storage device (caller owns storage). + bodys(storage& body) NOEXCEPT; + + /// The aggregate logical byte size (cold size) across all columns. + inline size_t size() const NOEXCEPT; + + /// The aggregate byte capacity (hot size) across all columns. + inline size_t capacity() const NOEXCEPT; + + /// The logical record count (common across columns). + inline Link count() const NOEXCEPT; + + /// Reduce logical size to count records (false if exceeds logical). + bool truncate(const Link& count) NOEXCEPT; + + /// Increase logical size to count records as required (false if fails). + bool expand(const Link& count) NOEXCEPT; + + /// Thread safe but reservations do not accumulate (effectively unsafe). + /// Increase capacity by count records (false only if fails). + bool reserve(const Link& count) NOEXCEPT; + + /// Unified allocation across all columns (one lock). + /// Increase logical size by count records, return offset to first (or eof). + Link allocate(const Link& count) NOEXCEPT; + + /// Report element write completion of count records at link. + void complete(const Link& link, const Link& count) NOEXCEPT; + + /// Get the unified fault condition. + code get_fault() const NOEXCEPT; + + /// Get the unified space required to clear the disk full condition. + size_t get_space() const NOEXCEPT; + + /// Unified resume from disk full condition. + code reload() NOEXCEPT; + +private: + static constexpr auto columns = sizeof...(Sizes); + static constexpr auto key_size = keys::size(); + static constexpr std::array sizes{ Sizes... }; + static constexpr auto is_slab = (std::get(sizes) == max_size_t); + static_assert(!is_slab || is_one(columns), "slab implies single column"); + static_assert(!is_zero(columns), "requires at least one column"); + + template + static constexpr size_t stride() NOEXCEPT; + template + static constexpr size_t strides(std::index_sequence) NOEXCEPT; + + /// Convert between record links and the file's native denomination + /// (elements). Single column file elements are BYTES (width one), so + /// records convert by stride (slabs pass through, links are byte offsets). + /// Aggregate file elements are ROWS, which equal records (pass through). + static constexpr size_t link_to_elements(const Link& link) NOEXCEPT; + static constexpr Link elements_to_link(size_t elements) NOEXCEPT; + + // Thread and remap safe. + storage& files_; +}; + +template +using body = bodys; + +} // namespace database +} // namespace libbitcoin + +#define TEMPLATE template +#define CLASS bodys + +#include + +#undef CLASS +#undef TEMPLATE + +#endif diff --git a/src/memory/mman.cpp b/src/memory/mman.cpp index e93c85b84..37edd1c36 100644 --- a/src/memory/mman.cpp +++ b/src/memory/mman.cpp @@ -1,7 +1,5 @@ // mman_win32 based on code.google.com/p/mman-win32 (MIT License). -#include -#include #include #include @@ -331,87 +329,3 @@ int fallocate(int fd, int, off_t offset, off_t len) NOEXCEPT #endif // HAVE_MSC -#if defined(HAVE_STAGING) - -void* mmap_reserve(size_t size) NOEXCEPT -{ - return ::mmap(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); -} - -int mmap_commit(void* address, size_t size) NOEXCEPT -{ - return ::mprotect(address, size, PROT_READ | PROT_WRITE); -} - -int mmap_settle(void* address, size_t size, int fd, size_t offset) NOEXCEPT -{ - return ::mmap(address, size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, - static_cast(offset)) == MAP_FAILED ? -1 : 0; -} - -int mmap_unsettle(void* address, size_t size) NOEXCEPT -{ - return ::mmap(address, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED ? -1 : 0; -} - -// Chunked to avoid platform-specific single-transfer length limits. -constexpr size_t transfer_chunk = size_t{ 1 } << 30; - -bool pread_all(int fd, uint8_t* to, size_t size, size_t offset) NOEXCEPT -{ - while (size > 0) - { - const auto request = std::min(size, transfer_chunk); - const auto result = ::pread(fd, to, request, - static_cast(offset)); - - if (result < 0) - { - if (errno == EINTR) - continue; - - return false; - } - - // Early eof implies the requested range exceeds the file. - if (result == 0) - return false; - - const auto bytes = static_cast(result); - to += bytes; - offset += bytes; - size -= bytes; - } - - return true; -} - -bool pwrite_all(int fd, const uint8_t* from, size_t size, - size_t offset) NOEXCEPT -{ - while (size > 0) - { - const auto request = std::min(size, transfer_chunk); - const auto result = ::pwrite(fd, from, request, - static_cast(offset)); - - if (result <= 0) - { - if (result < 0 && errno == EINTR) - continue; - - return false; - } - - const auto bytes = static_cast(result); - from += bytes; - offset += bytes; - size -= bytes; - } - - return true; -} - -#endif // HAVE_STAGING diff --git a/src/memory/mstage.cpp b/src/memory/mstage.cpp new file mode 100644 index 000000000..02cbc13ba --- /dev/null +++ b/src/memory/mstage.cpp @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include + +#if defined(MANAGE_STAGING) + +#include +#include +#include + + +void* mmap_reserve(size_t size) NOEXCEPT +{ + return ::mmap(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); +} + +int mmap_commit(void* address, size_t size) NOEXCEPT +{ + return ::mprotect(address, size, PROT_READ | PROT_WRITE); +} + +int mmap_settle(void* address, size_t size, int fd, size_t offset) NOEXCEPT +{ + return ::mmap(address, size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, + static_cast(offset)) == MAP_FAILED ? -1 : 0; +} + +int mmap_unsettle(void* address, size_t size) NOEXCEPT +{ + return ::mmap(address, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED ? -1 : 0; +} + +// Chunked to avoid platform-specific single-transfer length limits. +constexpr size_t transfer_chunk = size_t{ 1 } << 30; + +bool pread_all(int fd, uint8_t* to, size_t size, size_t offset) NOEXCEPT +{ + while (size > 0) + { + const auto request = std::min(size, transfer_chunk); + const auto result = ::pread(fd, to, request, + static_cast(offset)); + + if (result < 0) + { + if (errno == EINTR) + continue; + + return false; + } + + // Early eof implies the requested range exceeds the file. + if (result == 0) + return false; + + const auto bytes = static_cast(result); + to += bytes; + offset += bytes; + size -= bytes; + } + + return true; +} + +bool pwrite_all(int fd, const uint8_t* from, size_t size, + size_t offset) NOEXCEPT +{ + while (size > 0) + { + const auto request = std::min(size, transfer_chunk); + const auto result = ::pwrite(fd, from, request, + static_cast(offset)); + + if (result <= 0) + { + if (result < 0 && errno == EINTR) + continue; + + return false; + } + + const auto bytes = static_cast(result); + from += bytes; + offset += bytes; + size -= bytes; + } + + return true; +} + +#endif // MANAGE_STAGING diff --git a/test/memory/mmap.cpp b/test/memory/mmap.cpp index d17a17386..366b54c1c 100644 --- a/test/memory/mmap.cpp +++ b/test/memory/mmap.cpp @@ -913,6 +913,60 @@ BOOST_AUTO_TEST_CASE(mmap__staged__truncate_below_flush_rewrite__expected) BOOST_REQUIRE(!reopened.get_fault()); } +#if defined(MANAGE_STAGING) + +BOOST_AUTO_TEST_CASE(mmap__frontier__staged_out_of_order_completion__expected) +{ + const std::string file = TEST_PATH; + BOOST_REQUIRE(test::create(file)); + + map instance(file, 1, 50, true, true); + BOOST_REQUIRE(!instance.open()); + BOOST_REQUIRE(!instance.load()); + + // Frontier holds at each extent start until its writes complete. + const auto first = instance.allocate(100); + const auto second = instance.allocate(50); + BOOST_REQUIRE_EQUAL(instance.frontier(), first); + + // Completing the second extent does not advance past the first. + instance.complete(second, 50); + BOOST_REQUIRE_EQUAL(instance.frontier(), first); + + // Completing the first pops both, frontier reaches logical. + instance.complete(first, 100); + BOOST_REQUIRE_EQUAL(instance.frontier(), instance.size()); + + BOOST_REQUIRE(!instance.unload()); + BOOST_REQUIRE(!instance.close()); + BOOST_REQUIRE(!instance.get_fault()); +} + +BOOST_AUTO_TEST_CASE(mmap__frontier__staged_truncate__discards_extents) +{ + const std::string file = TEST_PATH; + BOOST_REQUIRE(test::create(file)); + + map instance(file, 1, 50, true, true); + BOOST_REQUIRE(!instance.open()); + BOOST_REQUIRE(!instance.load()); + + const auto first = instance.allocate(100); + instance.complete(first, 100); + const auto second = instance.allocate(50); + BOOST_REQUIRE_EQUAL(instance.frontier(), second); + + // Truncation below the open extent discards it, frontier follows logical. + BOOST_REQUIRE(instance.truncate(first + 100)); + BOOST_REQUIRE_EQUAL(instance.frontier(), instance.size()); + + BOOST_REQUIRE(!instance.unload()); + BOOST_REQUIRE(!instance.close()); + BOOST_REQUIRE(!instance.get_fault()); +} + +#endif // MANAGE_STAGING + BOOST_AUTO_TEST_CASE(mmap__unstaged__rewrite_below_flush__expected) { constexpr size_t size = 10'000; diff --git a/test/mocks/chunk_storage.hpp b/test/mocks/chunk_storage.hpp index d31e9ea45..c3a6d88e6 100644 --- a/test/mocks/chunk_storage.hpp +++ b/test/mocks/chunk_storage.hpp @@ -1,333 +1,342 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_TEST_MOCKS_CHUNK_STORAGE_HPP -#define LIBBITCOIN_DATABASE_TEST_MOCKS_CHUNK_STORAGE_HPP - -#include -#include -#include "../test.hpp" - -namespace test { - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -template -class default_storage - : public Storage -{ -public: - using path = std::filesystem::path; - - default_storage(const path& filename="test", size_t minimum=1, - size_t expansion=0, bool random=true) NOEXCEPT - : Storage(filename, minimum, expansion, random) - { - } -}; - -/// A thread safe storage implementation built on data_chunk(s); the vector- -/// backed substitute for mmap. One data_chunk per column. -template -class chunk_storages - : public database::storage -{ -public: - using path = std::filesystem::path; - - static constexpr size_t columns = sizeof...(Widths); - static_assert(columns >= one, "requires at least one column"); - static constexpr std::array widths{ Widths... }; - static constexpr size_t stride = (Widths + ...); - using paths = std::array; - - /// Scalar construction (is_one(columns)): single backing. - chunk_storages() NOEXCEPT requires (is_one(columns)) - : alias_{}, paths_{ path{ "test" } }, logical_{} - { - } - - chunk_storages(system::data_chunk& reference) NOEXCEPT - requires (is_one(columns)) - : alias_{ &reference }, paths_{ path{ "test" } }, - logical_{ reference.size() } - { - } - - chunk_storages(const path& filename, size_t=1, size_t=0, bool=true, - bool=false) NOEXCEPT requires (is_one(columns)) - : alias_{}, paths_{ filename }, logical_{} - { - } - - /// Aggregate construction (columns > 1): one backing per column. - chunk_storages(const paths& filenames, size_t=1, size_t=0, bool=true, - bool=false) NOEXCEPT requires (columns > one) - : alias_{}, paths_{ filenames }, logical_{} - { - } - - ~chunk_storages() = default; - - /// test side door (column 0 buffer). - system::data_chunk& buffer() NOEXCEPT - { - return at(zero); - } - - // storage interface (no-op lifecycle). - // ------------------------------------------------------------------------ - - code get_fault() const NOEXCEPT override - { - return {}; - } - - size_t get_space() const NOEXCEPT override - { - return {}; - } - - code create() const NOEXCEPT override - { - return error::success; - } - - code open() NOEXCEPT override - { - return error::success; - } - - code close() NOEXCEPT override - { - return error::success; - } - - code load() NOEXCEPT override - { - return error::success; - } - - code reload() NOEXCEPT override - { - return error::success; - } - - code flush() NOEXCEPT override - { - return error::success; - } - - code unload() NOEXCEPT override - { - return error::success; - } - - code shrink() NOEXCEPT override - { - return error::success; - } - - code dump(const path&) const NOEXCEPT override - { - return error::success; - } - - const path& file() const NOEXCEPT override - { - return paths_[0]; - } - - // sizing (row-denominated, shared across columns). - // ------------------------------------------------------------------------ - - size_t capacity() const NOEXCEPT override - { - std::shared_lock field_lock(field_mutex_); - return is_zero(widths[0]) ? logical_ : at(zero).size() / widths[0]; - } - - size_t size() const NOEXCEPT override - { - std::shared_lock field_lock(field_mutex_); - return logical_; - } - - bool truncate(size_t count) NOEXCEPT override - { - std::unique_lock field_lock(field_mutex_); - if (count > logical_) - return false; - - logical_ = count; - return true; - } - - bool expand(size_t count) NOEXCEPT override - { - std::unique_lock field_lock(field_mutex_); - - for (size_t column{}; column < columns; ++column) - if (to_capacity(count, column) > at(column).max_size()) - return false; - - if (count <= logical_) - return true; - - logical_ = count; - for (size_t column{}; column < columns; ++column) - if (to_capacity(logical_, column) > at(column).size()) - at(column).resize(to_capacity(logical_, column)); - - return true; - } - - bool reserve(size_t count) NOEXCEPT override - { - std::unique_lock field_lock(field_mutex_); - if (system::is_add_overflow(logical_, count)) - return false; - - const auto end = logical_ + count; - for (size_t column{}; column < columns; ++column) - if (to_capacity(end, column) > at(column).max_size()) - return false; - - for (size_t column{}; column < columns; ++column) - if (to_capacity(end, column) > at(column).size()) - at(column).resize(to_capacity(end, column)); - - return true; - } - - size_t allocate(size_t count) NOEXCEPT override - { - std::unique_lock field_lock(field_mutex_); - if (system::is_add_overflow(logical_, count)) - return chunk_storages::eof; - - const auto end = logical_ + count; - for (size_t column{}; column < columns; ++column) - if (to_capacity(end, column) > at(column).max_size()) - return chunk_storages::eof; - - std::unique_lock map_lock(map_mutex_); - const auto link = logical_; - - logical_ = end; - for (size_t column{}; column < columns; ++column) - if (to_capacity(logical_, column) > at(column).size()) - at(column).resize(to_capacity(logical_, column)); - - return link; - } - - // access - // ------------------------------------------------------------------------ - - memory get_filled(size_t offset, size_t size, - uint8_t backfill) NOEXCEPT override - { - { - std::unique_lock field_lock(field_mutex_); - if (system::is_add_overflow(offset, size)) - return {}; - - std::unique_lock map_lock(map_mutex_); - auto& buffer = at(zero); - const auto minimum = offset + size; - if (minimum > buffer.size()) - buffer.resize(minimum, backfill); - - // Reflect byte growth of column 0 into the shared row count. - if (!is_zero(widths[0]) && (minimum / widths[0]) > logical_) - logical_ = (minimum / widths[0]); - } - - return get(offset); - } - - memory get_capacity(size_t offset=zero) const NOEXCEPT override - { - auto& buffer = at(zero); - accessor out{ map_mutex_ }; - out.assign(get_raw(offset), std::next(buffer.data(), buffer.size())); - return out; - } - - memory::iterator get_raw(size_t offset=zero) const NOEXCEPT override - { - return std::next(at(zero).data(), offset); - } - - memory::iterator get_raw_at(size_t column, - size_t offset=zero) const NOEXCEPT override - { - return std::next(at(column).data(), offset); - } - - memory get(size_t offset=zero) const NOEXCEPT override - { - return get_at(zero, offset); - } - - memory get_at(size_t column, size_t offset=zero) const NOEXCEPT override - { - using namespace system; - auto data = at(column).data(); - const auto allocated = size() * widths.at(column); - accessor out(map_mutex_); - out.assign(std::next(data, offset), std::next(data, allocated)); - return out; - } - - // This is protected by mutex. - mutable std::array buffers_{}; - -private: - // Byte capacity of a column for the given shared row count. - static constexpr size_t to_capacity(size_t rows, size_t column) NOEXCEPT - { - return rows * widths.at(column); - } - - // Column backing selector: N owned column chunks, with an optional - // external alias overriding column 0 (single-column reference ctor). - system::data_chunk& at(size_t column) const NOEXCEPT - { - return (is_zero(column) && !is_null(alias_)) ? *alias_ : - buffers_.at(column); - } - - // These are protected by mutex. - system::data_chunk* const alias_; - size_t logical_{}; - - // These are thread safe. - const paths paths_; - mutable std::shared_mutex field_mutex_{}; - mutable std::shared_mutex map_mutex_{}; -}; - -BC_POP_WARNING() - -using chunk_storage = chunk_storages; - -} // namespace test - -#endif +/** + * Copyright (c) 2011-2026 libbitcoin developers + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_DATABASE_TEST_MOCKS_CHUNK_STORAGE_HPP +#define LIBBITCOIN_DATABASE_TEST_MOCKS_CHUNK_STORAGE_HPP + +#include +#include +#include "../test.hpp" + +namespace test { + +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + +template +class default_storage + : public Storage +{ +public: + using path = std::filesystem::path; + + default_storage(const path& filename="test", size_t minimum=1, + size_t expansion=0, bool random=true) NOEXCEPT + : Storage(filename, minimum, expansion, random) + { + } +}; + +/// A thread safe storage implementation built on data_chunk(s); the vector- +/// backed substitute for mmap. One data_chunk per column. +template +class chunk_storages + : public database::storage +{ +public: + using path = std::filesystem::path; + + static constexpr size_t columns = sizeof...(Widths); + static_assert(columns >= one, "requires at least one column"); + static constexpr std::array widths{ Widths... }; + static constexpr size_t stride = (Widths + ...); + using paths = std::array; + + /// Scalar construction (is_one(columns)): single backing. + chunk_storages() NOEXCEPT requires (is_one(columns)) + : alias_{}, paths_{ path{ "test" } }, logical_{} + { + } + + chunk_storages(system::data_chunk& reference) NOEXCEPT + requires (is_one(columns)) + : alias_{ &reference }, paths_{ path{ "test" } }, + logical_{ reference.size() } + { + } + + chunk_storages(const path& filename, size_t=1, size_t=0, bool=true, + bool=false) NOEXCEPT requires (is_one(columns)) + : alias_{}, paths_{ filename }, logical_{} + { + } + + /// Aggregate construction (columns > 1): one backing per column. + chunk_storages(const paths& filenames, size_t=1, size_t=0, bool=true, + bool=false) NOEXCEPT requires (columns > one) + : alias_{}, paths_{ filenames }, logical_{} + { + } + + ~chunk_storages() = default; + + /// test side door (column 0 buffer). + system::data_chunk& buffer() NOEXCEPT + { + return at(zero); + } + + // storage interface (no-op lifecycle). + // ------------------------------------------------------------------------ + + code get_fault() const NOEXCEPT override + { + return {}; + } + + size_t get_space() const NOEXCEPT override + { + return {}; + } + + code create() const NOEXCEPT override + { + return error::success; + } + + code open() NOEXCEPT override + { + return error::success; + } + + code close() NOEXCEPT override + { + return error::success; + } + + code load() NOEXCEPT override + { + return error::success; + } + + code reload() NOEXCEPT override + { + return error::success; + } + + code flush() NOEXCEPT override + { + return error::success; + } + + code unload() NOEXCEPT override + { + return error::success; + } + + code shrink() NOEXCEPT override + { + return error::success; + } + + code dump(const path&) const NOEXCEPT override + { + return error::success; + } + + const path& file() const NOEXCEPT override + { + return paths_[0]; + } + + // sizing (row-denominated, shared across columns). + // ------------------------------------------------------------------------ + + size_t capacity() const NOEXCEPT override + { + std::shared_lock field_lock(field_mutex_); + return is_zero(widths[0]) ? logical_ : at(zero).size() / widths[0]; + } + + size_t size() const NOEXCEPT override + { + std::shared_lock field_lock(field_mutex_); + return logical_; + } + + bool truncate(size_t count) NOEXCEPT override + { + std::unique_lock field_lock(field_mutex_); + if (count > logical_) + return false; + + logical_ = count; + return true; + } + + bool expand(size_t count) NOEXCEPT override + { + std::unique_lock field_lock(field_mutex_); + + for (size_t column{}; column < columns; ++column) + if (to_capacity(count, column) > at(column).max_size()) + return false; + + if (count <= logical_) + return true; + + logical_ = count; + for (size_t column{}; column < columns; ++column) + if (to_capacity(logical_, column) > at(column).size()) + at(column).resize(to_capacity(logical_, column)); + + return true; + } + + bool reserve(size_t count) NOEXCEPT override + { + std::unique_lock field_lock(field_mutex_); + if (system::is_add_overflow(logical_, count)) + return false; + + const auto end = logical_ + count; + for (size_t column{}; column < columns; ++column) + if (to_capacity(end, column) > at(column).max_size()) + return false; + + for (size_t column{}; column < columns; ++column) + if (to_capacity(end, column) > at(column).size()) + at(column).resize(to_capacity(end, column)); + + return true; + } + + size_t allocate(size_t count) NOEXCEPT override + { + std::unique_lock field_lock(field_mutex_); + if (system::is_add_overflow(logical_, count)) + return chunk_storages::eof; + + const auto end = logical_ + count; + for (size_t column{}; column < columns; ++column) + if (to_capacity(end, column) > at(column).max_size()) + return chunk_storages::eof; + + std::unique_lock map_lock(map_mutex_); + const auto link = logical_; + + logical_ = end; + for (size_t column{}; column < columns; ++column) + if (to_capacity(logical_, column) > at(column).size()) + at(column).resize(to_capacity(logical_, column)); + + return link; + } + + void complete(size_t, size_t) NOEXCEPT override + { + } + + size_t frontier() const NOEXCEPT override + { + return size(); + } + + // access + // ------------------------------------------------------------------------ + + memory get_filled(size_t offset, size_t size, + uint8_t backfill) NOEXCEPT override + { + { + std::unique_lock field_lock(field_mutex_); + if (system::is_add_overflow(offset, size)) + return {}; + + std::unique_lock map_lock(map_mutex_); + auto& buffer = at(zero); + const auto minimum = offset + size; + if (minimum > buffer.size()) + buffer.resize(minimum, backfill); + + // Reflect byte growth of column 0 into the shared row count. + if (!is_zero(widths[0]) && (minimum / widths[0]) > logical_) + logical_ = (minimum / widths[0]); + } + + return get(offset); + } + + memory get_capacity(size_t offset=zero) const NOEXCEPT override + { + auto& buffer = at(zero); + accessor out{ map_mutex_ }; + out.assign(get_raw(offset), std::next(buffer.data(), buffer.size())); + return out; + } + + memory::iterator get_raw(size_t offset=zero) const NOEXCEPT override + { + return std::next(at(zero).data(), offset); + } + + memory::iterator get_raw_at(size_t column, + size_t offset=zero) const NOEXCEPT override + { + return std::next(at(column).data(), offset); + } + + memory get(size_t offset=zero) const NOEXCEPT override + { + return get_at(zero, offset); + } + + memory get_at(size_t column, size_t offset=zero) const NOEXCEPT override + { + using namespace system; + auto data = at(column).data(); + const auto allocated = size() * widths.at(column); + accessor out(map_mutex_); + out.assign(std::next(data, offset), std::next(data, allocated)); + return out; + } + + // This is protected by mutex. + mutable std::array buffers_{}; + +private: + // Byte capacity of a column for the given shared row count. + static constexpr size_t to_capacity(size_t rows, size_t column) NOEXCEPT + { + return rows * widths.at(column); + } + + // Column backing selector: N owned column chunks, with an optional + // external alias overriding column 0 (single-column reference ctor). + system::data_chunk& at(size_t column) const NOEXCEPT + { + return (is_zero(column) && !is_null(alias_)) ? *alias_ : + buffers_.at(column); + } + + // These are protected by mutex. + system::data_chunk* const alias_; + size_t logical_{}; + + // These are thread safe. + const paths paths_; + mutable std::shared_mutex field_mutex_{}; + mutable std::shared_mutex map_mutex_{}; +}; + +BC_POP_WARNING() + +using chunk_storage = chunk_storages; + +} // namespace test + +#endif From 60c362bf047d69afebe2836e99049e3877b4758c Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 25 Jul 2026 11:57:11 -0400 Subject: [PATCH 03/12] Posix memory map change accounting to lock-free. --- builds/gnu/Makefile.am | 970 +++++++++--------- .../database/impl/memory/mmap_private.ipp | 13 +- .../database/impl/memory/mmap_staging.ipp | 114 +- .../database/impl/memory/mmap_storage.ipp | 27 +- include/bitcoin/database/memory/mmap.hpp | 13 +- 5 files changed, 586 insertions(+), 551 deletions(-) diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am index f14afe97d..c7f10e158 100644 --- a/builds/gnu/Makefile.am +++ b/builds/gnu/Makefile.am @@ -1,485 +1,485 @@ -############################################################################### -# Copyright (c) 2014-2026 libbitcoin-database developers (see COPYING). -# -# GENERATED SOURCE CODE, DO NOT EDIT EXCEPT EXPERIMENTALLY -# -############################################################################### - -# Automake settings. -#============================================================================== -# Look for macros in the 'm4' subdirectory. -#------------------------------------------------------------------------------ -ACLOCAL_AMFLAGS = -I m4 - -# Distribute data. -#============================================================================== -# files => ${pkgconfigdir} -#------------------------------------------------------------------------------ -pkgconfig_DATA = \ - libbitcoin-database.pc - -# files => ${docdir} -#------------------------------------------------------------------------------ -doc_DATA = \ - AUTHORS \ - ../../COPYING \ - ChangeLog \ - INSTALL \ - NEWS \ - README - -# Libraries. -#============================================================================== -# Target library 'src/libbitcoin-database.la' -#------------------------------------------------------------------------------ -lib_LTLIBRARIES = src/libbitcoin-database.la - -src_libbitcoin_database_la_LIBS = src/libbitcoin-database.la - -src_libbitcoin_database_la_CPPFLAGS = \ - -I${srcdir}/../../include \ - ${libbitcoin_system_BUILD_CPPFLAGS} - -src_libbitcoin_database_la_LDFLAGS = \ - ${libbitcoin_system_LDFLAGS} - -src_libbitcoin_database_la_LIBADD = \ - ${libbitcoin_system_LIBS} - -src_libbitcoin_database_la_SOURCES = \ - ${srcdir}/../../src/define.cpp \ - ${srcdir}/../../src/error.cpp \ - ${srcdir}/../../src/settings.cpp \ - ${srcdir}/../../src/file/rotator.cpp \ - ${srcdir}/../../src/file/utilities.cpp \ - ${srcdir}/../../src/locks/file_lock.cpp \ - ${srcdir}/../../src/locks/flush_lock.cpp \ - ${srcdir}/../../src/locks/interprocess_lock.cpp \ - ${srcdir}/../../src/memory/mman.cpp \ - ${srcdir}/../../src/memory/mstage.cpp \ - ${srcdir}/../../src/memory/utilities.cpp \ - ${srcdir}/../../src/types/history.cpp \ - ${srcdir}/../../src/types/multisig_view.cpp \ - ${srcdir}/../../src/types/unspent.cpp - -include_bitcoindir = \ - ${includedir}/bitcoin - -include_bitcoin_HEADERS = \ - ${srcdir}/../../include/bitcoin/database.hpp - -include_bitcoin_databasedir = \ - ${includedir}/bitcoin/database - -include_bitcoin_database_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/boost.hpp \ - ${srcdir}/../../include/bitcoin/database/define.hpp \ - ${srcdir}/../../include/bitcoin/database/error.hpp \ - ${srcdir}/../../include/bitcoin/database/query.hpp \ - ${srcdir}/../../include/bitcoin/database/settings.hpp \ - ${srcdir}/../../include/bitcoin/database/store.hpp \ - ${srcdir}/../../include/bitcoin/database/version.hpp - -include_bitcoin_database_filedir = \ - ${includedir}/bitcoin/database/file - -include_bitcoin_database_file_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/file/file.hpp \ - ${srcdir}/../../include/bitcoin/database/file/rotator.hpp \ - ${srcdir}/../../include/bitcoin/database/file/utilities.hpp - -include_bitcoin_database_impl_memorydir = \ - ${includedir}/bitcoin/database/impl/memory - -include_bitcoin_database_impl_memory_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_dispatch.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_private.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_staging.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_storage.ipp - -include_bitcoin_database_impl_primitivesdir = \ - ${includedir}/bitcoin/database/impl/primitives - -include_bitcoin_database_impl_primitives_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/arrayhead.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/arraymap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/body.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/hashhead.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/hashmap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/hashmaps.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/iterator.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/keys.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/linkage.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/nohead.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/nomap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/primitives/nomaps.ipp - -include_bitcoin_database_impl_querydir = \ - ${includedir}/bitcoin/database/impl/query - -include_bitcoin_database_impl_query_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/amounts.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/confirmed.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/extent.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/fee_rate.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/filters.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/height.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/initialize.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/locator.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/merkle.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/properties_block.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/properties_tx.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/query.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/sequences.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/sizes.ipp - -include_bitcoin_database_impl_query_addressdir = \ - ${includedir}/bitcoin/database/impl/query/address - -include_bitcoin_database_impl_query_address_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/address/address_balance.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/address/address_history.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/address/address_outpoints.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/address/address_unspent.ipp - -include_bitcoin_database_impl_query_archivedir = \ - ${includedir}/bitcoin/database/impl/query/archive - -include_bitcoin_database_impl_query_archive_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/archive/chain_reader.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/archive/chain_writer.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/archive/wire_reader.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/archive/wire_writer.ipp - -include_bitcoin_database_impl_query_batchdir = \ - ${includedir}/bitcoin/database/impl/query/batch - -include_bitcoin_database_impl_query_batch_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/batch/ecdsa.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/batch/prevalid.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/batch/schnorr.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/batch/silent.ipp - -include_bitcoin_database_impl_query_consensusdir = \ - ${includedir}/bitcoin/database/impl/query/consensus - -include_bitcoin_database_impl_query_consensus_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_block.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_chain_state.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_compact.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_forks.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_populate.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_prevouts.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_states.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_strong.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_transaction.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_work.ipp - -include_bitcoin_database_impl_query_navigatedir = \ - ${includedir}/bitcoin/database/impl/query/navigate - -include_bitcoin_database_impl_query_navigate_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_arraymap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_forward.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_hashmap.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_natural.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_reverse.ipp - -include_bitcoin_database_impl_storedir = \ - ${includedir}/bitcoin/database/impl/store - -include_bitcoin_database_impl_store_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/store/store.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_backup.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_close.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_create.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_dump.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_events.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_open.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_open_load.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_prune.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_reload.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_report.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_restore.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_snapshot.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_tables.ipp \ - ${srcdir}/../../include/bitcoin/database/impl/store/store_unload_close.ipp - -include_bitcoin_database_locksdir = \ - ${includedir}/bitcoin/database/locks - -include_bitcoin_database_locks_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/locks/file_lock.hpp \ - ${srcdir}/../../include/bitcoin/database/locks/flush_lock.hpp \ - ${srcdir}/../../include/bitcoin/database/locks/interprocess_lock.hpp \ - ${srcdir}/../../include/bitcoin/database/locks/locks.hpp - -include_bitcoin_database_memorydir = \ - ${includedir}/bitcoin/database/memory - -include_bitcoin_database_memory_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/memory/accessor.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/finalizer.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/memory.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/mman.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/mmap.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/mmaps.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/mstage.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/reader.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/streamers.hpp \ - ${srcdir}/../../include/bitcoin/database/memory/utilities.hpp - -include_bitcoin_database_memory_interfacesdir = \ - ${includedir}/bitcoin/database/memory/interfaces - -include_bitcoin_database_memory_interfaces_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/memory/interfaces/storage.hpp - -include_bitcoin_database_primitivesdir = \ - ${includedir}/bitcoin/database/primitives - -include_bitcoin_database_primitives_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/primitives/arrayhead.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/arraymap.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/body.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/column.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/hashhead.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/hashmap.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/hashmaps.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/iterator.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/keys.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/linkage.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/nohead.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/nomap.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/nomaps.hpp \ - ${srcdir}/../../include/bitcoin/database/primitives/primitives.hpp - -include_bitcoin_database_tablesdir = \ - ${includedir}/bitcoin/database/tables - -include_bitcoin_database_tables_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/context.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/event.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/names.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/schema.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/table.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/tables.hpp - -include_bitcoin_database_tables_archivesdir = \ - ${includedir}/bitcoin/database/tables/archives - -include_bitcoin_database_tables_archives_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/archives/header.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/input.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/ins.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/output.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/outs.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/transaction.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/archives/txs.hpp - -include_bitcoin_database_tables_cachesdir = \ - ${includedir}/bitcoin/database/tables/caches - -include_bitcoin_database_tables_caches_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/caches/duplicate.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/ecdsa.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/prevalid.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/prevout.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/schnorr.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/silent.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/validated_bk.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/caches/validated_tx.hpp - -include_bitcoin_database_tables_indexesdir = \ - ${includedir}/bitcoin/database/tables/indexes - -include_bitcoin_database_tables_indexes_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/indexes/height.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/indexes/strong_tx.hpp - -include_bitcoin_database_tables_optionalsdir = \ - ${includedir}/bitcoin/database/tables/optionals - -include_bitcoin_database_tables_optionals_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/tables/optionals/address.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/optionals/filter_bk.hpp \ - ${srcdir}/../../include/bitcoin/database/tables/optionals/filter_tx.hpp - -include_bitcoin_database_typesdir = \ - ${includedir}/bitcoin/database/types - -include_bitcoin_database_types_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/types/association.hpp \ - ${srcdir}/../../include/bitcoin/database/types/associations.hpp \ - ${srcdir}/../../include/bitcoin/database/types/block_state.hpp \ - ${srcdir}/../../include/bitcoin/database/types/fee_rate.hpp \ - ${srcdir}/../../include/bitcoin/database/types/header_state.hpp \ - ${srcdir}/../../include/bitcoin/database/types/history.hpp \ - ${srcdir}/../../include/bitcoin/database/types/multisig_view.hpp \ - ${srcdir}/../../include/bitcoin/database/types/point_set.hpp \ - ${srcdir}/../../include/bitcoin/database/types/position.hpp \ - ${srcdir}/../../include/bitcoin/database/types/span.hpp \ - ${srcdir}/../../include/bitcoin/database/types/tx_state.hpp \ - ${srcdir}/../../include/bitcoin/database/types/type.hpp \ - ${srcdir}/../../include/bitcoin/database/types/types.hpp \ - ${srcdir}/../../include/bitcoin/database/types/unspent.hpp - -# Tests. -#============================================================================== -# Target test 'test/libbitcoin-database-test' -#------------------------------------------------------------------------------ -if WITH_TESTS - -check_PROGRAMS = test/libbitcoin-database-test - -test_libbitcoin_database_test_CPPFLAGS = \ - ${boost_BUILD_CPPFLAGS} \ - ${boost_unit_test_framework_BUILD_CPPFLAGS} \ - ${src_libbitcoin_database_la_CPPFLAGS} - -test_libbitcoin_database_test_LDFLAGS = \ - ${boost_LDFLAGS} \ - ${boost_unit_test_framework_LDFLAGS} \ - ${src_libbitcoin_database_la_LDFLAGS} - -test_libbitcoin_database_test_LDADD = \ - ${boost_LIBS} \ - ${boost_unit_test_framework_LIBS} \ - ${src_libbitcoin_database_la_LIBS} \ - ${src_libbitcoin_database_la_LIBADD} - -test_libbitcoin_database_test_SOURCES = \ - ${srcdir}/../../test/error.cpp \ - ${srcdir}/../../test/main.cpp \ - ${srcdir}/../../test/settings.cpp \ - ${srcdir}/../../test/test.cpp \ - ${srcdir}/../../test/file/rotator.cpp \ - ${srcdir}/../../test/file/utilities.cpp \ - ${srcdir}/../../test/locks/file_lock.cpp \ - ${srcdir}/../../test/locks/flush_lock.cpp \ - ${srcdir}/../../test/locks/interprocess_lock.cpp \ - ${srcdir}/../../test/memory/accessor.cpp \ - ${srcdir}/../../test/memory/mmap.cpp \ - ${srcdir}/../../test/memory/utilities.cpp \ - ${srcdir}/../../test/mocks/blocks.cpp \ - ${srcdir}/../../test/primitives/arrayhead.cpp \ - ${srcdir}/../../test/primitives/arraymap.cpp \ - ${srcdir}/../../test/primitives/body.cpp \ - ${srcdir}/../../test/primitives/hashhead.cpp \ - ${srcdir}/../../test/primitives/hashmap.cpp \ - ${srcdir}/../../test/primitives/hashmaps.cpp \ - ${srcdir}/../../test/primitives/iterator.cpp \ - ${srcdir}/../../test/primitives/keys.cpp \ - ${srcdir}/../../test/primitives/linkage.cpp \ - ${srcdir}/../../test/primitives/nohead.cpp \ - ${srcdir}/../../test/primitives/nomap.cpp \ - ${srcdir}/../../test/query/amounts.cpp \ - ${srcdir}/../../test/query/confirmed.cpp \ - ${srcdir}/../../test/query/extent.cpp \ - ${srcdir}/../../test/query/fee_rate.cpp \ - ${srcdir}/../../test/query/filters.cpp \ - ${srcdir}/../../test/query/height.cpp \ - ${srcdir}/../../test/query/initialize.cpp \ - ${srcdir}/../../test/query/locator.cpp \ - ${srcdir}/../../test/query/merkle.cpp \ - ${srcdir}/../../test/query/properties_block.cpp \ - ${srcdir}/../../test/query/properties_tx.cpp \ - ${srcdir}/../../test/query/sequences.cpp \ - ${srcdir}/../../test/query/sizes.cpp \ - ${srcdir}/../../test/query/address/address_balance.cpp \ - ${srcdir}/../../test/query/address/address_history.cpp \ - ${srcdir}/../../test/query/address/address_outpoints.cpp \ - ${srcdir}/../../test/query/address/address_unspent.cpp \ - ${srcdir}/../../test/query/archive/chain_reader.cpp \ - ${srcdir}/../../test/query/archive/chain_writer.cpp \ - ${srcdir}/../../test/query/archive/wire_reader.cpp \ - ${srcdir}/../../test/query/archive/wire_writer.cpp \ - ${srcdir}/../../test/query/batch/ecdsa.cpp \ - ${srcdir}/../../test/query/batch/prevalid.cpp \ - ${srcdir}/../../test/query/batch/schnorr.cpp \ - ${srcdir}/../../test/query/batch/silent.cpp \ - ${srcdir}/../../test/query/consensus/consensus_block.cpp \ - ${srcdir}/../../test/query/consensus/consensus_chain_state.cpp \ - ${srcdir}/../../test/query/consensus/consensus_compact.cpp \ - ${srcdir}/../../test/query/consensus/consensus_forks.cpp \ - ${srcdir}/../../test/query/consensus/consensus_populate.cpp \ - ${srcdir}/../../test/query/consensus/consensus_prevouts.cpp \ - ${srcdir}/../../test/query/consensus/consensus_states.cpp \ - ${srcdir}/../../test/query/consensus/consensus_strong.cpp \ - ${srcdir}/../../test/query/consensus/consensus_transaction.cpp \ - ${srcdir}/../../test/query/consensus/consensus_work.cpp \ - ${srcdir}/../../test/query/navigate/navigate_arraymap.cpp \ - ${srcdir}/../../test/query/navigate/navigate_forward.cpp \ - ${srcdir}/../../test/query/navigate/navigate_hashmap.cpp \ - ${srcdir}/../../test/query/navigate/navigate_natural.cpp \ - ${srcdir}/../../test/query/navigate/navigate_reverse.cpp \ - ${srcdir}/../../test/store/store.cpp \ - ${srcdir}/../../test/store/store_backup.cpp \ - ${srcdir}/../../test/store/store_close.cpp \ - ${srcdir}/../../test/store/store_create.cpp \ - ${srcdir}/../../test/store/store_dump.cpp \ - ${srcdir}/../../test/store/store_events.cpp \ - ${srcdir}/../../test/store/store_open.cpp \ - ${srcdir}/../../test/store/store_open_load.cpp \ - ${srcdir}/../../test/store/store_prune.cpp \ - ${srcdir}/../../test/store/store_reload.cpp \ - ${srcdir}/../../test/store/store_report.cpp \ - ${srcdir}/../../test/store/store_restore.cpp \ - ${srcdir}/../../test/store/store_snapshot.cpp \ - ${srcdir}/../../test/store/store_tables.cpp \ - ${srcdir}/../../test/store/store_unload_close.cpp \ - ${srcdir}/../../test/tables/archives/header.cpp \ - ${srcdir}/../../test/tables/archives/input.cpp \ - ${srcdir}/../../test/tables/archives/ins.cpp \ - ${srcdir}/../../test/tables/archives/output.cpp \ - ${srcdir}/../../test/tables/archives/outs.cpp \ - ${srcdir}/../../test/tables/archives/transaction.cpp \ - ${srcdir}/../../test/tables/archives/txs.cpp \ - ${srcdir}/../../test/tables/caches/duplicate.cpp \ - ${srcdir}/../../test/tables/caches/ecdsa.cpp \ - ${srcdir}/../../test/tables/caches/prevalid.cpp \ - ${srcdir}/../../test/tables/caches/prevout.cpp \ - ${srcdir}/../../test/tables/caches/schnorr.cpp \ - ${srcdir}/../../test/tables/caches/silent.cpp \ - ${srcdir}/../../test/tables/caches/validated_bk.cpp \ - ${srcdir}/../../test/tables/caches/validated_tx.cpp \ - ${srcdir}/../../test/tables/indexes/height.cpp \ - ${srcdir}/../../test/tables/indexes/strong_tx.cpp \ - ${srcdir}/../../test/tables/optional/address.cpp \ - ${srcdir}/../../test/tables/optional/filter_bk.cpp \ - ${srcdir}/../../test/tables/optional/filter_tx.cpp \ - ${srcdir}/../../test/types/history.cpp \ - ${srcdir}/../../test/types/span.cpp \ - ${srcdir}/../../test/types/unspent.cpp - -TESTS = test_runner.sh - -endif WITH_TESTS - -# Binaries. -#============================================================================== -# Target binary 'tools/initchain/initchain' -#------------------------------------------------------------------------------ -if WITH_TOOLS - -noinst_PROGRAMS = tools/initchain/initchain - -tools_initchain_initchain_CPPFLAGS = \ - ${src_libbitcoin_database_la_CPPFLAGS} - -tools_initchain_initchain_LDFLAGS = \ - ${src_libbitcoin_database_la_LDFLAGS} - -tools_initchain_initchain_LDADD = \ - ${src_libbitcoin_database_la_LIBS} \ - ${src_libbitcoin_database_la_LIBADD} - -tools_initchain_initchain_SOURCES = \ - ${srcdir}/../../tools/initchain/initchain.cpp - -target_tools = tools/initchain/initchain - -tools: ${target_tools} - -endif WITH_TOOLS +############################################################################### +# Copyright (c) 2014-2026 libbitcoin-database developers (see COPYING). +# +# GENERATED SOURCE CODE, DO NOT EDIT EXCEPT EXPERIMENTALLY +# +############################################################################### + +# Automake settings. +#============================================================================== +# Look for macros in the 'm4' subdirectory. +#------------------------------------------------------------------------------ +ACLOCAL_AMFLAGS = -I m4 + +# Distribute data. +#============================================================================== +# files => ${pkgconfigdir} +#------------------------------------------------------------------------------ +pkgconfig_DATA = \ + libbitcoin-database.pc + +# files => ${docdir} +#------------------------------------------------------------------------------ +doc_DATA = \ + AUTHORS \ + ../../COPYING \ + ChangeLog \ + INSTALL \ + NEWS \ + README + +# Libraries. +#============================================================================== +# Target library 'src/libbitcoin-database.la' +#------------------------------------------------------------------------------ +lib_LTLIBRARIES = src/libbitcoin-database.la + +src_libbitcoin_database_la_LIBS = src/libbitcoin-database.la + +src_libbitcoin_database_la_CPPFLAGS = \ + -I${srcdir}/../../include \ + ${libbitcoin_system_BUILD_CPPFLAGS} + +src_libbitcoin_database_la_LDFLAGS = \ + ${libbitcoin_system_LDFLAGS} + +src_libbitcoin_database_la_LIBADD = \ + ${libbitcoin_system_LIBS} + +src_libbitcoin_database_la_SOURCES = \ + ${srcdir}/../../src/define.cpp \ + ${srcdir}/../../src/error.cpp \ + ${srcdir}/../../src/settings.cpp \ + ${srcdir}/../../src/file/rotator.cpp \ + ${srcdir}/../../src/file/utilities.cpp \ + ${srcdir}/../../src/locks/file_lock.cpp \ + ${srcdir}/../../src/locks/flush_lock.cpp \ + ${srcdir}/../../src/locks/interprocess_lock.cpp \ + ${srcdir}/../../src/memory/mman.cpp \ + ${srcdir}/../../src/memory/mstage.cpp \ + ${srcdir}/../../src/memory/utilities.cpp \ + ${srcdir}/../../src/types/history.cpp \ + ${srcdir}/../../src/types/multisig_view.cpp \ + ${srcdir}/../../src/types/unspent.cpp + +include_bitcoindir = \ + ${includedir}/bitcoin + +include_bitcoin_HEADERS = \ + ${srcdir}/../../include/bitcoin/database.hpp + +include_bitcoin_databasedir = \ + ${includedir}/bitcoin/database + +include_bitcoin_database_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/boost.hpp \ + ${srcdir}/../../include/bitcoin/database/define.hpp \ + ${srcdir}/../../include/bitcoin/database/error.hpp \ + ${srcdir}/../../include/bitcoin/database/query.hpp \ + ${srcdir}/../../include/bitcoin/database/settings.hpp \ + ${srcdir}/../../include/bitcoin/database/store.hpp \ + ${srcdir}/../../include/bitcoin/database/version.hpp + +include_bitcoin_database_filedir = \ + ${includedir}/bitcoin/database/file + +include_bitcoin_database_file_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/file/file.hpp \ + ${srcdir}/../../include/bitcoin/database/file/rotator.hpp \ + ${srcdir}/../../include/bitcoin/database/file/utilities.hpp + +include_bitcoin_database_impl_memorydir = \ + ${includedir}/bitcoin/database/impl/memory + +include_bitcoin_database_impl_memory_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_dispatch.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_private.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_staging.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_storage.ipp + +include_bitcoin_database_impl_primitivesdir = \ + ${includedir}/bitcoin/database/impl/primitives + +include_bitcoin_database_impl_primitives_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/arrayhead.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/arraymap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/body.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/hashhead.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/hashmap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/hashmaps.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/iterator.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/keys.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/linkage.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/nohead.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/nomap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/primitives/nomaps.ipp + +include_bitcoin_database_impl_querydir = \ + ${includedir}/bitcoin/database/impl/query + +include_bitcoin_database_impl_query_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/amounts.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/confirmed.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/extent.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/fee_rate.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/filters.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/height.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/initialize.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/locator.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/merkle.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/properties_block.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/properties_tx.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/query.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/sequences.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/sizes.ipp + +include_bitcoin_database_impl_query_addressdir = \ + ${includedir}/bitcoin/database/impl/query/address + +include_bitcoin_database_impl_query_address_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/address/address_balance.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/address/address_history.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/address/address_outpoints.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/address/address_unspent.ipp + +include_bitcoin_database_impl_query_archivedir = \ + ${includedir}/bitcoin/database/impl/query/archive + +include_bitcoin_database_impl_query_archive_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/archive/chain_reader.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/archive/chain_writer.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/archive/wire_reader.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/archive/wire_writer.ipp + +include_bitcoin_database_impl_query_batchdir = \ + ${includedir}/bitcoin/database/impl/query/batch + +include_bitcoin_database_impl_query_batch_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/batch/ecdsa.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/batch/prevalid.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/batch/schnorr.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/batch/silent.ipp + +include_bitcoin_database_impl_query_consensusdir = \ + ${includedir}/bitcoin/database/impl/query/consensus + +include_bitcoin_database_impl_query_consensus_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_block.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_chain_state.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_compact.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_forks.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_populate.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_prevouts.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_states.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_strong.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_transaction.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/consensus/consensus_work.ipp + +include_bitcoin_database_impl_query_navigatedir = \ + ${includedir}/bitcoin/database/impl/query/navigate + +include_bitcoin_database_impl_query_navigate_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_arraymap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_forward.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_hashmap.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_natural.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/query/navigate/navigate_reverse.ipp + +include_bitcoin_database_impl_storedir = \ + ${includedir}/bitcoin/database/impl/store + +include_bitcoin_database_impl_store_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/impl/store/store.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_backup.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_close.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_create.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_dump.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_events.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_open.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_open_load.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_prune.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_reload.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_report.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_restore.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_snapshot.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_tables.ipp \ + ${srcdir}/../../include/bitcoin/database/impl/store/store_unload_close.ipp + +include_bitcoin_database_locksdir = \ + ${includedir}/bitcoin/database/locks + +include_bitcoin_database_locks_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/locks/file_lock.hpp \ + ${srcdir}/../../include/bitcoin/database/locks/flush_lock.hpp \ + ${srcdir}/../../include/bitcoin/database/locks/interprocess_lock.hpp \ + ${srcdir}/../../include/bitcoin/database/locks/locks.hpp + +include_bitcoin_database_memorydir = \ + ${includedir}/bitcoin/database/memory + +include_bitcoin_database_memory_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/memory/accessor.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/finalizer.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/memory.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/mman.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/mmap.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/mmaps.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/mstage.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/reader.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/streamers.hpp \ + ${srcdir}/../../include/bitcoin/database/memory/utilities.hpp + +include_bitcoin_database_memory_interfacesdir = \ + ${includedir}/bitcoin/database/memory/interfaces + +include_bitcoin_database_memory_interfaces_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/memory/interfaces/storage.hpp + +include_bitcoin_database_primitivesdir = \ + ${includedir}/bitcoin/database/primitives + +include_bitcoin_database_primitives_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/primitives/arrayhead.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/arraymap.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/body.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/column.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/hashhead.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/hashmap.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/hashmaps.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/iterator.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/keys.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/linkage.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/nohead.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/nomap.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/nomaps.hpp \ + ${srcdir}/../../include/bitcoin/database/primitives/primitives.hpp + +include_bitcoin_database_tablesdir = \ + ${includedir}/bitcoin/database/tables + +include_bitcoin_database_tables_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/context.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/event.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/names.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/schema.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/table.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/tables.hpp + +include_bitcoin_database_tables_archivesdir = \ + ${includedir}/bitcoin/database/tables/archives + +include_bitcoin_database_tables_archives_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/archives/header.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/input.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/ins.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/output.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/outs.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/transaction.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/archives/txs.hpp + +include_bitcoin_database_tables_cachesdir = \ + ${includedir}/bitcoin/database/tables/caches + +include_bitcoin_database_tables_caches_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/caches/duplicate.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/ecdsa.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/prevalid.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/prevout.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/schnorr.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/silent.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/validated_bk.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/caches/validated_tx.hpp + +include_bitcoin_database_tables_indexesdir = \ + ${includedir}/bitcoin/database/tables/indexes + +include_bitcoin_database_tables_indexes_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/indexes/height.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/indexes/strong_tx.hpp + +include_bitcoin_database_tables_optionalsdir = \ + ${includedir}/bitcoin/database/tables/optionals + +include_bitcoin_database_tables_optionals_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/tables/optionals/address.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/optionals/filter_bk.hpp \ + ${srcdir}/../../include/bitcoin/database/tables/optionals/filter_tx.hpp + +include_bitcoin_database_typesdir = \ + ${includedir}/bitcoin/database/types + +include_bitcoin_database_types_HEADERS = \ + ${srcdir}/../../include/bitcoin/database/types/association.hpp \ + ${srcdir}/../../include/bitcoin/database/types/associations.hpp \ + ${srcdir}/../../include/bitcoin/database/types/block_state.hpp \ + ${srcdir}/../../include/bitcoin/database/types/fee_rate.hpp \ + ${srcdir}/../../include/bitcoin/database/types/header_state.hpp \ + ${srcdir}/../../include/bitcoin/database/types/history.hpp \ + ${srcdir}/../../include/bitcoin/database/types/multisig_view.hpp \ + ${srcdir}/../../include/bitcoin/database/types/point_set.hpp \ + ${srcdir}/../../include/bitcoin/database/types/position.hpp \ + ${srcdir}/../../include/bitcoin/database/types/span.hpp \ + ${srcdir}/../../include/bitcoin/database/types/tx_state.hpp \ + ${srcdir}/../../include/bitcoin/database/types/type.hpp \ + ${srcdir}/../../include/bitcoin/database/types/types.hpp \ + ${srcdir}/../../include/bitcoin/database/types/unspent.hpp + +# Tests. +#============================================================================== +# Target test 'test/libbitcoin-database-test' +#------------------------------------------------------------------------------ +if WITH_TESTS + +check_PROGRAMS = test/libbitcoin-database-test + +test_libbitcoin_database_test_CPPFLAGS = \ + ${boost_BUILD_CPPFLAGS} \ + ${boost_unit_test_framework_BUILD_CPPFLAGS} \ + ${src_libbitcoin_database_la_CPPFLAGS} + +test_libbitcoin_database_test_LDFLAGS = \ + ${boost_LDFLAGS} \ + ${boost_unit_test_framework_LDFLAGS} \ + ${src_libbitcoin_database_la_LDFLAGS} + +test_libbitcoin_database_test_LDADD = \ + ${boost_LIBS} \ + ${boost_unit_test_framework_LIBS} \ + ${src_libbitcoin_database_la_LIBS} \ + ${src_libbitcoin_database_la_LIBADD} + +test_libbitcoin_database_test_SOURCES = \ + ${srcdir}/../../test/error.cpp \ + ${srcdir}/../../test/main.cpp \ + ${srcdir}/../../test/settings.cpp \ + ${srcdir}/../../test/test.cpp \ + ${srcdir}/../../test/file/rotator.cpp \ + ${srcdir}/../../test/file/utilities.cpp \ + ${srcdir}/../../test/locks/file_lock.cpp \ + ${srcdir}/../../test/locks/flush_lock.cpp \ + ${srcdir}/../../test/locks/interprocess_lock.cpp \ + ${srcdir}/../../test/memory/accessor.cpp \ + ${srcdir}/../../test/memory/mmap.cpp \ + ${srcdir}/../../test/memory/utilities.cpp \ + ${srcdir}/../../test/mocks/blocks.cpp \ + ${srcdir}/../../test/primitives/arrayhead.cpp \ + ${srcdir}/../../test/primitives/arraymap.cpp \ + ${srcdir}/../../test/primitives/body.cpp \ + ${srcdir}/../../test/primitives/hashhead.cpp \ + ${srcdir}/../../test/primitives/hashmap.cpp \ + ${srcdir}/../../test/primitives/hashmaps.cpp \ + ${srcdir}/../../test/primitives/iterator.cpp \ + ${srcdir}/../../test/primitives/keys.cpp \ + ${srcdir}/../../test/primitives/linkage.cpp \ + ${srcdir}/../../test/primitives/nohead.cpp \ + ${srcdir}/../../test/primitives/nomap.cpp \ + ${srcdir}/../../test/query/amounts.cpp \ + ${srcdir}/../../test/query/confirmed.cpp \ + ${srcdir}/../../test/query/extent.cpp \ + ${srcdir}/../../test/query/fee_rate.cpp \ + ${srcdir}/../../test/query/filters.cpp \ + ${srcdir}/../../test/query/height.cpp \ + ${srcdir}/../../test/query/initialize.cpp \ + ${srcdir}/../../test/query/locator.cpp \ + ${srcdir}/../../test/query/merkle.cpp \ + ${srcdir}/../../test/query/properties_block.cpp \ + ${srcdir}/../../test/query/properties_tx.cpp \ + ${srcdir}/../../test/query/sequences.cpp \ + ${srcdir}/../../test/query/sizes.cpp \ + ${srcdir}/../../test/query/address/address_balance.cpp \ + ${srcdir}/../../test/query/address/address_history.cpp \ + ${srcdir}/../../test/query/address/address_outpoints.cpp \ + ${srcdir}/../../test/query/address/address_unspent.cpp \ + ${srcdir}/../../test/query/archive/chain_reader.cpp \ + ${srcdir}/../../test/query/archive/chain_writer.cpp \ + ${srcdir}/../../test/query/archive/wire_reader.cpp \ + ${srcdir}/../../test/query/archive/wire_writer.cpp \ + ${srcdir}/../../test/query/batch/ecdsa.cpp \ + ${srcdir}/../../test/query/batch/prevalid.cpp \ + ${srcdir}/../../test/query/batch/schnorr.cpp \ + ${srcdir}/../../test/query/batch/silent.cpp \ + ${srcdir}/../../test/query/consensus/consensus_block.cpp \ + ${srcdir}/../../test/query/consensus/consensus_chain_state.cpp \ + ${srcdir}/../../test/query/consensus/consensus_compact.cpp \ + ${srcdir}/../../test/query/consensus/consensus_forks.cpp \ + ${srcdir}/../../test/query/consensus/consensus_populate.cpp \ + ${srcdir}/../../test/query/consensus/consensus_prevouts.cpp \ + ${srcdir}/../../test/query/consensus/consensus_states.cpp \ + ${srcdir}/../../test/query/consensus/consensus_strong.cpp \ + ${srcdir}/../../test/query/consensus/consensus_transaction.cpp \ + ${srcdir}/../../test/query/consensus/consensus_work.cpp \ + ${srcdir}/../../test/query/navigate/navigate_arraymap.cpp \ + ${srcdir}/../../test/query/navigate/navigate_forward.cpp \ + ${srcdir}/../../test/query/navigate/navigate_hashmap.cpp \ + ${srcdir}/../../test/query/navigate/navigate_natural.cpp \ + ${srcdir}/../../test/query/navigate/navigate_reverse.cpp \ + ${srcdir}/../../test/store/store.cpp \ + ${srcdir}/../../test/store/store_backup.cpp \ + ${srcdir}/../../test/store/store_close.cpp \ + ${srcdir}/../../test/store/store_create.cpp \ + ${srcdir}/../../test/store/store_dump.cpp \ + ${srcdir}/../../test/store/store_events.cpp \ + ${srcdir}/../../test/store/store_open.cpp \ + ${srcdir}/../../test/store/store_open_load.cpp \ + ${srcdir}/../../test/store/store_prune.cpp \ + ${srcdir}/../../test/store/store_reload.cpp \ + ${srcdir}/../../test/store/store_report.cpp \ + ${srcdir}/../../test/store/store_restore.cpp \ + ${srcdir}/../../test/store/store_snapshot.cpp \ + ${srcdir}/../../test/store/store_tables.cpp \ + ${srcdir}/../../test/store/store_unload_close.cpp \ + ${srcdir}/../../test/tables/archives/header.cpp \ + ${srcdir}/../../test/tables/archives/input.cpp \ + ${srcdir}/../../test/tables/archives/ins.cpp \ + ${srcdir}/../../test/tables/archives/output.cpp \ + ${srcdir}/../../test/tables/archives/outs.cpp \ + ${srcdir}/../../test/tables/archives/transaction.cpp \ + ${srcdir}/../../test/tables/archives/txs.cpp \ + ${srcdir}/../../test/tables/caches/duplicate.cpp \ + ${srcdir}/../../test/tables/caches/ecdsa.cpp \ + ${srcdir}/../../test/tables/caches/prevalid.cpp \ + ${srcdir}/../../test/tables/caches/prevout.cpp \ + ${srcdir}/../../test/tables/caches/schnorr.cpp \ + ${srcdir}/../../test/tables/caches/silent.cpp \ + ${srcdir}/../../test/tables/caches/validated_bk.cpp \ + ${srcdir}/../../test/tables/caches/validated_tx.cpp \ + ${srcdir}/../../test/tables/indexes/height.cpp \ + ${srcdir}/../../test/tables/indexes/strong_tx.cpp \ + ${srcdir}/../../test/tables/optional/address.cpp \ + ${srcdir}/../../test/tables/optional/filter_bk.cpp \ + ${srcdir}/../../test/tables/optional/filter_tx.cpp \ + ${srcdir}/../../test/types/history.cpp \ + ${srcdir}/../../test/types/span.cpp \ + ${srcdir}/../../test/types/unspent.cpp + +TESTS = test_runner.sh + +endif WITH_TESTS + +# Binaries. +#============================================================================== +# Target binary 'tools/initchain/initchain' +#------------------------------------------------------------------------------ +if WITH_TOOLS + +noinst_PROGRAMS = tools/initchain/initchain + +tools_initchain_initchain_CPPFLAGS = \ + ${src_libbitcoin_database_la_CPPFLAGS} + +tools_initchain_initchain_LDFLAGS = \ + ${src_libbitcoin_database_la_LDFLAGS} + +tools_initchain_initchain_LDADD = \ + ${src_libbitcoin_database_la_LIBS} \ + ${src_libbitcoin_database_la_LIBADD} + +tools_initchain_initchain_SOURCES = \ + ${srcdir}/../../tools/initchain/initchain.cpp + +target_tools = tools/initchain/initchain + +tools: ${target_tools} + +endif WITH_TOOLS diff --git a/include/bitcoin/database/impl/memory/mmap_private.ipp b/include/bitcoin/database/impl/memory/mmap_private.ipp index 4e2e45435..b5a3800e4 100644 --- a/include/bitcoin/database/impl/memory/mmap_private.ipp +++ b/include/bitcoin/database/impl/memory/mmap_private.ipp @@ -25,6 +25,7 @@ #include #include #include +#include namespace libbitcoin { namespace database { @@ -46,7 +47,7 @@ bool CLASS::map_all_(std::index_sequence) NOEXCEPT { #if defined(MANAGE_STAGING) using namespace system; - const auto page = possible_narrow_sign_cast(page_size()); + const auto page = page_size(); // Page size must be a power of two. if (is_zero(page) || !is_one(ones_count(page))) @@ -57,9 +58,8 @@ bool CLASS::map_all_(std::index_sequence) NOEXCEPT } page_ = page; - cursor_ = zero; - ring_head_ = zero; - ring_size_ = zero; + ring_head_.store(zero); + ring_size_.store(zero); settled_.store(staged_ ? logical_.load() : zero); frontier_.store(staged_ ? logical_.load() : zero); #endif @@ -83,9 +83,8 @@ bool CLASS::unmap_all_(std::index_sequence) NOEXCEPT capacity_.store(zero); #if defined(MANAGE_STAGING) - cursor_ = zero; - ring_head_ = zero; - ring_size_ = zero; + ring_head_.store(zero); + ring_size_.store(zero); settled_.store(zero); frontier_.store(zero); #endif diff --git a/include/bitcoin/database/impl/memory/mmap_staging.ipp b/include/bitcoin/database/impl/memory/mmap_staging.ipp index d12b1e359..6c6baf4bd 100644 --- a/include/bitcoin/database/impl/memory/mmap_staging.ipp +++ b/include/bitcoin/database/impl/memory/mmap_staging.ipp @@ -42,51 +42,56 @@ void CLASS::complete(size_t ) NOEXCEPT { #if defined(MANAGE_STAGING) - std::unique_lock extent_lock(extent_mutex_); - - if (!staged_ || is_zero(ring_size_) || is_zero(count)) + if (!staged_ || is_zero(count)) return; - // Cursor probe: completions cluster at the frontier, so the walk from the - // remembered extent (falling back to a full scan) is amortized O(1). - auto found = extents; - auto index = cursor_; - for (size_t probe{}; probe < ring_size_; ++probe) + // Acquire-ordered window snapshot (published entries are immobile). + const auto head = ring_head_.load(std::memory_order_acquire); + const auto size = ring_size_.load(std::memory_order_acquire); + + // Lock-free binary search of the sorted window for the covering extent. + size_t low{}; + auto high = size; + while (low < high) { - const auto& record = ring_.at(index); - if ((offset >= record.start) && - (offset < system::ceilinged_add(record.start, record.count))) + const auto middle = to_half(low + high); + auto& record = ring_.at((head + middle) % extents); + const auto start = record.start.load(std::memory_order_relaxed); + + if (offset < start) { - found = index; - break; + high = middle; + continue; } - index = (index + one) % extents; - if (index == ((ring_head_ + ring_size_) % extents)) - index = ring_head_; - } - - if (found == extents) - return; + if (offset >= (start + record.count)) + { + low = add1(middle); + continue; + } - cursor_ = found; - auto& record = ring_.at(found); - record.outstanding = system::floored_subtract(record.outstanding, count); + // Wrap from over-completion stalls the frontier (conservative). + const auto previous = record.outstanding.fetch_sub(count, + std::memory_order_relaxed); - // Pop completed extents from the head, advancing the frontier. - while (!is_zero(ring_size_) && is_zero(ring_.at(ring_head_).outstanding)) - { - ring_head_ = (ring_head_ + one) % extents; - --ring_size_; - } + // Repair a decrement landed on a recycled slot (start moved). + if (record.start.load(std::memory_order_relaxed) != start) + { + record.outstanding.fetch_add(count, std::memory_order_relaxed); + return; + } - frontier_.store(is_zero(ring_size_) ? logical_.load() : - ring_.at(ring_head_).start); + // Extent completion is allocation-coarse, so maintenance is cheap + // here and the element write fast path otherwise takes no lock. + if (previous == count) + { + std::unique_lock extent_lock(extent_mutex_, std::try_to_lock); + if (extent_lock.owns_lock()) + maintain_(); + } - // Reset a cursor left pointing at a popped (dead) slot. - const auto live = !is_zero(ring_size_) && - (((found + extents - ring_head_) % extents) < ring_size_); - cursor_ = live ? found : ring_head_; + return; + } #endif } @@ -110,20 +115,45 @@ void CLASS::record_(size_t start, size_t count) NOEXCEPT return; std::unique_lock extent_lock(extent_mutex_); + maintain_(); // Saturation stalls the frontier (conservative, safe); asserts in debug. - BC_ASSERT(ring_size_ < extents); - if (ring_size_ == extents) + const auto size = ring_size_.load(std::memory_order_relaxed); + BC_ASSERT(size < extents); + if (size == extents) return; - const auto tail = (ring_head_ + ring_size_) % extents; - ring_.at(tail) = { start, count, count * columns }; + const auto head = ring_head_.load(std::memory_order_relaxed); + auto& record = ring_.at((head + size) % extents); + record.start.store(start, std::memory_order_relaxed); + record.count = count; + record.outstanding.store(count * columns, std::memory_order_relaxed); - if (is_zero(ring_size_++)) - { + // Publish the extent (pairs with the acquire window snapshot). + ring_size_.store(add1(size), std::memory_order_release); + + if (is_zero(size)) frontier_.store(start); - cursor_ = ring_head_; +} + +// Pop completed extents from the head, advancing the frontier (locked). +TEMPLATE +void CLASS::maintain_() NOEXCEPT +{ + auto head = ring_head_.load(std::memory_order_relaxed); + auto size = ring_size_.load(std::memory_order_relaxed); + + while (!is_zero(size) && + is_zero(ring_.at(head).outstanding.load(std::memory_order_relaxed))) + { + head = add1(head) % extents; + --size; } + + ring_head_.store(head, std::memory_order_relaxed); + ring_size_.store(size, std::memory_order_release); + frontier_.store(is_zero(size) ? logical_.load() : + ring_.at(head).start.load(std::memory_order_relaxed)); } // staging dispatch, not thread safe. diff --git a/include/bitcoin/database/impl/memory/mmap_storage.ipp b/include/bitcoin/database/impl/memory/mmap_storage.ipp index 8798ae764..277319fae 100644 --- a/include/bitcoin/database/impl/memory/mmap_storage.ipp +++ b/include/bitcoin/database/impl/memory/mmap_storage.ipp @@ -324,28 +324,33 @@ bool CLASS::truncate(size_t count) NOEXCEPT if (staged_) { std::unique_lock extent_lock(extent_mutex_); - while (!is_zero(ring_size_)) + const auto head = ring_head_.load(std::memory_order_relaxed); + auto size = ring_size_.load(std::memory_order_relaxed); + + while (!is_zero(size)) { - auto& tail = ring_.at((ring_head_ + ring_size_ - one) % extents); - if (tail.start >= count) + auto& tail = ring_.at((head + sub1(size)) % extents); + const auto start = tail.start.load(std::memory_order_relaxed); + if (start >= count) { - --ring_size_; + --size; continue; } - if (system::ceilinged_add(tail.start, tail.count) > count) + if (system::ceilinged_add(start, tail.count) > count) { - tail.count = count - tail.start; - tail.outstanding = std::min(tail.outstanding, - tail.count * columns); + tail.count = count - start; + const auto limit = tail.count * columns; + if (tail.outstanding.load(std::memory_order_relaxed) > limit) + tail.outstanding.store(limit, std::memory_order_relaxed); } break; } - cursor_ = ring_head_; - frontier_.store(is_zero(ring_size_) ? count : - ring_.at(ring_head_).start); + ring_size_.store(size, std::memory_order_release); + frontier_.store(is_zero(size) ? count : + ring_.at(head).start.load(std::memory_order_relaxed)); } #endif diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index db4dd812b..f9b96b69f 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -241,6 +241,7 @@ class mmap // staging utilities, not thread safe. void record_(size_t start, size_t count) NOEXCEPT; + void maintain_() NOEXCEPT; bool advise_(uint8_t* map, size_t size) const NOEXCEPT; size_t to_reservation(size_t rows) const NOEXCEPT; size_t page_floor(size_t bytes) const NOEXCEPT; @@ -274,21 +275,21 @@ class mmap static constexpr size_t extents = 4096; struct extent { - size_t start; + std::atomic start; size_t count; - size_t outstanding; + std::atomic outstanding; }; // These are thread safe (atomic). std::atomic settled_{}; std::atomic frontier_{}; + std::atomic ring_head_{}; + std::atomic ring_size_{}; - // These are protected by extent_mutex_. + // These are protected by extent_mutex_ (ring entries are immobile, put + // under the mutex and published by release, read/decremented lock-free). std::array reserved_{}; std::array ring_{}; - size_t ring_head_{}; - size_t ring_size_{}; - size_t cursor_{}; size_t page_{}; mutable std::mutex extent_mutex_{}; #endif // MANAGE_STAGING From 3aa915c27fdf307fb9ac721ec3104f400dd6c61c Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 25 Jul 2026 17:01:07 -0400 Subject: [PATCH 04/12] Posix memory map settle dirty pages. --- include/bitcoin/database/impl/memory/mmap.ipp | 5 + .../database/impl/memory/mmap_private.ipp | 26 ++- .../database/impl/memory/mmap_staging.ipp | 180 +++++++++++++++++- .../database/impl/memory/mmap_storage.ipp | 25 ++- include/bitcoin/database/memory/mmap.hpp | 27 ++- include/bitcoin/database/memory/mstage.hpp | 3 + src/memory/mstage.cpp | 12 ++ 7 files changed, 258 insertions(+), 20 deletions(-) diff --git a/include/bitcoin/database/impl/memory/mmap.ipp b/include/bitcoin/database/impl/memory/mmap.ipp index 65ee5c3e9..e3e67bdbe 100644 --- a/include/bitcoin/database/impl/memory/mmap.ipp +++ b/include/bitcoin/database/impl/memory/mmap.ipp @@ -61,6 +61,11 @@ CLASS::mmap(const paths& filenames, size_t minimum, size_t expansion, TEMPLATE CLASS::~mmap() NOEXCEPT { +#if defined(MANAGE_STAGING) + // Join a settler left running by an unload bypass (thread safety). + settler_stop_(); +#endif + BC_ASSERT(!loaded_.load()); BC_ASSERT(is_zero(logical_.load())); BC_ASSERT(is_zero(capacity_.load())); diff --git a/include/bitcoin/database/impl/memory/mmap_private.ipp b/include/bitcoin/database/impl/memory/mmap_private.ipp index b5a3800e4..41b2523c6 100644 --- a/include/bitcoin/database/impl/memory/mmap_private.ipp +++ b/include/bitcoin/database/impl/memory/mmap_private.ipp @@ -20,6 +20,7 @@ #define LIBBITCOIN_DATABASE_MEMORY_MMAP_PRIVATE_IPP #include +#include #include #include #include @@ -58,8 +59,7 @@ bool CLASS::map_all_(std::index_sequence) NOEXCEPT } page_ = page; - ring_head_.store(zero); - ring_size_.store(zero); + window_.store(zero); settled_.store(staged_ ? logical_.load() : zero); frontier_.store(staged_ ? logical_.load() : zero); #endif @@ -83,8 +83,26 @@ bool CLASS::unmap_all_(std::index_sequence) NOEXCEPT capacity_.store(zero); #if defined(MANAGE_STAGING) - ring_head_.store(zero); - ring_size_.store(zero); + // TEMPORARY DIAGNOSTIC (not for commit): report stalled rings at unload. + const auto [head, size] = system::unpack_word(window_.load()); + if (staged_ && (!is_zero(size) || !is_zero(missed_.load()))) + { + std::fprintf(stderr, "[settle-diag] %s ring=%llu frontier=%zu " + "settled=%zu logical=%zu missed=%zu\n", + filenames_.front().string().c_str(), + static_cast(size), frontier_.load(), + settled_.load(), logical_.load(), missed_.load()); + + for (size_t index = 0; index < std::min(size, 3u); ++index) + { + const auto& record = ring_.at((head + index) % extents); + std::fprintf(stderr, "[settle-diag] extent[%zu] start=%zu " + "count=%zu out=%zu\n", index, record.start.load(), + record.count, record.outstanding.load()); + } + } + + window_.store(zero); settled_.store(zero); frontier_.store(zero); #endif diff --git a/include/bitcoin/database/impl/memory/mmap_staging.ipp b/include/bitcoin/database/impl/memory/mmap_staging.ipp index 6c6baf4bd..547a5ce9a 100644 --- a/include/bitcoin/database/impl/memory/mmap_staging.ipp +++ b/include/bitcoin/database/impl/memory/mmap_staging.ipp @@ -20,9 +20,11 @@ #define LIBBITCOIN_DATABASE_MEMORY_MMAP_STAGING_IPP #include +#include #include #include #include +#include namespace libbitcoin { namespace database { @@ -45,9 +47,10 @@ void CLASS::complete(size_t if (!staged_ || is_zero(count)) return; - // Acquire-ordered window snapshot (published entries are immobile). - const auto head = ring_head_.load(std::memory_order_acquire); - const auto size = ring_size_.load(std::memory_order_acquire); + // Acquire-ordered window snapshot (published entries are immobile; the + // packed word precludes observing a torn head/size pair across pops). + const auto [head, size] = system::unpack_word( + window_.load(std::memory_order_acquire)); // Lock-free binary search of the sorted window for the covering extent. size_t low{}; @@ -92,6 +95,39 @@ void CLASS::complete(size_t return; } + + // Contention can record extents out of start order (allocation claim and + // recording are not one atomic step), transiently breaking search order. + // Contiguity guarantees a unique cover, so fall back to a linear scan. + for (size_t index = 0; index < size; ++index) + { + auto& record = ring_.at((head + index) % extents); + const auto start = record.start.load(std::memory_order_relaxed); + + if ((offset < start) || (offset >= (start + record.count))) + continue; + + const auto previous = record.outstanding.fetch_sub(count, + std::memory_order_relaxed); + + if (record.start.load(std::memory_order_relaxed) != start) + { + record.outstanding.fetch_add(count, std::memory_order_relaxed); + return; + } + + if (previous == count) + { + std::unique_lock extent_lock(extent_mutex_, std::try_to_lock); + if (extent_lock.owns_lock()) + maintain_(); + } + + return; + } + + // TEMPORARY DIAGNOSTIC (not for commit): count uncovered completions. + missed_.fetch_add(one, std::memory_order_relaxed); #endif } @@ -118,19 +154,20 @@ void CLASS::record_(size_t start, size_t count) NOEXCEPT maintain_(); // Saturation stalls the frontier (conservative, safe); asserts in debug. - const auto size = ring_size_.load(std::memory_order_relaxed); + const auto [head, size] = system::unpack_word( + window_.load(std::memory_order_relaxed)); BC_ASSERT(size < extents); if (size == extents) return; - const auto head = ring_head_.load(std::memory_order_relaxed); auto& record = ring_.at((head + size) % extents); record.start.store(start, std::memory_order_relaxed); record.count = count; record.outstanding.store(count * columns, std::memory_order_relaxed); // Publish the extent (pairs with the acquire window snapshot). - ring_size_.store(add1(size), std::memory_order_release); + window_.store(system::pack_word(head, add1(size)), + std::memory_order_release); if (is_zero(size)) frontier_.store(start); @@ -140,8 +177,8 @@ void CLASS::record_(size_t start, size_t count) NOEXCEPT TEMPLATE void CLASS::maintain_() NOEXCEPT { - auto head = ring_head_.load(std::memory_order_relaxed); - auto size = ring_size_.load(std::memory_order_relaxed); + auto [head, size] = system::unpack_word( + window_.load(std::memory_order_relaxed)); while (!is_zero(size) && is_zero(ring_.at(head).outstanding.load(std::memory_order_relaxed))) @@ -150,8 +187,8 @@ void CLASS::maintain_() NOEXCEPT --size; } - ring_head_.store(head, std::memory_order_relaxed); - ring_size_.store(size, std::memory_order_release); + window_.store(system::pack_word(head, size), + std::memory_order_release); frontier_.store(is_zero(size) ? logical_.load() : ring_.at(head).start.load(std::memory_order_relaxed)); } @@ -460,6 +497,129 @@ size_t CLASS::page_ceiling(size_t bytes) const NOEXCEPT return page_floor(system::ceilinged_add(bytes, sub1(page_))); } + + +// settle scheduler, instance-owned (drains completed writes to clean cache). +// ---------------------------------------------------------------------------- +// private + +TEMPLATE +void CLASS::settler_start_() NOEXCEPT +{ + if (!staged_) + return; + + settling_.store(true); + settler_ = std::thread([this]() NOEXCEPT { settler_run_(); }); +} + +TEMPLATE +void CLASS::settler_stop_() NOEXCEPT +{ + if (!settling_.exchange(false)) + return; + + settler_cv_.notify_all(); + if (settler_.joinable()) + settler_.join(); +} + +// Pressure-paced draining (the windows model): writers are never blocked, +// drain intensity follows memory conditions, settled pages remain cached. +TEMPLATE +void CLASS::settler_run_() NOEXCEPT +{ + // Tiers derive from physical memory and pressure (no configuration). + const auto memory = system_memory(); + const auto urgent = memory / 4; + const auto active = memory / 32; + const auto chunk = std::max(one, (size_t{ 256 } << 20) / stride); + + const auto backlog = [this]() NOEXCEPT + { + return system::ceilinged_multiply(system::floored_subtract( + frontier_.load(), settled_.load()), stride); + }; + + while (settling_.load()) + { + std::unique_lock settler_lock(settler_mutex_); + settler_cv_.wait_for(settler_lock, std::chrono::seconds(1)); + settler_lock.unlock(); + + if (!settling_.load()) + return; + + auto bytes = backlog(); + if (is_zero(bytes)) + continue; + + // Urgency drains continuously, activity one chunk per tick. + const auto driven = (system_pressure() > one) || (bytes > urgent); + if (!driven && (bytes <= active)) + continue; + + do + { + if (!settle_next_(chunk)) + break; + + bytes = backlog(); + } + while (settling_.load() && driven && (bytes > active)); + } +} + +// Settle up to chunk completed rows: write under the shared remap lock +// (completed extents are immutable, writers proceed), convert under a brief +// exclusive. Durability remains a snapshot property (no sync here). +TEMPLATE +bool CLASS::settle_next_(size_t chunk) NOEXCEPT +{ + size_t from{}; + size_t target{}; + + { + std::shared_lock map_lock(remap_mutex_); + if (!loaded_.load() || fault_.load()) + return false; + + from = settled_.load(); + target = std::min(frontier_.load(), + system::ceilinged_add(from, chunk)); + + if (target <= from) + return false; + + if (!settle_write_(from, target, sequence{})) + { + set_first_code(error::fsync_failure); + return false; + } + } + + std::unique_lock map_lock(remap_mutex_); + if (!loaded_.load()) + return false; + + // A raced settle boundary (flush) invalidates only this conversion. + if (settled_.load() != from) + return true; + + return settle_all_(target, sequence{}); +} + +TEMPLATE +template +bool CLASS::settle_write_(size_t from, size_t to, + std::index_sequence) NOEXCEPT +{ + return (pwrite_all(opened_[Index], + std::next(memory_map_[Index], to_width(from)), + to_width(to) - to_width(from), + to_width(from)) && ...); +} + #endif // MANAGE_STAGING } // namespace database diff --git a/include/bitcoin/database/impl/memory/mmap_storage.ipp b/include/bitcoin/database/impl/memory/mmap_storage.ipp index 277319fae..5bde84914 100644 --- a/include/bitcoin/database/impl/memory/mmap_storage.ipp +++ b/include/bitcoin/database/impl/memory/mmap_storage.ipp @@ -143,6 +143,11 @@ code CLASS::load() NOEXCEPT } remap_mutex_.unlock(); + +#if defined(MANAGE_STAGING) + settler_start_(); +#endif + return error::success; } @@ -216,6 +221,10 @@ code CLASS::flush() NOEXCEPT TEMPLATE code CLASS::unload() NOEXCEPT { +#if defined(MANAGE_STAGING) + settler_stop_(); +#endif + std::unique_lock field_lock(field_mutex_); if (remap_mutex_.try_lock()) @@ -244,6 +253,10 @@ code CLASS::unload() NOEXCEPT TEMPLATE code CLASS::shrink() NOEXCEPT { +#if defined(MANAGE_STAGING) + settler_stop_(); +#endif + std::unique_lock field_lock(field_mutex_); if (remap_mutex_.try_lock()) @@ -269,6 +282,11 @@ code CLASS::shrink() NOEXCEPT } remap_mutex_.unlock(); + +#if defined(MANAGE_STAGING) + settler_start_(); +#endif + return error::success; } @@ -324,8 +342,8 @@ bool CLASS::truncate(size_t count) NOEXCEPT if (staged_) { std::unique_lock extent_lock(extent_mutex_); - const auto head = ring_head_.load(std::memory_order_relaxed); - auto size = ring_size_.load(std::memory_order_relaxed); + auto [head, size] = system::unpack_word( + window_.load(std::memory_order_relaxed)); while (!is_zero(size)) { @@ -348,7 +366,8 @@ bool CLASS::truncate(size_t count) NOEXCEPT break; } - ring_size_.store(size, std::memory_order_release); + window_.store(system::pack_word(head, size), + std::memory_order_release); frontier_.store(is_zero(size) ? count : ring_.at(head).start.load(std::memory_order_relaxed)); } diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index f9b96b69f..19e052042 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -20,9 +20,11 @@ #define LIBBITCOIN_DATABASE_MEMORY_MMAP_HPP #include +#include #include #include #include +#include #include #include #include @@ -242,6 +244,15 @@ class mmap // staging utilities, not thread safe. void record_(size_t start, size_t count) NOEXCEPT; void maintain_() NOEXCEPT; + + // settle scheduler (instance-owned thread, load/unload lifecycle). + void settler_start_() NOEXCEPT; + void settler_stop_() NOEXCEPT; + void settler_run_() NOEXCEPT; + bool settle_next_(size_t chunk) NOEXCEPT; + template + bool settle_write_(size_t from, size_t to, + std::index_sequence) NOEXCEPT; bool advise_(uint8_t* map, size_t size) const NOEXCEPT; size_t to_reservation(size_t rows) const NOEXCEPT; size_t page_floor(size_t bytes) const NOEXCEPT; @@ -280,11 +291,15 @@ class mmap std::atomic outstanding; }; - // These are thread safe (atomic). + // These are thread safe (atomic). The ring window packs head and size + // into one word (pack_word) so lock-free readers cannot observe a torn + // window across pops. std::atomic settled_{}; std::atomic frontier_{}; - std::atomic ring_head_{}; - std::atomic ring_size_{}; + std::atomic window_{}; + + // TEMPORARY DIAGNOSTIC (not for commit): completions with no extent. + std::atomic missed_{}; // These are protected by extent_mutex_ (ring entries are immobile, put // under the mutex and published by release, read/decremented lock-free). @@ -292,6 +307,12 @@ class mmap std::array ring_{}; size_t page_{}; mutable std::mutex extent_mutex_{}; + + // These are protected by settler_mutex_ (thread joined by stop). + std::thread settler_{}; + std::condition_variable settler_cv_{}; + std::atomic_bool settling_{}; + mutable std::mutex settler_mutex_{}; #endif // MANAGE_STAGING }; diff --git a/include/bitcoin/database/memory/mstage.hpp b/include/bitcoin/database/memory/mstage.hpp index 61c9f2d47..71b36f8e0 100644 --- a/include/bitcoin/database/memory/mstage.hpp +++ b/include/bitcoin/database/memory/mstage.hpp @@ -39,6 +39,9 @@ int mmap_settle(void* address, size_t size, int fd, size_t offset) NOEXCEPT; /// Replace settled pages with committed anonymous memory (contents undefined). int mmap_unsettle(void* address, size_t size) NOEXCEPT; +/// System memory pressure level (1 normal, 2 warning, 4 critical, 0 failure). +size_t system_pressure() NOEXCEPT; + /// Full-transfer positional file read/write (false on failure or early eof). bool pread_all(int fd, uint8_t* to, size_t size, size_t offset) NOEXCEPT; bool pwrite_all(int fd, const uint8_t* from, size_t size, diff --git a/src/memory/mstage.cpp b/src/memory/mstage.cpp index 02cbc13ba..ebfbfb814 100644 --- a/src/memory/mstage.cpp +++ b/src/memory/mstage.cpp @@ -25,8 +25,20 @@ #include #include +#include #include +size_t system_pressure() NOEXCEPT +{ + int level{}; + auto size = sizeof(level); + if (::sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &size, + nullptr, 0) != 0) + return {}; + + return static_cast(level); +} + void* mmap_reserve(size_t size) NOEXCEPT { From 236dc8f17dbd1dfab29110323cc4ffb9bd43e18d Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 25 Jul 2026 22:08:22 -0400 Subject: [PATCH 05/12] Posix memory map remove diagnostics. --- .../database/impl/memory/mmap_private.ipp | 20 ------------------- .../database/impl/memory/mmap_staging.ipp | 3 --- include/bitcoin/database/memory/mmap.hpp | 3 --- 3 files changed, 26 deletions(-) diff --git a/include/bitcoin/database/impl/memory/mmap_private.ipp b/include/bitcoin/database/impl/memory/mmap_private.ipp index 41b2523c6..d6932350b 100644 --- a/include/bitcoin/database/impl/memory/mmap_private.ipp +++ b/include/bitcoin/database/impl/memory/mmap_private.ipp @@ -20,7 +20,6 @@ #define LIBBITCOIN_DATABASE_MEMORY_MMAP_PRIVATE_IPP #include -#include #include #include #include @@ -83,25 +82,6 @@ bool CLASS::unmap_all_(std::index_sequence) NOEXCEPT capacity_.store(zero); #if defined(MANAGE_STAGING) - // TEMPORARY DIAGNOSTIC (not for commit): report stalled rings at unload. - const auto [head, size] = system::unpack_word(window_.load()); - if (staged_ && (!is_zero(size) || !is_zero(missed_.load()))) - { - std::fprintf(stderr, "[settle-diag] %s ring=%llu frontier=%zu " - "settled=%zu logical=%zu missed=%zu\n", - filenames_.front().string().c_str(), - static_cast(size), frontier_.load(), - settled_.load(), logical_.load(), missed_.load()); - - for (size_t index = 0; index < std::min(size, 3u); ++index) - { - const auto& record = ring_.at((head + index) % extents); - std::fprintf(stderr, "[settle-diag] extent[%zu] start=%zu " - "count=%zu out=%zu\n", index, record.start.load(), - record.count, record.outstanding.load()); - } - } - window_.store(zero); settled_.store(zero); frontier_.store(zero); diff --git a/include/bitcoin/database/impl/memory/mmap_staging.ipp b/include/bitcoin/database/impl/memory/mmap_staging.ipp index 547a5ce9a..a68742326 100644 --- a/include/bitcoin/database/impl/memory/mmap_staging.ipp +++ b/include/bitcoin/database/impl/memory/mmap_staging.ipp @@ -125,9 +125,6 @@ void CLASS::complete(size_t return; } - - // TEMPORARY DIAGNOSTIC (not for commit): count uncovered completions. - missed_.fetch_add(one, std::memory_order_relaxed); #endif } diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index 19e052042..68fbdfbdf 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -298,9 +298,6 @@ class mmap std::atomic frontier_{}; std::atomic window_{}; - // TEMPORARY DIAGNOSTIC (not for commit): completions with no extent. - std::atomic missed_{}; - // These are protected by extent_mutex_ (ring entries are immobile, put // under the mutex and published by release, read/decremented lock-free). std::array reserved_{}; From f7c1a91c08975e598b4f878cb230efc31fd3cae2 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 25 Jul 2026 23:05:27 -0400 Subject: [PATCH 06/12] Posix memory map fix disk full gap. --- .../bitcoin/database/impl/memory/mmap_staging.ipp | 13 +++++++++++++ .../bitcoin/database/impl/memory/mmap_storage.ipp | 7 +++++++ .../bitcoin/database/impl/store/store_reload.ipp | 7 +++++-- include/bitcoin/database/memory/mmap.hpp | 1 + 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/include/bitcoin/database/impl/memory/mmap_staging.ipp b/include/bitcoin/database/impl/memory/mmap_staging.ipp index a68742326..71bafa6fc 100644 --- a/include/bitcoin/database/impl/memory/mmap_staging.ipp +++ b/include/bitcoin/database/impl/memory/mmap_staging.ipp @@ -190,6 +190,19 @@ void CLASS::maintain_() NOEXCEPT ring_.at(head).start.load(std::memory_order_relaxed)); } +// Discard all extents, requires quiescent writers (locked). Any extent then +// outstanding is an abandoned write (unreferenced), safe to settle as is. +TEMPLATE +void CLASS::discard_() NOEXCEPT +{ + if (!staged_) + return; + + std::unique_lock extent_lock(extent_mutex_); + window_.store(zero, std::memory_order_release); + frontier_.store(logical_.load()); +} + // staging dispatch, not thread safe. // ---------------------------------------------------------------------------- // private diff --git a/include/bitcoin/database/impl/memory/mmap_storage.ipp b/include/bitcoin/database/impl/memory/mmap_storage.ipp index 5bde84914..918a6dd94 100644 --- a/include/bitcoin/database/impl/memory/mmap_storage.ipp +++ b/include/bitcoin/database/impl/memory/mmap_storage.ipp @@ -168,6 +168,13 @@ code CLASS::reload() NOEXCEPT return error::reload_unloaded; } +#if defined(MANAGE_STAGING) + // The store reload transactor implies quiescent writers, so remaining + // extents are orphans of abandoned writes, stalling the frontier (in + // any table, not just those that faulted). Discard to unjam settling. + discard_(); +#endif + // Allow resume from disk full. set_disk_space(zero); diff --git a/include/bitcoin/database/impl/store/store_reload.ipp b/include/bitcoin/database/impl/store/store_reload.ipp index 35f727ee7..57bcc1ec9 100644 --- a/include/bitcoin/database/impl/store/store_reload.ipp +++ b/include/bitcoin/database/impl/store/store_reload.ipp @@ -40,13 +40,16 @@ code CLASS::reload(const event_handler& handler) NOEXCEPT { if (!ec) { - // If any storage has a fault it will return as failure code. if (to_bool(file.get_space())) { handler(event_t::load_file, table); - ec = file.reload(); this->dirty_.store(true, std::memory_order_relaxed); } + + // Reload all storages, as staged instances must discard extents + // orphaned by abandoned writes (which may span into tables that + // did not themselves fault). Returns any storage fault as failure. + ec = file.reload(); } }; diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index 68fbdfbdf..d3cdb3100 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -244,6 +244,7 @@ class mmap // staging utilities, not thread safe. void record_(size_t start, size_t count) NOEXCEPT; void maintain_() NOEXCEPT; + void discard_() NOEXCEPT; // settle scheduler (instance-owned thread, load/unload lifecycle). void settler_start_() NOEXCEPT; From a1e5dd5409016ac2a8bb31794313d300a029f482 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 25 Jul 2026 23:56:54 -0400 Subject: [PATCH 07/12] Posix memory map throttle writers under pressure. --- include/bitcoin/database/impl/memory/mmap.ipp | 5 +++ .../database/impl/memory/mmap_staging.ipp | 39 ++++++++++++++++++- .../database/impl/memory/mmap_storage.ipp | 5 +++ include/bitcoin/database/memory/mmap.hpp | 8 ++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/include/bitcoin/database/impl/memory/mmap.ipp b/include/bitcoin/database/impl/memory/mmap.ipp index e3e67bdbe..b2cbc8ade 100644 --- a/include/bitcoin/database/impl/memory/mmap.ipp +++ b/include/bitcoin/database/impl/memory/mmap.ipp @@ -114,6 +114,11 @@ void CLASS::set_first_code(const error::error_t& ec) NOEXCEPT // error is atomic for public read exposure. error_.store(ec); + +#if defined(MANAGE_STAGING) + // A fault may stop draining, so it must release throttled callers. + signal_(); +#endif } } diff --git a/include/bitcoin/database/impl/memory/mmap_staging.ipp b/include/bitcoin/database/impl/memory/mmap_staging.ipp index 71bafa6fc..7326c669b 100644 --- a/include/bitcoin/database/impl/memory/mmap_staging.ipp +++ b/include/bitcoin/database/impl/memory/mmap_staging.ipp @@ -203,6 +203,37 @@ void CLASS::discard_() NOEXCEPT frontier_.store(logical_.load()); } +// Delay the caller while staging exceeds its memory bound (write throttle). +// Signaled as the settler drains; a fault or settler stop releases the caller +// into the normal allocation path (which fails on fault/unload). The relieved +// state is lock-free for the common (unparked) case; notifiers synchronize on +// throttle_mutex_ so a parked caller cannot miss its signal. +TEMPLATE +void CLASS::throttle_() NOEXCEPT +{ + const auto relieved = [this]() NOEXCEPT + { + return (system::ceilinged_multiply(system::floored_subtract( + logical_.load(), settled_.load()), stride) <= limit_) || + !settling_.load() || fault_.load(); + }; + + if (!staged_ || relieved()) + return; + + std::unique_lock throttle_lock(throttle_mutex_); + throttle_cv_.wait(throttle_lock, relieved); +} + +// Wake throttled callers, synchronized so a parking caller cannot miss the +// signal between its relieved check and its wait. +TEMPLATE +void CLASS::signal_() NOEXCEPT +{ + std::unique_lock throttle_lock(throttle_mutex_); + throttle_cv_.notify_all(); +} + // staging dispatch, not thread safe. // ---------------------------------------------------------------------------- // private @@ -216,6 +247,7 @@ bool CLASS::settle_all_(size_t rows, std::index_sequence) NOEXCEPT return false; settled_.store(rows); + signal_(); return true; } @@ -519,6 +551,7 @@ void CLASS::settler_start_() NOEXCEPT if (!staged_) return; + limit_ = system_memory() / 8; settling_.store(true); settler_ = std::thread([this]() NOEXCEPT { settler_run_(); }); } @@ -529,13 +562,15 @@ void CLASS::settler_stop_() NOEXCEPT if (!settling_.exchange(false)) return; + signal_(); settler_cv_.notify_all(); if (settler_.joinable()) settler_.join(); } -// Pressure-paced draining (the windows model): writers are never blocked, -// drain intensity follows memory conditions, settled pages remain cached. +// Pressure-paced draining (the windows model): drain intensity follows memory +// conditions, settled pages remain cached, writers delay only at the staging +// memory bound (write throttle). TEMPLATE void CLASS::settler_run_() NOEXCEPT { diff --git a/include/bitcoin/database/impl/memory/mmap_storage.ipp b/include/bitcoin/database/impl/memory/mmap_storage.ipp index 918a6dd94..3774a8d71 100644 --- a/include/bitcoin/database/impl/memory/mmap_storage.ipp +++ b/include/bitcoin/database/impl/memory/mmap_storage.ipp @@ -441,6 +441,11 @@ bool CLASS::reserve(size_t count) NOEXCEPT TEMPLATE size_t CLASS::allocate(size_t count) NOEXCEPT { +#if defined(MANAGE_STAGING) + // Nothing is held here, so parking cannot deadlock (as with remap waits). + throttle_(); +#endif + // Fast path: claim rows within published capacity (no locks). A failed // exchange implies another claim succeeded, so every retry is progress. for (auto start = logical_.load();;) diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index d3cdb3100..4ecafd11c 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -245,6 +245,8 @@ class mmap void record_(size_t start, size_t count) NOEXCEPT; void maintain_() NOEXCEPT; void discard_() NOEXCEPT; + void throttle_() NOEXCEPT; + void signal_() NOEXCEPT; // settle scheduler (instance-owned thread, load/unload lifecycle). void settler_start_() NOEXCEPT; @@ -311,6 +313,12 @@ class mmap std::condition_variable settler_cv_{}; std::atomic_bool settling_{}; mutable std::mutex settler_mutex_{}; + + // These delay allocation while staging exceeds its memory bound (write + // throttle). The bound is set at load, before writers exist. + size_t limit_{}; + std::condition_variable throttle_cv_{}; + mutable std::mutex throttle_mutex_{}; #endif // MANAGE_STAGING }; From 2817fb43e53b255ac37a6468df7dbbf1c8c09c3c Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 26 Jul 2026 00:04:48 -0400 Subject: [PATCH 08/12] Posix memory map trickle writes under lack of pressure. --- .../database/impl/memory/mmap_staging.ipp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/include/bitcoin/database/impl/memory/mmap_staging.ipp b/include/bitcoin/database/impl/memory/mmap_staging.ipp index 7326c669b..041961d08 100644 --- a/include/bitcoin/database/impl/memory/mmap_staging.ipp +++ b/include/bitcoin/database/impl/memory/mmap_staging.ipp @@ -570,7 +570,8 @@ void CLASS::settler_stop_() NOEXCEPT // Pressure-paced draining (the windows model): drain intensity follows memory // conditions, settled pages remain cached, writers delay only at the staging -// memory bound (write throttle). +// memory bound (write throttle), sustained stillness drains the residual (the +// lazy writer) so a quiescent map converges to fully settled. TEMPLATE void CLASS::settler_run_() NOEXCEPT { @@ -580,6 +581,12 @@ void CLASS::settler_run_() NOEXCEPT const auto active = memory / 32; const auto chunk = std::max(one, (size_t{ 256 } << 20) / stride); + // Ticks without allocation before idle draining (settling is not writing, + // so draining does not hold its own clock). + constexpr size_t rest = 60; + auto mark = logical_.load(); + size_t still{}; + const auto backlog = [this]() NOEXCEPT { return system::ceilinged_multiply(system::floored_subtract( @@ -595,13 +602,17 @@ void CLASS::settler_run_() NOEXCEPT if (!settling_.load()) return; + const auto top = logical_.load(); + still = (top == mark) ? std::min(add1(still), rest) : zero; + mark = top; + auto bytes = backlog(); if (is_zero(bytes)) continue; - // Urgency drains continuously, activity one chunk per tick. + // Urgency drains continuously, activity/stillness one chunk per tick. const auto driven = (system_pressure() > one) || (bytes > urgent); - if (!driven && (bytes <= active)) + if (!driven && (bytes <= active) && (still < rest)) continue; do From b66ec51ccf2524ce8117cea051939a110af4a7a1 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 26 Jul 2026 00:40:07 -0400 Subject: [PATCH 09/12] Posix memory map remove dead code (macOS hacks). --- .../database/impl/memory/mmap_private.ipp | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/include/bitcoin/database/impl/memory/mmap_private.ipp b/include/bitcoin/database/impl/memory/mmap_private.ipp index d6932350b..bd4dd12e5 100644 --- a/include/bitcoin/database/impl/memory/mmap_private.ipp +++ b/include/bitcoin/database/impl/memory/mmap_private.ipp @@ -126,12 +126,8 @@ bool CLASS::flush_(size_t const auto success = ((from >= to) || pwrite_all(opened_[Column], std::next(memory_map_[Column], from), to - from, from)) -#if defined(F_FULLFSYNC) // non-standard macOS behavior: news.ycombinator.com/item?id=30372218 && (::fcntl(opened_[Column], F_FULLFSYNC, 0) != fail); -#else - && (::fsync(opened_[Column]) != fail); -#endif #elif defined(HAVE_MSC) // unmap (and therefore msync) must be called before ftruncate. // "To flush all the dirty pages plus the metadata for the file and ensure @@ -140,10 +136,6 @@ bool CLASS::flush_(size_t const auto success = (::msync(memory_map_[Column], size, MS_SYNC) != fail) && (::fsync(opened_[Column]) != fail); -#elif defined(F_FULLFSYNC) - // macOS msync fails with zero logical size (but we are no longer calling). - // non-standard macOS behavior: news.ycombinator.com/item?id=30372218 - const auto success = ::fcntl(opened_[Column], F_FULLFSYNC, 0) != fail; #else // msync should not be required on modern linux, see linus et al. // stackoverflow.com/questions/5902629/mmap-msync-and-linux-process-termination @@ -198,11 +190,7 @@ bool CLASS::unmap_(size_t ((from >= logical) || pwrite_all(opened_[Column], std::next(memory_map_[Column], from), logical - from, from)) && (::ftruncate(opened_[Column], logical) != fail) -#if defined(F_FULLFSYNC) && (::fcntl(opened_[Column], F_FULLFSYNC, 0) != fail); -#else - && (::fsync(opened_[Column]) != fail); -#endif // Order ensures release of the reservation in case of transfer failure. const auto success = (::munmap(memory_map_[Column], @@ -224,11 +212,7 @@ bool CLASS::unmap_(size_t // POSIX permits resizing a mapped file. const auto truncated = (::ftruncate(opened_[Column], logical) != fail) -#if defined(F_FULLFSYNC) - && (::fcntl(opened_[Column], F_FULLFSYNC, 0) != fail); -#else && (::fsync(opened_[Column]) != fail); -#endif // Order ensures release in case of truncate failure. const auto success = release_(size) && truncated; @@ -286,47 +270,20 @@ bool CLASS::remap_(size_t size) NOEXCEPT return false; return commit_(size); -#else -#if !defined(HAVE_MSC) && !defined(MREMAP_MAYMOVE) - // macOS cannot remap in place, so release the mapping without trimming and - // the file remains at capacity_ bytes and resize_'s fallocate delta (and - // the fallocate shim's preallocation window) are exact by construction. - if (!release_(capacity_.load())) - return false; - - if (!resize_(size)) - { - map_(); - return false; - } #else if (!resize_(size)) return false; -#endif #if defined(HAVE_MSC) - // mman-win32 mremap hack (umap/map) requires flags and file descriptor. memory_map_[Column] = system::pointer_cast( ::mremap_(memory_map_[Column], to_width(capacity_.load()), to_width(size), PROT_READ | PROT_WRITE, MAP_SHARED, opened_[Column])); - -#elif defined(MREMAP_MAYMOVE) - +#else memory_map_[Column] = system::pointer_cast( ::mremap(memory_map_[Column], to_width(capacity_.load()), to_width(size), MREMAP_MAYMOVE)); - -#else - - // macOS does not define mremap or MREMAP_MAYMOVE. The prior mapping was - // released above and the resized file is mapped fresh. - // TODO: see "MREMAP_MAYMOVE" in sqlite for map extension technique. - memory_map_[Column] = system::pointer_cast( - ::mmap(nullptr, to_width(size), PROT_READ | PROT_WRITE, - MAP_SHARED, opened_[Column], 0)); - #endif return finalize_(size); From 7f94eb372d461083b5b5d40d418cff8e861bdc25 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 26 Jul 2026 00:54:09 -0400 Subject: [PATCH 10/12] Posix memory map fix custom fallocate regression. --- src/memory/mman.cpp | 79 +++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/src/memory/mman.cpp b/src/memory/mman.cpp index 37edd1c36..a3710a608 100644 --- a/src/memory/mman.cpp +++ b/src/memory/mman.cpp @@ -288,43 +288,66 @@ int sysconf(int) noexcept int fallocate(int fd, int, off_t offset, off_t len) NOEXCEPT { constexpr auto fail = -1; + const auto target = offset + len; - fstore_t store + // Physically allocated bytes (st_blocks is in 512 byte units). + const auto allocated = [fd]() NOEXCEPT -> off_t { - // Prefer contiguous allocation - .fst_flags = F_ALLOCATECONTIG, - - // Allocate from EOF - .fst_posmode = F_PEOFPOSMODE, - - // Ignored for allocation in PEOF mode. - .fst_offset = 0, - - // Delta size - .fst_length = len, - - // Output: actual bytes allocated - .fst_bytesalloc = 0 + struct stat status{}; + return (::fstat(fd, &status) == fail) ? fail : + (status.st_blocks * 512); }; - // Try contiguous allocation. - if (::fcntl(fd, F_PREALLOCATE, &store) == fail) + // Disk full recovery requires that a success fully reserves disk space, + // as writes must then never fail within reserved space. APFS spuriously + // fails F_PREALLOCATE (e.g. when logical EOF lies within blocks that a + // prior call preallocated), so neither its success nor failure is + // trusted: each pass is verified against physical allocation and the + // shortfall retried (F_PEOFPOSMODE allocates from the physical end, so + // progress is monotonic). Only a pass without progress is genuine + // exhaustion, reported as ENOSPC (the recoverable condition). The whole + // file check implies no holes because files grown only through this + // function are never sparse (extension lands in verified allocation). + for (auto current = allocated(); current < target;) { - // Fallback to non-contiguous. - store.fst_flags = F_ALLOCATEALL; - store.fst_bytesalloc = 0; - - // APFS spuriously fails F_PREALLOCATE (e.g. when logical EOF lies - // within blocks preallocated by a prior call), reporting ENOSPC - // despite ample space. Preallocation is therefore best-effort and - // failure is ignored: ftruncate is authoritative, and reports genuine - // exhaustion as its own ENOSPC (via sparse extension at worst). - ::fcntl(fd, F_PREALLOCATE, &store); + if (current == fail) + return fail; + + fstore_t store + { + // Prefer contiguous allocation, from physical EOF (offset + // ignored in PEOF mode), of the verified shortfall. + .fst_flags = F_ALLOCATECONTIG, + .fst_posmode = F_PEOFPOSMODE, + .fst_offset = 0, + .fst_length = target - current, + .fst_bytesalloc = 0 + }; + + // Fallback to non-contiguous allocation. + if (::fcntl(fd, F_PREALLOCATE, &store) == fail) + { + store.fst_flags = F_ALLOCATEALL; + store.fst_bytesalloc = 0; + ::fcntl(fd, F_PREALLOCATE, &store); + } + + const auto next = allocated(); + if (next == fail) + return fail; + + if (next <= current) + { + errno = ENOSPC; + return fail; + } + + current = next; } // Extend file to new size (required for mmap). This is not required on // Linux because fallocate(2) automatically extends file's logical size. - return ::ftruncate(fd, offset + len); + return ::ftruncate(fd, target); } #endif // HAVE_MSC From b34aea1c34164acf41289623f5150ea47a8bec48 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 26 Jul 2026 01:54:38 -0400 Subject: [PATCH 11/12] Posix memory map move system_pressure to utilities. --- include/bitcoin/database/memory/mstage.hpp | 3 -- include/bitcoin/database/memory/utilities.hpp | 4 +++ src/memory/mstage.cpp | 13 ------- src/memory/utilities.cpp | 35 +++++++++++++++++++ 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/include/bitcoin/database/memory/mstage.hpp b/include/bitcoin/database/memory/mstage.hpp index 71b36f8e0..61c9f2d47 100644 --- a/include/bitcoin/database/memory/mstage.hpp +++ b/include/bitcoin/database/memory/mstage.hpp @@ -39,9 +39,6 @@ int mmap_settle(void* address, size_t size, int fd, size_t offset) NOEXCEPT; /// Replace settled pages with committed anonymous memory (contents undefined). int mmap_unsettle(void* address, size_t size) NOEXCEPT; -/// System memory pressure level (1 normal, 2 warning, 4 critical, 0 failure). -size_t system_pressure() NOEXCEPT; - /// Full-transfer positional file read/write (false on failure or early eof). bool pread_all(int fd, uint8_t* to, size_t size, size_t offset) NOEXCEPT; bool pwrite_all(int fd, const uint8_t* from, size_t size, diff --git a/include/bitcoin/database/memory/utilities.hpp b/include/bitcoin/database/memory/utilities.hpp index a1bc60b81..90a9341ac 100644 --- a/include/bitcoin/database/memory/utilities.hpp +++ b/include/bitcoin/database/memory/utilities.hpp @@ -31,6 +31,10 @@ BCD_API size_t page_size() NOEXCEPT; /// The bytes of physical memory, zero if failed. BCD_API uint64_t system_memory() NOEXCEPT; +/// The system memory pressure level (1 normal, 2 warning, 4 critical), zero +/// if failed or the platform provides no source. +BCD_API size_t system_pressure() NOEXCEPT; + /// C++26: std::atomic::fetch_max template = true> Integral fetch_max(std::atomic& atomic, Integral value) NOEXCEPT diff --git a/src/memory/mstage.cpp b/src/memory/mstage.cpp index ebfbfb814..45fd61a96 100644 --- a/src/memory/mstage.cpp +++ b/src/memory/mstage.cpp @@ -25,21 +25,8 @@ #include #include -#include #include -size_t system_pressure() NOEXCEPT -{ - int level{}; - auto size = sizeof(level); - if (::sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &size, - nullptr, 0) != 0) - return {}; - - return static_cast(level); -} - - void* mmap_reserve(size_t size) NOEXCEPT { return ::mmap(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, diff --git a/src/memory/utilities.cpp b/src/memory/utilities.cpp index 8b80fe307..3b0bc7a00 100644 --- a/src/memory/utilities.cpp +++ b/src/memory/utilities.cpp @@ -23,6 +23,9 @@ #else #include #endif +#if defined(HAVE_APPLE) + #include +#endif #include namespace libbitcoin { @@ -48,6 +51,21 @@ uint64_t system_memory() NOEXCEPT status.ullTotalPhys; } +size_t system_pressure() NOEXCEPT +{ + // The kernel low memory resource signal (no configurable threshold). + BOOL low{ FALSE }; + const auto handle = CreateMemoryResourceNotification( + LowMemoryResourceNotification); + + if (handle == NULL) + return zero; + + const auto success = QueryMemoryResourceNotification(handle, &low) != 0; + CloseHandle(handle); + return !success ? zero : (low == FALSE ? size_t{ 1 } : size_t{ 4 }); +} + #else size_t page_size() NOEXCEPT @@ -77,6 +95,23 @@ uint64_t system_memory() NOEXCEPT possible_wide_cast(page_size())); } +size_t system_pressure() NOEXCEPT +{ +#if defined(HAVE_APPLE) + using namespace system; + int level{}; + auto size = sizeof(level); + if (::sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &size, + nullptr, 0) != 0) + return zero; + + return possible_narrow_sign_cast(level); +#else + // No source adopted (linux PSI /proc/pressure/memory when staged there). + return zero; +#endif +} + #endif } // namespace database From f97411cdbe33d3d136f59023878d23cccdccfa63 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 26 Jul 2026 02:55:18 -0400 Subject: [PATCH 12/12] Posix memory map extend to Linux (conditional compile). --- .../database/impl/memory/mmap_private.ipp | 8 +++++ src/memory/utilities.cpp | 32 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/bitcoin/database/impl/memory/mmap_private.ipp b/include/bitcoin/database/impl/memory/mmap_private.ipp index bd4dd12e5..e89c8968a 100644 --- a/include/bitcoin/database/impl/memory/mmap_private.ipp +++ b/include/bitcoin/database/impl/memory/mmap_private.ipp @@ -126,8 +126,12 @@ bool CLASS::flush_(size_t const auto success = ((from >= to) || pwrite_all(opened_[Column], std::next(memory_map_[Column], from), to - from, from)) +#if defined(F_FULLFSYNC) // non-standard macOS behavior: news.ycombinator.com/item?id=30372218 && (::fcntl(opened_[Column], F_FULLFSYNC, 0) != fail); +#else + && (::fsync(opened_[Column]) != fail); +#endif #elif defined(HAVE_MSC) // unmap (and therefore msync) must be called before ftruncate. // "To flush all the dirty pages plus the metadata for the file and ensure @@ -190,7 +194,11 @@ bool CLASS::unmap_(size_t ((from >= logical) || pwrite_all(opened_[Column], std::next(memory_map_[Column], from), logical - from, from)) && (::ftruncate(opened_[Column], logical) != fail) +#if defined(F_FULLFSYNC) && (::fcntl(opened_[Column], F_FULLFSYNC, 0) != fail); +#else + && (::fsync(opened_[Column]) != fail); +#endif // Order ensures release of the reservation in case of transfer failure. const auto success = (::munmap(memory_map_[Column], diff --git a/src/memory/utilities.cpp b/src/memory/utilities.cpp index 3b0bc7a00..c86b4d2ca 100644 --- a/src/memory/utilities.cpp +++ b/src/memory/utilities.cpp @@ -26,6 +26,10 @@ #if defined(HAVE_APPLE) #include #endif +#if defined(HAVE_LINUX) + #include + #include +#endif #include namespace libbitcoin { @@ -106,8 +110,34 @@ size_t system_pressure() NOEXCEPT return zero; return possible_narrow_sign_cast(level); +#elif defined(HAVE_LINUX) + // PSI (requires CONFIG_PSI): fraction of recent wall time that tasks + // stalled on memory reclaim. A tenth of time stalled is treated as + // genuine pressure, partial (some) as warning and total (full) as + // critical, mapping to the macos memorystatus level semantics. + auto level = zero; + if (const auto file = std::fopen("/proc/pressure/memory", "r")) + { + char line[128]; + double avg10{}; + level = one; + while (!is_null(std::fgets(line, sizeof(line), file))) + { + if ((std::sscanf(line, "some avg10=%lf", &avg10) == 1) && + (avg10 >= 10.0)) + level = std::max(level, size_t{ 2 }); + + if ((std::sscanf(line, "full avg10=%lf", &avg10) == 1) && + (avg10 >= 10.0)) + level = std::max(level, size_t{ 4 }); + } + + std::fclose(file); + } + + return level; #else - // No source adopted (linux PSI /proc/pressure/memory when staged there). + // No platform source. return zero; #endif }