/
hash_table_header.ipp
163 lines (138 loc) · 5.2 KB
/
hash_table_header.ipp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* Copyright (c) 2011-2018 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 <http://www.gnu.org/licenses/>.
*/
#ifndef LIBBITCOIN_DATABASE_HASH_TABLE_HEADER_IPP
#define LIBBITCOIN_DATABASE_HASH_TABLE_HEADER_IPP
#include <bitcoin/bitcoin.hpp>
#include <bitcoin/database/memory/memory.hpp>
#include <bitcoin/database/memory/storage.hpp>
namespace libbitcoin {
namespace database {
template <typename Index, typename Link>
template <typename Key>
inline Index hash_table_header<Index, Link>::remainder(const Key& key,
Index divisor)
{
// TODO: implement std::hash replacement to prevent store drift.
return divisor == 0 ? 0 : std::hash<Key>()(key) % divisor;
}
// Link must be unsigned (see static assertions below).
// HACK: This is a VC++ workaround, otherwise std::numeric_limits<Link>::max().
template <typename Index, typename Link>
const Link hash_table_header<Index, Link>::empty = (Link)bc::max_uint64;
template <typename Index, typename Link>
hash_table_header<Index, Link>::hash_table_header(storage& file, Index buckets)
: file_(file), buckets_(buckets)
{
static_assert(std::is_unsigned<Link>::value,
"Hash table header requires unsigned value type.");
static_assert(std::is_unsigned<Index>::value,
"Hash table header requires unsigned index type.");
}
template <typename Index, typename Link>
bool hash_table_header<Index, Link>::create()
{
const auto file_size = size(buckets_);
// The accessor must remain in scope until the end of the block.
const auto memory = file_.resize(file_size);
// Speed-optimized fill implementation.
memset(memory->buffer(), (uint8_t)empty, file_size);
// Overwrite the start of the buffer with the bucket count.
auto serial = make_unsafe_serializer(memory->buffer());
serial.template write_little_endian<Index>(buckets_);
return true;
}
template <typename Index, typename Link>
bool hash_table_header<Index, Link>::start()
{
// File is too small for the number of buckets in the header.
if (file_.size() < link(buckets_))
return false;
// The accessor must remain in scope until the end of the block.
const auto memory = file_.access();
// Does not require atomicity (no concurrency during start).
auto deserial = make_unsafe_deserializer(memory->buffer());
return deserial.template read_little_endian<Index>() == buckets_;
}
template <typename Index, typename Link>
Link hash_table_header<Index, Link>::read(Index index) const
{
BITCOIN_ASSERT(index < buckets_);
// The accessor must remain in scope until the end of the block.
const auto memory = file_.access();
memory->increment(link(index));
auto deserial = make_unsafe_deserializer(memory->buffer());
// Critical Section
///////////////////////////////////////////////////////////////////////////
shared_lock lock(mutex_);
return deserial.template read_little_endian<Link>();
///////////////////////////////////////////////////////////////////////////
}
template <typename Index, typename Link>
void hash_table_header<Index, Link>::write(Index index, Link value)
{
BITCOIN_ASSERT(index < buckets_);
// The accessor must remain in scope until the end of the block.
const auto memory = file_.access();
memory->increment(link(index));
auto serial = make_unsafe_serializer(memory->buffer());
// Critical Section
///////////////////////////////////////////////////////////////////////////
unique_lock lock(mutex_);
serial.template write_little_endian<Link>(value);
///////////////////////////////////////////////////////////////////////////
}
template <typename Index, typename Link>
Index hash_table_header<Index, Link>::buckets() const
{
return buckets_;
}
template <typename Index, typename Link>
size_t hash_table_header<Index, Link>::size()
{
return size(buckets_);
}
// static
template <typename Index, typename Link>
size_t hash_table_header<Index, Link>::size(Index buckets)
{
// Header byte size is file link of last bucket + 1:
//
// [ size:buckets ]
// [ [ row[0] ] ]
// [ [ ... ] ]
// [ [ row[buckets - 1] ] ] <=
//
return link(buckets);
}
// static
template <typename Index, typename Link>
file_offset hash_table_header<Index, Link>::link(Index index)
{
// File link of indexed bucket is:
//
// [ size :Index ]
// [ [ row[0] :Link ] ]
// [ [ ... ] ]
// => [ [ row[index]:Link ] ]
//
return sizeof(Index) + index * sizeof(Link);
}
} // namespace database
} // namespace libbitcoin
#endif