Skip to content

Commit 8066e47

Browse files
authored
AMT (#86)
Signed-off-by: turuslan <[email protected]>
1 parent bd3c012 commit 8066e47

File tree

17 files changed

+676
-71
lines changed

17 files changed

+676
-71
lines changed

core/crypto/signature/signature.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ namespace fc::crypto::signature {
4545
template <class Stream,
4646
typename = std::enable_if_t<
4747
std::remove_reference<Stream>::type::is_cbor_encoder_stream>>
48-
Stream &operator<<(Stream &&s, const Signature &signature) noexcept {
48+
Stream &operator<<(Stream &&s, const Signature &signature) {
4949
std::vector<uint8_t> bytes{};
5050
visit_in_place(signature,
5151
[&bytes](const BlsSignature &v) {

core/primitives/address/address_codec.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace fc::primitives::address {
3939
template <class Stream,
4040
typename = std::enable_if_t<
4141
std::remove_reference<Stream>::type::is_cbor_encoder_stream>>
42-
Stream &operator<<(Stream &&s, const Address &address) noexcept {
42+
Stream &operator<<(Stream &&s, const Address &address) {
4343
return s << encode(address);
4444
}
4545

core/primitives/ticket/epost_ticket_codec.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace fc::primitives::ticket {
2929
template <class Stream,
3030
typename = std::enable_if_t<
3131
std::remove_reference<Stream>::type::is_cbor_encoder_stream>>
32-
Stream &operator<<(Stream &&s, const EPostTicket &ticket) noexcept {
32+
Stream &operator<<(Stream &&s, const EPostTicket &ticket) {
3333
return s << (s.list() << ticket.partial << ticket.sector_id
3434
<< ticket.challenge_index);
3535
}
@@ -64,7 +64,7 @@ namespace fc::primitives::ticket {
6464
template <class Stream,
6565
typename = std::enable_if_t<
6666
std::remove_reference<Stream>::type::is_cbor_encoder_stream>>
67-
Stream &operator<<(Stream &&s, const EPostProof &epp) noexcept {
67+
Stream &operator<<(Stream &&s, const EPostProof &epp) {
6868
return s << (s.list() << epp.proof << epp.post_rand
6969
<< epp.candidates);
7070
}

core/primitives/ticket/ticket_codec.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace fc::primitives::ticket {
3030
template <class Stream,
3131
typename = std::enable_if_t<
3232
std::remove_reference<Stream>::type::is_cbor_encoder_stream>>
33-
Stream &operator<<(Stream &&s, const Ticket &ticket) noexcept {
33+
Stream &operator<<(Stream &&s, const Ticket &ticket) {
3434
return s << (s.list() << ticket.bytes);
3535
}
3636

core/primitives/tipset/tipset.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ namespace fc::primitives::tipset {
9898
template <class Stream,
9999
typename = std::enable_if_t<
100100
std::remove_reference<Stream>::type::is_cbor_encoder_stream>>
101-
Stream &operator<<(Stream &&s, const Tipset &tipset) noexcept {
101+
Stream &operator<<(Stream &&s, const Tipset &tipset) {
102102
return s << (s.list() << tipset.cids << tipset.blks << tipset.height);
103103
}
104104

core/storage/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# SPDX-License-Identifier: Apache-2.0
44
#
55

6+
add_subdirectory(amt)
67
add_subdirectory(config)
78
add_subdirectory(filestore)
89
add_subdirectory(hamt)

core/storage/amt/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#
2+
# Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
6+
add_library(amt
7+
amt.cpp
8+
)
9+
target_link_libraries(amt
10+
cbor
11+
cid
12+
outcome
13+
)

core/storage/amt/amt.cpp

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "storage/amt/amt.hpp"
7+
8+
#include "common/which.hpp"
9+
10+
OUTCOME_CPP_DEFINE_CATEGORY(fc::storage::amt, AmtError, e) {
11+
using fc::storage::amt::AmtError;
12+
switch (e) {
13+
case AmtError::EXPECTED_CID:
14+
return "Expected CID";
15+
case AmtError::DECODE_WRONG:
16+
return "Decode wrong";
17+
case AmtError::INDEX_TOO_BIG:
18+
return "Index too big";
19+
case AmtError::NOT_FOUND:
20+
return "Not found";
21+
}
22+
return "Unknown error";
23+
}
24+
25+
namespace fc::storage::amt {
26+
auto pow(uint64_t base, uint64_t exponent) {
27+
uint64_t result{1};
28+
if (exponent != 0) {
29+
--exponent;
30+
result *= base;
31+
}
32+
while (exponent != 0) {
33+
if (exponent % 2 == 0) {
34+
exponent /= 2;
35+
result *= result;
36+
} else {
37+
--exponent;
38+
result *= base;
39+
}
40+
}
41+
return result;
42+
}
43+
44+
auto maskAt(uint64_t height) {
45+
return pow(kWidth, height);
46+
}
47+
48+
auto maxAt(uint64_t height) {
49+
return maskAt(height + 1);
50+
}
51+
52+
Amt::Amt(std::shared_ptr<ipfs::IpfsDatastore> store)
53+
: store_(std::move(store)), root_(Root{}) {}
54+
55+
Amt::Amt(std::shared_ptr<ipfs::IpfsDatastore> store, const CID &root)
56+
: store_(std::move(store)), root_(root) {}
57+
58+
outcome::result<uint64_t> Amt::count() {
59+
OUTCOME_TRY(loadRoot());
60+
return boost::get<Root>(root_).count;
61+
}
62+
63+
outcome::result<void> Amt::set(uint64_t key, gsl::span<const uint8_t> value) {
64+
if (key >= kMaxIndex) {
65+
return AmtError::INDEX_TOO_BIG;
66+
}
67+
OUTCOME_TRY(loadRoot());
68+
auto &root = boost::get<Root>(root_);
69+
while (key >= maxAt(root.height)) {
70+
root.node = {
71+
true, Node::Links{{0, std::make_shared<Node>(std::move(root.node))}}};
72+
++root.height;
73+
}
74+
OUTCOME_TRY(add, set(root.node, root.height, key, value));
75+
if (add) {
76+
++root.count;
77+
}
78+
return outcome::success();
79+
}
80+
81+
outcome::result<Value> Amt::get(uint64_t key) {
82+
if (key >= kMaxIndex) {
83+
return AmtError::INDEX_TOO_BIG;
84+
}
85+
OUTCOME_TRY(loadRoot());
86+
auto &root = boost::get<Root>(root_);
87+
if (key >= maxAt(root.height)) {
88+
return AmtError::NOT_FOUND;
89+
}
90+
std::reference_wrapper<Node> node = root.node;
91+
for (auto height = root.height; height != 0; --height) {
92+
auto mask = maskAt(height);
93+
OUTCOME_TRY(child, loadLink(node, key / mask, false));
94+
key %= mask;
95+
node = *child;
96+
}
97+
auto &values = boost::get<Node::Values>(node.get().items);
98+
auto it = values.find(key);
99+
if (it == values.end()) {
100+
return AmtError::NOT_FOUND;
101+
}
102+
return it->second;
103+
}
104+
105+
outcome::result<void> Amt::remove(uint64_t key) {
106+
if (key >= kMaxIndex) {
107+
return AmtError::INDEX_TOO_BIG;
108+
}
109+
OUTCOME_TRY(loadRoot());
110+
auto &root = boost::get<Root>(root_);
111+
if (key >= maxAt(root.height)) {
112+
return AmtError::NOT_FOUND;
113+
}
114+
OUTCOME_TRY(remove(root.node, root.height, key));
115+
--root.count;
116+
while (root.height > 0) {
117+
auto &links = boost::get<Node::Links>(root.node.items);
118+
if (links.size() != 1 && links.find(0) == links.end()) {
119+
break;
120+
}
121+
OUTCOME_TRY(child, loadLink(root.node, 0, false));
122+
auto node = std::move(*child);
123+
root.node = std::move(node);
124+
--root.height;
125+
}
126+
return outcome::success();
127+
}
128+
129+
outcome::result<CID> Amt::flush() {
130+
if (which<Root>(root_)) {
131+
auto &root = boost::get<Root>(root_);
132+
OUTCOME_TRY(flush(root.node));
133+
OUTCOME_TRY(cid, store_->setCbor(root));
134+
root_ = cid;
135+
}
136+
return boost::get<CID>(root_);
137+
}
138+
139+
outcome::result<void> Amt::visit(const Visitor &visitor) {
140+
OUTCOME_TRY(loadRoot());
141+
auto &root = boost::get<Root>(root_);
142+
return visit(root.node, root.height, 0, visitor);
143+
}
144+
145+
outcome::result<bool> Amt::set(Node &node,
146+
uint64_t height,
147+
uint64_t key,
148+
gsl::span<const uint8_t> value) {
149+
node.has_bits = true;
150+
if (height == 0) {
151+
return boost::get<Node::Values>(node.items)
152+
.insert(std::make_pair(key, value))
153+
.second;
154+
}
155+
auto mask = maskAt(height);
156+
OUTCOME_TRY(child, loadLink(node, key / mask, true));
157+
return set(*child, height - 1, key % mask, value);
158+
}
159+
160+
outcome::result<bool> Amt::remove(Node &node, uint64_t height, uint64_t key) {
161+
if (height == 0) {
162+
auto &values = boost::get<Node::Values>(node.items);
163+
if (values.erase(key) == 0) {
164+
return AmtError::NOT_FOUND;
165+
}
166+
return outcome::success();
167+
}
168+
auto mask = maskAt(height);
169+
auto index = key / mask;
170+
OUTCOME_TRY(child, loadLink(node, index, false));
171+
OUTCOME_TRY(remove(*child, height - 1, key % mask));
172+
// github.com/filecoin-project/go-amt-ipld/v2 behavior
173+
auto empty = visit_in_place(
174+
child->items,
175+
[](const Node::Values &values) { return values.empty(); },
176+
[](const Node::Links &links) { return links.empty(); });
177+
if (empty) {
178+
boost::get<Node::Links>(node.items).erase(index);
179+
}
180+
return outcome::success();
181+
}
182+
183+
outcome::result<void> Amt::flush(Node &node) {
184+
if (which<Node::Links>(node.items)) {
185+
auto &links = boost::get<Node::Links>(node.items);
186+
for (auto &pair : links) {
187+
if (which<Node::Ptr>(pair.second)) {
188+
auto &child = *boost::get<Node::Ptr>(pair.second);
189+
OUTCOME_TRY(flush(child));
190+
OUTCOME_TRY(cid, store_->setCbor(child));
191+
pair.second = cid;
192+
}
193+
}
194+
}
195+
return outcome::success();
196+
}
197+
198+
outcome::result<void> Amt::visit(Node &node,
199+
uint64_t height,
200+
uint64_t offset,
201+
const Visitor &visitor) {
202+
if (height == 0) {
203+
for (auto &it : boost::get<Node::Values>(node.items)) {
204+
OUTCOME_TRY(visitor(offset + it.first, it.second));
205+
}
206+
return outcome::success();
207+
}
208+
auto mask = maskAt(height);
209+
for (auto &it : boost::get<Node::Links>(node.items)) {
210+
OUTCOME_TRY(loadLink(node, it.first, false));
211+
OUTCOME_TRY(visit(*boost::get<Node::Ptr>(it.second),
212+
height - 1,
213+
offset + it.first * mask,
214+
visitor));
215+
}
216+
return outcome::success();
217+
}
218+
219+
outcome::result<void> Amt::loadRoot() {
220+
if (which<CID>(root_)) {
221+
OUTCOME_TRY(root, store_->getCbor<Root>(boost::get<CID>(root_)));
222+
root_ = root;
223+
}
224+
return outcome::success();
225+
}
226+
227+
outcome::result<Node::Ptr> Amt::loadLink(Node &parent,
228+
uint64_t index,
229+
bool create) {
230+
if (which<Node::Values>(parent.items)
231+
&& boost::get<Node::Values>(parent.items).empty()) {
232+
parent.items = Node::Links{};
233+
}
234+
auto &links = boost::get<Node::Links>(parent.items);
235+
auto it = links.find(index);
236+
if (it == links.end()) {
237+
if (create) {
238+
auto node = std::make_shared<Node>();
239+
links[index] = node;
240+
return node;
241+
}
242+
return AmtError::NOT_FOUND;
243+
}
244+
auto &link = it->second;
245+
if (which<CID>(link)) {
246+
OUTCOME_TRY(node, store_->getCbor<Node>(boost::get<CID>(link)));
247+
link = std::make_shared<Node>(std::move(node));
248+
}
249+
return boost::get<Node::Ptr>(link);
250+
}
251+
} // namespace fc::storage::amt

0 commit comments

Comments
 (0)