xrbp 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|