openMSX
TigerTree.cc
Go to the documentation of this file.
1#include "TigerTree.hh"
2
3#include "tiger.hh"
4#include "Math.hh"
5#include "MemBuffer.hh"
6#include "ScopedAssign.hh"
7
8#include <cassert>
9#include <map>
10#include <span>
11
12namespace openmsx {
13
15{
16 struct Info {
18 bool valid;
19 };
21 time_t time = -1;
23};
24// Typically contains 0 or 1 element, and only rarely 2 or more. But we need
25// the address of existing elements to remain stable when new elements are
26// inserted. So still use std::map instead of std::vector.
27static std::map<std::pair<size_t, std::string>, TTCacheEntry> ttCache;
28
29[[nodiscard]] static constexpr size_t calcNumNodes(size_t dataSize)
30{
31 auto numBlocks = (dataSize + TigerTree::BLOCK_SIZE - 1) / TigerTree::BLOCK_SIZE;
32 return (numBlocks == 0) ? 1 : 2 * numBlocks - 1;
33}
34
35[[nodiscard]] static TTCacheEntry& getCacheEntry(
36 TTData& data, size_t dataSize, const std::string& name)
37{
38 auto& result = ttCache[std::pair(dataSize, name)];
39 if (!data.isCacheStillValid(result.time)) { // note: has side effect
40 size_t numNodes = calcNumNodes(dataSize);
41 result.nodes.resize(numNodes);
42 for (auto& i : result.nodes) i.valid = false; // all invalid
43 result.numNodesValid = 0;
44 }
45 return result;
46}
47
48TigerTree::TigerTree(TTData& data_, size_t dataSize_, const std::string& name)
49 : data(data_)
50 , dataSize(dataSize_)
51 , entry(getCacheEntry(data, dataSize, name))
52{
53}
54
55const TigerHash& TigerTree::calcHash(const std::function<void(size_t, size_t)>& progressCallback)
56{
57 return calcHash(getTop(), progressCallback);
58}
59
60void TigerTree::notifyChange(size_t offset, size_t len, time_t time)
61{
62 entry.time = time;
63
64 assert((offset + len) <= dataSize);
65 if (len == 0) return;
66
67 if (entry.nodes[getTop().n].valid) {
68 entry.nodes[getTop().n].valid = false; // set sentinel
69 entry.numNodesValid--;
70 }
71 auto first = offset / BLOCK_SIZE;
72 auto last = (offset + len - 1) / BLOCK_SIZE;
73 assert(first <= last); // requires len != 0
74 do {
75 auto node = getLeaf(first);
76 while (entry.nodes[node.n].valid) {
77 entry.nodes[node.n].valid = false;
78 entry.numNodesValid--;
79 node = getParent(node);
80 }
81 } while (++first <= last);
82}
83
84const TigerHash& TigerTree::calcHash(Node node, const std::function<void(size_t, size_t)>& progressCallback)
85{
86 auto n = node.n;
87 auto& nod = entry.nodes[n];
88 if (!nod.valid) {
89 if (n & 1) {
90 // interior node
91 auto left = getLeftChild (node);
92 auto right = getRightChild(node);
93 const auto& h1 = calcHash(left, progressCallback);
94 const auto& h2 = calcHash(right, progressCallback);
95 tiger_int(h1, h2, nod.hash);
96 } else {
97 // leaf node
98 size_t b = n * (BLOCK_SIZE / 2);
99 size_t l = dataSize - b;
100
101 if (l >= BLOCK_SIZE) {
102 auto* d = data.getData(b, BLOCK_SIZE);
103 tiger_leaf(std::span{d, BLOCK_SIZE}, nod.hash);
104 } else {
105 // partial last block
106 auto* d = data.getData(b, l);
107 auto sa = ScopedAssign(d[-1], uint8_t(0));
108 tiger(std::span{d - 1, l + 1}, nod.hash);
109 }
110 }
111 nod.valid = true;
112 entry.numNodesValid++;
113 if (progressCallback) {
114 progressCallback(entry.numNodesValid, entry.nodes.size());
115 }
116 }
117 return nod.hash;
118}
119
120
121// The TigerTree::nodes member variable stores a linearized binary tree. The
122// linearization is done like in this example:
123//
124// 7 (level = 8)
125// ----/ \---- .
126// 3 11 (level = 4)
127// / \ / \ .
128// 1 5 9 | (level = 2)
129// / \ / \ / \ | .
130// 0 2 4 6 8 10 12 (level = 1)
131//
132// All leaf nodes are given even node values (0, 2, 4, .., 12). Leaf nodes have
133// level=1. At the next level (level=2) leaf nodes are pair-wise combined in
134// internal nodes. So nodes 0 and 2 are combined in node 1, 4+6->5 and 8+10->9.
135// Leaf-node 12 cannot be paired (there is no leaf-node 14), so there's no
136// corresponding node at level=2. The next level is level=4 (level values
137// double for each higher level). At level=4 node 3 is the combination of 1 and
138// 5 and 9+12->11. Note that 11 is a combination of two nodes from a different
139// (lower) level. And finally, node 7 at level=8 combines 3+11.
140//
141// The following methods navigate in this tree.
142
143TigerTree::Node TigerTree::getTop() const
144{
145 auto n = Math::floodRight(entry.nodes.size() / 2);
146 return {n, n + 1};
147}
148
149TigerTree::Node TigerTree::getLeaf(size_t block) const
150{
151 assert((2 * block) < entry.nodes.size());
152 return {2 * block, 1};
153}
154
155TigerTree::Node TigerTree::getParent(Node node) const
156{
157 assert(node.n < entry.nodes.size());
158 do {
159 node.n = (node.n & ~(2 * node.l)) + node.l;
160 node.l *= 2;
161 } while (node.n >= entry.nodes.size());
162 return node;
163}
164
165TigerTree::Node TigerTree::getLeftChild(Node node) const
166{
167 assert(node.n < entry.nodes.size());
168 assert(node.l > 1);
169 node.l /= 2;
170 node.n -= node.l;
171 return node;
172}
173
174TigerTree::Node TigerTree::getRightChild(Node node) const
175{
176 assert(node.n < entry.nodes.size());
177 while (true) {
178 assert(node.l > 1);
179 node.l /= 2;
180 auto r = node.n + node.l;
181 if (r < entry.nodes.size()) return {r, node.l};
182 }
183}
184
185} // namespace openmsx
Assign new value to some variable and restore the original value when this object goes out of scope.
This class manages the lifetime of a block of memory.
Definition MemBuffer.hh:32
The TigerTree class will query the to-be-hashed data via this abstract interface.
Definition TigerTree.hh:44
const TigerHash & calcHash(const std::function< void(size_t, size_t)> &progressCallback)
Calculate the hash value.
Definition TigerTree.cc:55
TigerTree(TTData &data, size_t dataSize, const std::string &name)
Create TigerTree calculator for the given (abstract) data block of given size.
Definition TigerTree.cc:48
void notifyChange(size_t offset, size_t len, time_t time)
Inform this calculator about changes in the input data.
Definition TigerTree.cc:60
static constexpr size_t BLOCK_SIZE
Definition TigerTree.hh:75
constexpr auto floodRight(std::unsigned_integral auto x) noexcept
Returns the smallest number of the form 2^n-1 that is greater or equal to the given number.
Definition Math.hh:32
This file implemented 3 utility functions:
Definition Autofire.cc:11
void tiger(std::span< const uint8_t > input, TigerHash &result)
Generic function to calculate a tiger-hash.
Definition tiger.cc:680
void tiger_int(const TigerHash &h0, const TigerHash &h1, TigerHash &result)
Use for tiger-tree internal node hash calculations.
Definition tiger.cc:711
void tiger_leaf(std::span< uint8_t > data, TigerHash &result)
Use for tiger-tree leaf node hash calculations.
Definition tiger.cc:731
MemBuffer< Info > nodes
Definition TigerTree.cc:20
This struct represents the result of a tiger-hash.
Definition tiger.hh:30