xrbp 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/examples/nodestore1.rb +12 -7
- data/lib/xrbp/core_ext.rb +27 -0
- data/lib/xrbp/crypto/account.rb +28 -3
- data/lib/xrbp/nodestore.rb +6 -0
- data/lib/xrbp/nodestore/amendments.rb +13 -0
- data/lib/xrbp/nodestore/backends/decompressor.rb +8 -6
- data/lib/xrbp/nodestore/backends/nudb.rb +2 -0
- data/lib/xrbp/nodestore/backends/rocksdb.rb +1 -0
- data/lib/xrbp/nodestore/db.rb +5 -387
- data/lib/xrbp/nodestore/fees.rb +19 -0
- data/lib/xrbp/nodestore/format.rb +72 -1
- data/lib/xrbp/nodestore/ledger.rb +272 -0
- data/lib/xrbp/nodestore/parser.rb +407 -0
- data/lib/xrbp/nodestore/protocol.rb +5 -0
- data/lib/xrbp/nodestore/protocol/currency.rb +11 -0
- data/lib/xrbp/nodestore/protocol/indexes.rb +109 -0
- data/lib/xrbp/nodestore/protocol/issue.rb +26 -0
- data/lib/xrbp/nodestore/protocol/quality.rb +10 -0
- data/lib/xrbp/nodestore/protocol/rate.rb +21 -0
- data/lib/xrbp/nodestore/shamap.rb +447 -0
- data/lib/xrbp/nodestore/shamap/errors.rb +8 -0
- data/lib/xrbp/nodestore/shamap/inner_node.rb +98 -0
- data/lib/xrbp/nodestore/shamap/item.rb +14 -0
- data/lib/xrbp/nodestore/shamap/node.rb +49 -0
- data/lib/xrbp/nodestore/shamap/node_factory.rb +120 -0
- data/lib/xrbp/nodestore/shamap/node_id.rb +83 -0
- data/lib/xrbp/nodestore/shamap/tagged_cache.rb +20 -0
- data/lib/xrbp/nodestore/shamap/tree_node.rb +21 -0
- data/lib/xrbp/nodestore/sle.rb +4 -0
- data/lib/xrbp/nodestore/sle/st_account.rb +8 -0
- data/lib/xrbp/nodestore/sle/st_amount.rb +226 -0
- data/lib/xrbp/nodestore/sle/st_ledger_entry.rb +24 -0
- data/lib/xrbp/nodestore/sle/st_object.rb +46 -0
- data/lib/xrbp/nodestore/sqldb.rb +23 -0
- data/lib/xrbp/nodestore/uint.rb +7 -0
- data/lib/xrbp/version.rb +1 -1
- data/spec/xrbp/nodestore/backends/nudb_spec.rb +3 -1
- data/spec/xrbp/nodestore/backends/rocksdb_spec.rb +1 -1
- data/spec/xrbp/nodestore/{backends/db_parser.rb → db_parser.rb} +2 -2
- data/spec/xrbp/nodestore/ledger_access.rb +17 -0
- metadata +30 -3
@@ -0,0 +1,109 @@
|
|
1
|
+
module XRBP
|
2
|
+
module NodeStore
|
3
|
+
# Return DB lookup indices for the following artifacts
|
4
|
+
module Indexes
|
5
|
+
|
6
|
+
def self.get_quality(base)
|
7
|
+
# FIXME: reverse to convert big to little endian,
|
8
|
+
# need to account for all platforms
|
9
|
+
base[-8..-1].reverse.to_bn
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.get_quality_next(base)
|
13
|
+
nxt = "10000000000000000".to_i(16)
|
14
|
+
(base.to_bn + nxt).bytes
|
15
|
+
.reverse.pack("C*")
|
16
|
+
end
|
17
|
+
|
18
|
+
###
|
19
|
+
|
20
|
+
def self.dir_node_index(root, index)
|
21
|
+
return root if index == 0
|
22
|
+
|
23
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
24
|
+
sha512 << "\0"
|
25
|
+
sha512 << Format::LEDGER_NAMESPACE[:dir_node]
|
26
|
+
sha512 << root
|
27
|
+
sha512 << index.bytes.rjust!(8, 0).pack("C*")
|
28
|
+
|
29
|
+
sha512.digest[0..31]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.page(key, index)
|
33
|
+
dir_node_index key, index
|
34
|
+
end
|
35
|
+
|
36
|
+
# Account index from id
|
37
|
+
def self.account(id)
|
38
|
+
id = Crypto.account_id(id)
|
39
|
+
|
40
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
41
|
+
sha512 << "\0"
|
42
|
+
sha512 << Format::LEDGER_NAMESPACE[:account]
|
43
|
+
sha512 << id
|
44
|
+
|
45
|
+
sha512.digest[0..31]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Trust line for account/iou
|
49
|
+
def self.line(account, iou)
|
50
|
+
account = Crypto.account_id(account)
|
51
|
+
issuer = Crypto.account_id(iou[:account])
|
52
|
+
|
53
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
54
|
+
sha512 << "\0"
|
55
|
+
sha512 << Format::LEDGER_NAMESPACE[:ripple]
|
56
|
+
|
57
|
+
if account.to_bn < issuer.to_bn
|
58
|
+
sha512 << account
|
59
|
+
sha512 << issuer
|
60
|
+
|
61
|
+
else
|
62
|
+
sha512 << issuer
|
63
|
+
sha512 << account
|
64
|
+
end
|
65
|
+
|
66
|
+
sha512 << Format.encode_currency(iou[:currency])
|
67
|
+
|
68
|
+
sha512.digest[0..31]
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO: order book dir hash for ledger
|
72
|
+
def self.order_book_dir()
|
73
|
+
end
|
74
|
+
|
75
|
+
# Order book index for given input/output
|
76
|
+
def self.order_book(input, output)
|
77
|
+
input = Hash[input]
|
78
|
+
output = Hash[output]
|
79
|
+
|
80
|
+
# Currency always upcase
|
81
|
+
input[:currency].upcase!
|
82
|
+
output[:currency].upcase!
|
83
|
+
|
84
|
+
# If currency == 'XRP' set corresponding issuer
|
85
|
+
input[:account] = Crypto.xrp_account if input[:currency] == 'XRP'
|
86
|
+
output[:account] = Crypto.xrp_account if output[:currency] == 'XRP'
|
87
|
+
|
88
|
+
# Convert currency to binary representation
|
89
|
+
input[:currency] = Format.encode_currency(input[:currency])
|
90
|
+
output[:currency] = Format.encode_currency(output[:currency])
|
91
|
+
|
92
|
+
# convert input / output account to binary representation
|
93
|
+
input[:account] = Crypto.account_id(input[:account])
|
94
|
+
output[:account] = Crypto.account_id(output[:account])
|
95
|
+
|
96
|
+
book_base = ["\0", Format::LEDGER_NAMESPACE[:book_dir],
|
97
|
+
input[:currency], output[:currency],
|
98
|
+
input[:account], output[:account]].join
|
99
|
+
|
100
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
101
|
+
book_base = sha512.digest(book_base)[0..31]
|
102
|
+
|
103
|
+
# XXX: get_quality_index shorthand:
|
104
|
+
book_base[-8..-1] = [0, 0, 0, 0, 0, 0, 0, 0].pack("C*")
|
105
|
+
book_base
|
106
|
+
end
|
107
|
+
end # module Indexes
|
108
|
+
end # module NodeStore
|
109
|
+
end # module XRBP
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module XRBP
|
2
|
+
module NodeStore
|
3
|
+
class Issue
|
4
|
+
attr_reader :currency, :account
|
5
|
+
|
6
|
+
def initialize(currency, account)
|
7
|
+
@currency = currency
|
8
|
+
@account = account
|
9
|
+
end
|
10
|
+
|
11
|
+
def xrp?
|
12
|
+
self == NodeStore.xrp_issue
|
13
|
+
end
|
14
|
+
end # class Issue
|
15
|
+
|
16
|
+
def self.xrp_issue
|
17
|
+
@xrp_issue ||= Issue.new(NodeStore.xrp_currency,
|
18
|
+
Crypto.xrp_account)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.no_issue
|
22
|
+
@no_issue ||= Issue.new(NodeStore.no_currency,
|
23
|
+
Crypto.no_account)
|
24
|
+
end
|
25
|
+
end # module NodeStore
|
26
|
+
end # module XRBP
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module XRBP
|
2
|
+
module NodeStore
|
3
|
+
# Ripple specific constant used for parsing qualities and other things
|
4
|
+
# https://github.com/ripple/rippled/blob/develop/src/ripple/protocol/Quality.h#L107
|
5
|
+
QUALITY_ONE = 1000000000
|
6
|
+
|
7
|
+
class Quality
|
8
|
+
end # class Quality
|
9
|
+
end # module NodeStore
|
10
|
+
end # module XRBP
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module XRBP
|
2
|
+
module NodeStore
|
3
|
+
class Rate
|
4
|
+
attr_reader :rate
|
5
|
+
|
6
|
+
def initialize(rate=nil)
|
7
|
+
@rate = rate
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.parity
|
11
|
+
@parity ||= Rate.new(QUALITY_ONE)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_amount
|
15
|
+
STAmount.new :issue => Issue.no_issue,
|
16
|
+
:mantissa => rate,
|
17
|
+
:exponent => -9
|
18
|
+
end
|
19
|
+
end # class Rate
|
20
|
+
end # module NodeStore
|
21
|
+
end # module XRBP
|
@@ -0,0 +1,447 @@
|
|
1
|
+
require_relative './shamap/errors'
|
2
|
+
require_relative './shamap/node_id'
|
3
|
+
require_relative './shamap/node'
|
4
|
+
require_relative './shamap/inner_node'
|
5
|
+
require_relative './shamap/tree_node'
|
6
|
+
require_relative './shamap/item'
|
7
|
+
require_relative './shamap/tagged_cache'
|
8
|
+
|
9
|
+
module XRBP
|
10
|
+
class SHAMap
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
def initialize(args={})
|
14
|
+
@db = args[:db]
|
15
|
+
@version = args[:version]
|
16
|
+
|
17
|
+
if @version == 2
|
18
|
+
@root = InnerNode.new :v2 => true,
|
19
|
+
:depth => 0
|
20
|
+
else
|
21
|
+
@root = InnerNode.new :v2 => false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Invoke callback block w/ each sequential SHAMap item
|
26
|
+
# Implements Enumerable interface.
|
27
|
+
def each
|
28
|
+
current, stack = *peek_first_item
|
29
|
+
until current.nil?
|
30
|
+
yield current.item
|
31
|
+
current, stack = *peek_next_item(current.item.key, stack)
|
32
|
+
end
|
33
|
+
|
34
|
+
return self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return the next key in tree greater than
|
38
|
+
# specified one and less than last
|
39
|
+
def succ(key, last)
|
40
|
+
item = upper_bound(key)
|
41
|
+
return nil if item == map_end
|
42
|
+
return nil if last && item.key.to_bn >= last.to_bn
|
43
|
+
return item.key
|
44
|
+
end
|
45
|
+
|
46
|
+
# Read Key from database and return
|
47
|
+
# corresponding SLE
|
48
|
+
def read(key)
|
49
|
+
raise if key.zero?
|
50
|
+
item = peek_item(key)
|
51
|
+
return nil unless item
|
52
|
+
sle = NodeStore::SLE.new :item => item,
|
53
|
+
:key => key
|
54
|
+
#return nil unless key.check?(sle)
|
55
|
+
sle
|
56
|
+
end
|
57
|
+
|
58
|
+
###
|
59
|
+
|
60
|
+
# Return node corresponding to key
|
61
|
+
# or nil if not found
|
62
|
+
def peek_item(key)
|
63
|
+
leaf = find_key(key)
|
64
|
+
return nil unless leaf
|
65
|
+
leaf.peek_item
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return node corresponding to first item in map
|
69
|
+
def peek_first_item
|
70
|
+
stack = []
|
71
|
+
node, stack = *first_below(@root, stack)
|
72
|
+
return nil unless node
|
73
|
+
return node, stack
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return node corresponding to next sequential
|
77
|
+
# item in map
|
78
|
+
def peek_next_item(id, stack)
|
79
|
+
raise if stack.empty?
|
80
|
+
raise unless stack.last.first.leaf?
|
81
|
+
stack.pop
|
82
|
+
|
83
|
+
until stack.empty?
|
84
|
+
node, node_id = *stack.last
|
85
|
+
raise if node.leaf?
|
86
|
+
|
87
|
+
# Select next higher tree branch
|
88
|
+
inner = node
|
89
|
+
(node_id.select_branch(id) + 1).upto(15) { |b|
|
90
|
+
next if inner.empty_branch?(b)
|
91
|
+
node = descend_throw(inner, b)
|
92
|
+
leaf, stack = *first_below(node, stack, b)
|
93
|
+
raise unless leaf && leaf.leaf?
|
94
|
+
return leaf, stack
|
95
|
+
}
|
96
|
+
|
97
|
+
stack.pop
|
98
|
+
end
|
99
|
+
|
100
|
+
return nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# Fetch node from database raising
|
104
|
+
# error if it is not found
|
105
|
+
def fetch_node(key)
|
106
|
+
node = fetch_node_nt(key)
|
107
|
+
raise unless node
|
108
|
+
node
|
109
|
+
end
|
110
|
+
|
111
|
+
# Retrieve node from db.
|
112
|
+
# nt = no throw
|
113
|
+
def fetch_node_nt(key)
|
114
|
+
res = get_cache(key)
|
115
|
+
return res if res
|
116
|
+
canonicalize(key, fetch_node_from_db(key))
|
117
|
+
end
|
118
|
+
|
119
|
+
# Fetch key from database and assign
|
120
|
+
# result as root element
|
121
|
+
def fetch_root(key)
|
122
|
+
return true if key == root.hash
|
123
|
+
|
124
|
+
root = fetch_node_nt(key)
|
125
|
+
return false unless root
|
126
|
+
|
127
|
+
@root = root
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
attr_reader :db, :root
|
134
|
+
|
135
|
+
def v2?
|
136
|
+
root && root.v2?
|
137
|
+
end
|
138
|
+
|
139
|
+
def map_end
|
140
|
+
:end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Used to cache nodes by key
|
144
|
+
def treecache
|
145
|
+
@treecache ||= TaggedCache.new
|
146
|
+
end
|
147
|
+
|
148
|
+
###
|
149
|
+
|
150
|
+
# Return node in cache corresponding to key
|
151
|
+
def get_cache(key)
|
152
|
+
treecache.fetch(key)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Return node in tree corresponding to key, else nil
|
156
|
+
def find_key(key)
|
157
|
+
leaf, stack = walk_towards_key(key)
|
158
|
+
return nil if leaf && leaf.peek_item.key != key
|
159
|
+
leaf
|
160
|
+
end
|
161
|
+
|
162
|
+
# Retreive specified key from database and
|
163
|
+
# create new Node-subclass instance corresponding
|
164
|
+
# to record type.
|
165
|
+
def fetch_node_from_db(key)
|
166
|
+
# XXX: shorthand object decoding by removing unused & type fields
|
167
|
+
obj = db[key][9..-1]
|
168
|
+
|
169
|
+
begin
|
170
|
+
node = Node.make(obj, 0, :prefix, key, true)
|
171
|
+
|
172
|
+
if node && node.inner?
|
173
|
+
if node.v2? != v2?
|
174
|
+
raise unless root && root.empty?
|
175
|
+
if v2?
|
176
|
+
@root = make_v2
|
177
|
+
else
|
178
|
+
@root = make_v1
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
return node
|
184
|
+
rescue Exception
|
185
|
+
puts "TODO: verify"
|
186
|
+
return TreeNode.new
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Canonicalize/cache key/node in treecache
|
191
|
+
def canonicalize(key, node)
|
192
|
+
treecache.canonicalize(key, node)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Return bool indicating if node is
|
196
|
+
# inconsistent with this tree
|
197
|
+
def inconsistent_node?(node)
|
198
|
+
return true if !root ||
|
199
|
+
!node
|
200
|
+
return false if node.tree_node?
|
201
|
+
v2 = node.v2?
|
202
|
+
return true unless !v2 || node.depth != 0
|
203
|
+
return false if v2 == v2?
|
204
|
+
|
205
|
+
#state = INVALID
|
206
|
+
return true
|
207
|
+
end
|
208
|
+
|
209
|
+
###
|
210
|
+
|
211
|
+
# Return first item in tree _after_ given
|
212
|
+
# key (eg whose key is > given key).
|
213
|
+
#
|
214
|
+
# Given item does not need to be in tree.
|
215
|
+
def upper_bound(key)
|
216
|
+
# Return traversal stack to key
|
217
|
+
leaf, stack = walk_towards_key(key)
|
218
|
+
|
219
|
+
# Pop the stack until empty
|
220
|
+
until stack.empty?
|
221
|
+
node, node_id = *stack.last
|
222
|
+
|
223
|
+
# If current item is leaf, return if
|
224
|
+
# item.key > key
|
225
|
+
if node.leaf?
|
226
|
+
if node.item.key.to_bn > key.to_bn
|
227
|
+
return node.item
|
228
|
+
end
|
229
|
+
|
230
|
+
# If inner node, select next higher
|
231
|
+
# branch to traverse
|
232
|
+
else
|
233
|
+
branch = nil
|
234
|
+
if v2?
|
235
|
+
if node.common_prefix?(key)
|
236
|
+
branch = node_id.select_branch(key) + 1
|
237
|
+
elsif key.to_bn < node.common.to_bn
|
238
|
+
branch = 0
|
239
|
+
else
|
240
|
+
branch = 16
|
241
|
+
end
|
242
|
+
else
|
243
|
+
branch = node_id.select_branch(key) + 1
|
244
|
+
end
|
245
|
+
|
246
|
+
# Start traversal from selected branch
|
247
|
+
# on up, returning first node below
|
248
|
+
# non-empty branches
|
249
|
+
inner = node
|
250
|
+
branch.upto(15) { |b|
|
251
|
+
next if inner.empty_branch?(b)
|
252
|
+
node = descend_throw inner, b
|
253
|
+
leaf, stack = first_below node, stack, b
|
254
|
+
raise Error::MissingNode unless leaf
|
255
|
+
return leaf.item
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
259
|
+
stack.pop
|
260
|
+
end
|
261
|
+
|
262
|
+
# If no items > this one, return map_end
|
263
|
+
map_end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Descends Inner Tree Nodes in NodeStore
|
267
|
+
# until we reach non-inner-node.
|
268
|
+
#
|
269
|
+
# Return complete stack of walk.
|
270
|
+
def walk_towards_key(key)
|
271
|
+
stack = []
|
272
|
+
|
273
|
+
# Start with root node
|
274
|
+
in_node = root
|
275
|
+
node_id = NodeID.new
|
276
|
+
|
277
|
+
# Iterate until node is no longer inner
|
278
|
+
while in_node.inner?
|
279
|
+
stack.push [in_node, node_id]
|
280
|
+
|
281
|
+
return nil, stack if v2? && in_node.common_prefix?(key)
|
282
|
+
|
283
|
+
# Select tree branch which has key
|
284
|
+
# we are looking for, ensure it is not empty
|
285
|
+
branch = node_id.select_branch(key)
|
286
|
+
return nil, stack if in_node.empty_branch?(branch)
|
287
|
+
|
288
|
+
# Descend to branch node
|
289
|
+
in_node = descend_throw in_node, branch
|
290
|
+
if v2?
|
291
|
+
if in_node.inner?
|
292
|
+
node_id = NodeID.new :depth => in_node.depth,
|
293
|
+
:key => in_node.common
|
294
|
+
else
|
295
|
+
node_id = NodeID.new :depth => 64,
|
296
|
+
:key => in_node.key
|
297
|
+
end
|
298
|
+
|
299
|
+
else
|
300
|
+
# Get ID of branch node
|
301
|
+
node_id = node_id.child_node_id branch
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Push final node (assumably corresponding to key)
|
306
|
+
stack.push [in_node, node_id]
|
307
|
+
|
308
|
+
# Return final node (corresponding to key) and stack
|
309
|
+
return in_node, stack
|
310
|
+
end
|
311
|
+
|
312
|
+
# Descend to specified branch in parent,
|
313
|
+
# throw exception if we cannot
|
314
|
+
def descend_throw(parent, branch)
|
315
|
+
ret = descend(parent, branch)
|
316
|
+
raise Errors::MissingNode, parent.child_hash(branch) if !ret &&
|
317
|
+
!parent.empty_branch?(branch)
|
318
|
+
ret
|
319
|
+
end
|
320
|
+
|
321
|
+
# Retreive node from nodestore corresponding to
|
322
|
+
# specified branch of parent.
|
323
|
+
def descend(parent, branch)
|
324
|
+
ret = parent.child(branch)
|
325
|
+
return ret if ret # || !backed? # TODO (backed)
|
326
|
+
|
327
|
+
node = fetch_node_nt(parent.child_hash(branch))
|
328
|
+
return nil if !node || inconsistent_node?(node)
|
329
|
+
|
330
|
+
node = parent.canonicalize_child(branch, node)
|
331
|
+
node
|
332
|
+
end
|
333
|
+
|
334
|
+
# Returns first leaf node at or below the specified
|
335
|
+
# node.
|
336
|
+
#
|
337
|
+
# @param node to evaluation
|
338
|
+
# @param stack ancestor node stack
|
339
|
+
# @param branch this node is on
|
340
|
+
def first_below(node, stack, branch=0)
|
341
|
+
# Return node if node is a leaf
|
342
|
+
if node.leaf?
|
343
|
+
stack.push [node, node.peek_item.key]
|
344
|
+
return node, stack
|
345
|
+
end
|
346
|
+
|
347
|
+
# Append node to ancestry stack for traversal
|
348
|
+
if stack.empty?
|
349
|
+
stack.push [node, NodeID.new]
|
350
|
+
|
351
|
+
else
|
352
|
+
if v2?
|
353
|
+
stack.push [node, NodeID.new(:depth => node.depth,
|
354
|
+
:key => node.common)]
|
355
|
+
|
356
|
+
else
|
357
|
+
stack.push [node, stack.last.last.child_node_id(branch)]
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# Iterate over non-empty branches
|
362
|
+
i = 0
|
363
|
+
while i < 16
|
364
|
+
if !node.empty_branch?(i)
|
365
|
+
# descend into branch
|
366
|
+
node = descend_throw(node, i)
|
367
|
+
raise if stack.empty?
|
368
|
+
|
369
|
+
# Return first leaf
|
370
|
+
if node.leaf?
|
371
|
+
stack.push [node, node.peek_item.key]
|
372
|
+
return node, stack
|
373
|
+
end
|
374
|
+
|
375
|
+
# Continue tree descent at new level
|
376
|
+
if v2?
|
377
|
+
stack.push [node, NodeID.new(:depth => node.depth,
|
378
|
+
:key => node.common)]
|
379
|
+
|
380
|
+
else
|
381
|
+
stack.push [node, stack.last.last.child_node_id(branch)]
|
382
|
+
end
|
383
|
+
|
384
|
+
i = 0 # scan all 16 branches of this new node
|
385
|
+
|
386
|
+
else
|
387
|
+
i += 1
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# No node found, return nil and the stack
|
392
|
+
return nil, stack
|
393
|
+
end
|
394
|
+
|
395
|
+
public
|
396
|
+
|
397
|
+
# Returns first directory index in the specified root index
|
398
|
+
#
|
399
|
+
# @see cdir_next below
|
400
|
+
def cdir_first(root_index)
|
401
|
+
node = read(root_index)
|
402
|
+
raise unless node # never probe for dirs
|
403
|
+
cdir_next(root_index, node, 0)
|
404
|
+
end
|
405
|
+
|
406
|
+
# Returns the key of the index in the the node's
|
407
|
+
# "indexes" field corresponding to 'dir_entry'.
|
408
|
+
#
|
409
|
+
# Also returns directory node which contains
|
410
|
+
# key of the node being returned.
|
411
|
+
#
|
412
|
+
# Also returns dir_entry index of next record in
|
413
|
+
# directory node.
|
414
|
+
#
|
415
|
+
# This method handles the special case where dir_entry
|
416
|
+
# is greater than the local indexes size but the
|
417
|
+
# 'index_next' is also set. In this case, index
|
418
|
+
# traversal will continue on the next SLE node
|
419
|
+
# whose lookup key is calculated from the root
|
420
|
+
# index and 'index_next' value. In this case
|
421
|
+
# the directory node and next dir_entry will be
|
422
|
+
# set appropriately and returned.
|
423
|
+
#
|
424
|
+
# @param root_index top level index of the tree
|
425
|
+
# being traversed.
|
426
|
+
# @param node SLE containing 'indexes' field from
|
427
|
+
# which the 'dir_entry'th index will be returned
|
428
|
+
# @param dir_entry numerical array index to return
|
429
|
+
# from 'indexes'
|
430
|
+
def cdir_next(root_index, node, dir_entry)
|
431
|
+
indexes = node.field(:vector256, :indexes)
|
432
|
+
raise unless dir_entry <= indexes.size
|
433
|
+
|
434
|
+
if dir_entry >= indexes.size
|
435
|
+
nxt = node.field(:uint64, :index_next)
|
436
|
+
return nil unless nxt
|
437
|
+
|
438
|
+
nxt = read(NodeStore::Indexes::page(root_index, nxt))
|
439
|
+
return nil unless nxt
|
440
|
+
|
441
|
+
return cdir_next(root_index, nxt, 0)
|
442
|
+
end
|
443
|
+
|
444
|
+
return indexes[dir_entry], node, (dir_entry + 1)
|
445
|
+
end
|
446
|
+
end # class SHAMap
|
447
|
+
end # module XRBP
|