Skip to content

Commit

Permalink
Merge pull request #27 from evoskuil/master
Browse files Browse the repository at this point in the history
Fix spend database to use full point as hash key.
  • Loading branch information
evoskuil committed Aug 12, 2016
2 parents b024105 + e67b1ef commit 34a8b33
Show file tree
Hide file tree
Showing 21 changed files with 348 additions and 250 deletions.
3 changes: 1 addition & 2 deletions include/bitcoin/database/databases/block_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ namespace database {
class BCD_API block_database
{
public:
// Valid file offsets should never be zero.
static BC_CONSTEXPR file_offset empty = 0;
static const file_offset empty;

/// Construct the database.
block_database(const boost::filesystem::path& map_filename,
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/database/databases/spend_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class BCD_API spend_database
spend_statinfo statinfo() const;

private:
typedef record_hash_table<hash_digest> record_map;
typedef record_hash_table<chain::point> record_map;

// Hash table used for looking up inpoint spends by outpoint.
memory_map lookup_file_;
Expand Down
11 changes: 10 additions & 1 deletion include/bitcoin/database/impl/hash_table_header.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@
namespace libbitcoin {
namespace database {

// If the bucket count is zero we trust what is read from the file.
static BC_CONSTEXPR size_t trust_file_bucket_count = 0;

// This VC++ workaround is OK because ValueType must be unsigned.
//static constexpr ValueType empty = std::numeric_limits<ValueType>::max();
template <typename IndexType, typename ValueType>
const ValueType hash_table_header<IndexType, ValueType>::empty =
(ValueType)bc::max_uint64;

template <typename IndexType, typename ValueType>
hash_table_header<IndexType, ValueType>::hash_table_header(memory_map& file)
: hash_table_header(file, trust_file_bucket_count)
Expand All @@ -41,7 +48,9 @@ hash_table_header<IndexType, ValueType>::hash_table_header(memory_map& file,
IndexType buckets)
: file_(file), buckets_(buckets)
{
static_assert(empty == (ValueType)0xffffffffffffffff,
////static_assert(empty == (ValueType)0xffffffffffffffff,
//// "Unexpected value for empty sentinel.");
BITCOIN_ASSERT_MSG(empty == (ValueType)0xffffffffffffffff,
"Unexpected value for empty sentinel.");

static_assert(std::is_unsigned<ValueType>::value,
Expand Down
49 changes: 27 additions & 22 deletions include/bitcoin/database/impl/record_hash_table.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,41 @@
namespace libbitcoin {
namespace database {

template <typename HashType>
record_hash_table<HashType>::record_hash_table(
template <typename KeyType>
record_hash_table<KeyType>::record_hash_table(
record_hash_table_header& header, record_manager& manager)
: header_(header), manager_(manager)
{
}

template <typename HashType>
void record_hash_table<HashType>::store(const HashType& key,
// This is not limited to storing unique key values. If duplicate keyed values
// are store then retrieval and unlinking will fail as these multiples cannot
// be differentiated.
template <typename KeyType>
void record_hash_table<KeyType>::store(const KeyType& key,
const write_function write)
{
// Store current bucket value.
const auto old_begin = read_bucket_value(key);
record_row<HashType> item(manager_, 0);
record_row<KeyType> item(manager_, 0);
const auto new_begin = item.create(key, old_begin);
write(item.data());

// Link record to header.
link(key, new_begin);
}

template <typename HashType>
const memory_ptr record_hash_table<HashType>::find(const HashType& key) const
// This is limited to returning the first of multiple matching key values.
template <typename KeyType>
const memory_ptr record_hash_table<KeyType>::find(const KeyType& key) const
{
// Find start item...
auto current = read_bucket_value(key);

// Iterate through list...
while (current != header_.empty)
{
const record_row<HashType> item(manager_, current);
const record_row<KeyType> item(manager_, current);

// Found, return data.
if (item.compare(key))
Expand All @@ -78,12 +82,13 @@ const memory_ptr record_hash_table<HashType>::find(const HashType& key) const
return nullptr;
}

template <typename HashType>
bool record_hash_table<HashType>::unlink(const HashType& key)
// This is limited to unlinking the first of multiple matching key values.
template <typename KeyType>
bool record_hash_table<KeyType>::unlink(const KeyType& key)
{
// Find start item...
const auto begin = read_bucket_value(key);
const record_row<HashType> begin_item(manager_, begin);
const record_row<KeyType> begin_item(manager_, begin);

// If start item has the key then unlink from buckets.
if (begin_item.compare(key))
Expand All @@ -99,7 +104,7 @@ bool record_hash_table<HashType>::unlink(const HashType& key)
// Iterate through list...
while (current != header_.empty)
{
const record_row<HashType> item(manager_, current);
const record_row<KeyType> item(manager_, current);

// Found, unlink current item from previous.
if (item.compare(key))
Expand All @@ -121,34 +126,34 @@ bool record_hash_table<HashType>::unlink(const HashType& key)
return false;
}

template <typename HashType>
array_index record_hash_table<HashType>::bucket_index(
const HashType& key) const
template <typename KeyType>
array_index record_hash_table<KeyType>::bucket_index(
const KeyType& key) const
{
const auto bucket = remainder(key, header_.size());
BITCOIN_ASSERT(bucket < header_.size());
return bucket;
}

template <typename HashType>
array_index record_hash_table<HashType>::read_bucket_value(
const HashType& key) const
template <typename KeyType>
array_index record_hash_table<KeyType>::read_bucket_value(
const KeyType& key) const
{
auto value = header_.read(bucket_index(key));
static_assert(sizeof(value) == sizeof(array_index), "Invalid size");
return value;
}

template <typename HashType>
void record_hash_table<HashType>::link(const HashType& key,
template <typename KeyType>
void record_hash_table<KeyType>::link(const KeyType& key,
const array_index begin)
{
header_.write(bucket_index(key), begin);
}

template <typename HashType>
template <typename KeyType>
template <typename ListItem>
void record_hash_table<HashType>::release(const ListItem& item,
void record_hash_table<KeyType>::release(const ListItem& item,
const file_offset previous)
{
ListItem previous_item(manager_, previous);
Expand Down
24 changes: 12 additions & 12 deletions include/bitcoin/database/impl/record_multimap.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@
namespace libbitcoin {
namespace database {

template <typename HashType>
record_multimap<HashType>::record_multimap(record_hash_table_type& map,
template <typename KeyType>
record_multimap<KeyType>::record_multimap(record_hash_table_type& map,
record_list& records)
: map_(map), records_(records)
{
}

template <typename HashType>
array_index record_multimap<HashType>::lookup(const HashType& key) const
template <typename KeyType>
array_index record_multimap<KeyType>::lookup(const KeyType& key) const
{
const auto start_info = map_.find(key);

Expand All @@ -50,8 +50,8 @@ array_index record_multimap<HashType>::lookup(const HashType& key) const
///////////////////////////////////////////////////////////////////////////
}

template <typename HashType>
void record_multimap<HashType>::add_row(const HashType& key,
template <typename KeyType>
void record_multimap<KeyType>::add_row(const KeyType& key,
write_function write)
{
const auto start_info = map_.find(key);
Expand All @@ -66,8 +66,8 @@ void record_multimap<HashType>::add_row(const HashType& key,
add_to_list(start_info, write);
}

template <typename HashType>
void record_multimap<HashType>::add_to_list(memory_ptr start_info,
template <typename KeyType>
void record_multimap<KeyType>::add_to_list(memory_ptr start_info,
write_function write)
{
const auto address = REMAP_ADDRESS(start_info);
Expand All @@ -93,8 +93,8 @@ void record_multimap<HashType>::add_to_list(memory_ptr start_info,
///////////////////////////////////////////////////////////////////////////
}

template <typename HashType>
void record_multimap<HashType>::delete_last_row(const HashType& key)
template <typename KeyType>
void record_multimap<KeyType>::delete_last_row(const KeyType& key)
{
const auto start_info = map_.find(key);
BITCOIN_ASSERT_MSG(start_info, "The row to delete was not found.");
Expand Down Expand Up @@ -130,8 +130,8 @@ void record_multimap<HashType>::delete_last_row(const HashType& key)
///////////////////////////////////////////////////////////////////////////
}

template <typename HashType>
void record_multimap<HashType>::create_new(const HashType& key,
template <typename KeyType>
void record_multimap<KeyType>::create_new(const KeyType& key,
write_function write)
{
const auto first = records_.create();
Expand Down
46 changes: 23 additions & 23 deletions include/bitcoin/database/impl/record_row.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ namespace database {
* With the starting item, we can iterate until the end using the
* next_index() method.
*/
template <typename HashType>
template <typename KeyType>
class record_row
{
public:
static BC_CONSTEXPR size_t index_size = sizeof(array_index);
static BC_CONSTEXPR size_t hash_size = std::tuple_size<HashType>::value;
static BC_CONSTEXPR file_offset value_begin = hash_size + index_size;
static BC_CONSTEXPR size_t key_size = std::tuple_size<KeyType>::value;
static BC_CONSTEXPR file_offset value_begin = key_size + index_size;

record_row(record_manager& manager, array_index index);

array_index create(const HashType& key, const array_index next);
array_index create(const KeyType& key, const array_index next);

/// Does this match?
bool compare(const HashType& key) const;
bool compare(const KeyType& key) const;

/// The actual user data.
const memory_ptr data() const;
Expand All @@ -65,20 +65,20 @@ private:
mutable shared_mutex mutex_;
};

template <typename HashType>
record_row<HashType>::record_row(record_manager& manager,
template <typename KeyType>
record_row<KeyType>::record_row(record_manager& manager,
const array_index index)
: manager_(manager), index_(index)
{
static_assert(index_size == 4, "Invalid array_index size.");
}

template <typename HashType>
array_index record_row<HashType>::create(const HashType& key,
template <typename KeyType>
array_index record_row<KeyType>::create(const KeyType& key,
const array_index next)
{
// Create new record.
// [ HashType ]
// [ KeyType ]
// [ next:4 ]
// [ value... ]
index_ = manager_.new_records(1);
Expand All @@ -98,23 +98,23 @@ array_index record_row<HashType>::create(const HashType& key,
///////////////////////////////////////////////////////////////////////////
}

template <typename HashType>
bool record_row<HashType>::compare(const HashType& key) const
template <typename KeyType>
bool record_row<KeyType>::compare(const KeyType& key) const
{
// Key data is at the start.
const auto memory = raw_data(0);
return std::equal(key.begin(), key.end(), REMAP_ADDRESS(memory));
}

template <typename HashType>
const memory_ptr record_row<HashType>::data() const
template <typename KeyType>
const memory_ptr record_row<KeyType>::data() const
{
// Value data is at the end.
return raw_data(value_begin);
}

template <typename HashType>
array_index record_row<HashType>::next_index() const
template <typename KeyType>
array_index record_row<KeyType>::next_index() const
{
const auto memory = raw_next_data();
const auto next_address = REMAP_ADDRESS(memory);
Expand All @@ -126,8 +126,8 @@ array_index record_row<HashType>::next_index() const
///////////////////////////////////////////////////////////////////////////
}

template <typename HashType>
void record_row<HashType>::write_next_index(array_index next)
template <typename KeyType>
void record_row<KeyType>::write_next_index(array_index next)
{
const auto memory = raw_next_data();
auto serial = make_serializer(REMAP_ADDRESS(memory));
Expand All @@ -139,19 +139,19 @@ void record_row<HashType>::write_next_index(array_index next)
///////////////////////////////////////////////////////////////////////////
}

template <typename HashType>
const memory_ptr record_row<HashType>::raw_data(file_offset offset) const
template <typename KeyType>
const memory_ptr record_row<KeyType>::raw_data(file_offset offset) const
{
auto memory = manager_.get(index_);
REMAP_INCREMENT(memory, offset);
return memory;
}

template <typename HashType>
const memory_ptr record_row<HashType>::raw_next_data() const
template <typename KeyType>
const memory_ptr record_row<KeyType>::raw_next_data() const
{
// Next position is after key data.
return raw_data(hash_size);
return raw_data(key_size);
}

} // namespace database
Expand Down
24 changes: 5 additions & 19 deletions include/bitcoin/database/impl/remainder.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,17 @@
#define LIBBITCOIN_DATABASE_REMAINDER_IPP

#include <cstdint>
#include <functional>
#include <bitcoin/bitcoin.hpp>

namespace libbitcoin {
namespace database {

template <typename HashType>
uint32_t remainder(const HashType& value, const uint32_t divisor)
/// Return a hash of the key reduced to the domain of the divisor.
template <typename KeyType, typename Divisor>
Divisor remainder(const KeyType& key, const Divisor divisor)
{
static_assert(sizeof(HashType) >= sizeof(uint32_t), "HashType too small.");
if (divisor == 0)
return 0;

const auto hash32 = from_big_endian_unsafe<uint32_t>(value.begin());
return hash32 % divisor;
}

template <typename HashType>
uint64_t remainder(const HashType& value, const uint64_t divisor)
{
static_assert(sizeof(HashType) >= sizeof(uint64_t), "HashType too small.");
if (divisor == 0)
return 0;

const auto hash64 = from_big_endian_unsafe<uint64_t>(value.begin());
return hash64 % divisor;
return divisor == 0 ? 0 : std::hash<KeyType>()(key) % divisor;
}

} // namespace database
Expand Down

0 comments on commit 34a8b33

Please sign in to comment.