perobs 3.0.1 → 4.3.0

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.
Files changed (75) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +19 -18
  3. data/lib/perobs.rb +2 -0
  4. data/lib/perobs/Array.rb +68 -21
  5. data/lib/perobs/BTree.rb +110 -54
  6. data/lib/perobs/BTreeBlob.rb +14 -13
  7. data/lib/perobs/BTreeDB.rb +11 -10
  8. data/lib/perobs/BTreeNode.rb +551 -197
  9. data/lib/perobs/BTreeNodeCache.rb +10 -8
  10. data/lib/perobs/BTreeNodeLink.rb +11 -1
  11. data/lib/perobs/BigArray.rb +285 -0
  12. data/lib/perobs/BigArrayNode.rb +1002 -0
  13. data/lib/perobs/BigHash.rb +246 -0
  14. data/lib/perobs/BigTree.rb +197 -0
  15. data/lib/perobs/BigTreeNode.rb +873 -0
  16. data/lib/perobs/Cache.rb +47 -22
  17. data/lib/perobs/ClassMap.rb +2 -2
  18. data/lib/perobs/ConsoleProgressMeter.rb +61 -0
  19. data/lib/perobs/DataBase.rb +4 -3
  20. data/lib/perobs/DynamoDB.rb +62 -20
  21. data/lib/perobs/EquiBlobsFile.rb +174 -59
  22. data/lib/perobs/FNV_Hash_1a_64.rb +54 -0
  23. data/lib/perobs/FlatFile.rb +536 -242
  24. data/lib/perobs/FlatFileBlobHeader.rb +120 -84
  25. data/lib/perobs/FlatFileDB.rb +58 -27
  26. data/lib/perobs/FuzzyStringMatcher.rb +175 -0
  27. data/lib/perobs/Hash.rb +129 -35
  28. data/lib/perobs/IDList.rb +144 -0
  29. data/lib/perobs/IDListPage.rb +107 -0
  30. data/lib/perobs/IDListPageFile.rb +180 -0
  31. data/lib/perobs/IDListPageRecord.rb +142 -0
  32. data/lib/perobs/LockFile.rb +3 -0
  33. data/lib/perobs/Object.rb +28 -20
  34. data/lib/perobs/ObjectBase.rb +53 -10
  35. data/lib/perobs/PersistentObjectCache.rb +142 -0
  36. data/lib/perobs/PersistentObjectCacheLine.rb +99 -0
  37. data/lib/perobs/ProgressMeter.rb +97 -0
  38. data/lib/perobs/SpaceManager.rb +273 -0
  39. data/lib/perobs/SpaceTree.rb +63 -47
  40. data/lib/perobs/SpaceTreeNode.rb +134 -115
  41. data/lib/perobs/SpaceTreeNodeLink.rb +1 -1
  42. data/lib/perobs/StackFile.rb +1 -1
  43. data/lib/perobs/Store.rb +180 -70
  44. data/lib/perobs/version.rb +1 -1
  45. data/perobs.gemspec +4 -4
  46. data/test/Array_spec.rb +48 -39
  47. data/test/BTreeDB_spec.rb +2 -2
  48. data/test/BTree_spec.rb +50 -1
  49. data/test/BigArray_spec.rb +261 -0
  50. data/test/BigHash_spec.rb +152 -0
  51. data/test/BigTreeNode_spec.rb +153 -0
  52. data/test/BigTree_spec.rb +259 -0
  53. data/test/EquiBlobsFile_spec.rb +105 -5
  54. data/test/FNV_Hash_1a_64_spec.rb +59 -0
  55. data/test/FlatFileDB_spec.rb +199 -15
  56. data/test/FuzzyStringMatcher_spec.rb +261 -0
  57. data/test/Hash_spec.rb +27 -16
  58. data/test/IDList_spec.rb +77 -0
  59. data/test/LegacyDBs/LegacyDB.rb +155 -0
  60. data/test/LegacyDBs/version_3/class_map.json +1 -0
  61. data/test/LegacyDBs/version_3/config.json +1 -0
  62. data/test/LegacyDBs/version_3/database.blobs +0 -0
  63. data/test/LegacyDBs/version_3/database_spaces.blobs +0 -0
  64. data/test/LegacyDBs/version_3/index.blobs +0 -0
  65. data/test/LegacyDBs/version_3/version +1 -0
  66. data/test/LockFile_spec.rb +9 -6
  67. data/test/Object_spec.rb +5 -5
  68. data/test/SpaceManager_spec.rb +176 -0
  69. data/test/SpaceTree_spec.rb +27 -9
  70. data/test/Store_spec.rb +353 -206
  71. data/test/perobs_spec.rb +7 -3
  72. data/test/spec_helper.rb +9 -4
  73. metadata +59 -16
  74. data/lib/perobs/SpaceTreeNodeCache.rb +0 -76
  75. data/lib/perobs/TreeDB.rb +0 -277
@@ -0,0 +1,97 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # = ProgressMeter.rb -- Persistent Ruby Object Store
4
+ #
5
+ # Copyright (c) 2018 by Chris Schlaeger <chris@taskjuggler.org>
6
+ #
7
+ # MIT License
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+
28
+ require 'time'
29
+
30
+ module PEROBS
31
+
32
+ # This is the base class for all ProgressMeter classes. It only logs into
33
+ # the PEROBS log. You need to create a derived class that overloads
34
+ # print_bar() and print_time() to provide more fancy outputs.
35
+ class ProgressMeter
36
+
37
+ def initialize
38
+ @name = nil
39
+ @max_value = nil
40
+ @current_value = nil
41
+ @start_time = nil
42
+ @end_time = nil
43
+ end
44
+
45
+ def start(name, max_value)
46
+ @name = name
47
+ unless max_value >= 0
48
+ raise ArgumentError, "Maximum value (#{max_value}) must be larger " +
49
+ "or equal to 0"
50
+ end
51
+ @max_value = max_value
52
+ @current_value = 0
53
+ @start_time = Time.now
54
+ @end_time = nil
55
+ print_bar
56
+
57
+ if block_given?
58
+ yield(self)
59
+ done
60
+ end
61
+ end
62
+
63
+ def update(value)
64
+ return unless (value_i = value.to_i) > @current_value
65
+
66
+ @current_value = value_i
67
+ print_bar
68
+ end
69
+
70
+ def done
71
+ @end_time = Time.now
72
+ print_time
73
+ PEROBS.log.info "#{@name} completed in " +
74
+ secsToHMS(@end_time - @start_time)
75
+ end
76
+
77
+ private
78
+
79
+ def print_bar
80
+ end
81
+
82
+ def print_time
83
+ end
84
+
85
+ def secsToHMS(secs)
86
+ secs = secs.to_i
87
+ s = secs % 60
88
+ mins = secs / 60
89
+ m = mins % 60
90
+ h = mins / 60
91
+ "#{h}:#{'%02d' % m}:#{'%02d' % s}"
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
@@ -0,0 +1,273 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # = SpaceManager.rb -- Persistent Ruby Object Store
4
+ #
5
+ # Copyright (c) 2020 by Chris Schlaeger <chris@taskjuggler.org>
6
+ #
7
+ # MIT License
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+
28
+ require 'perobs/BTree'
29
+ require 'perobs/EquiBlobsFile'
30
+ require 'perobs/FlatFile'
31
+ require 'perobs/FlatFileBlobHeader'
32
+
33
+ module PEROBS
34
+
35
+ # The SpaceManager is used to keep a list of all the empty spaces in a
36
+ # FlatFileDB file. An empty space is described by its starting address and
37
+ # its length in bytes. The SpaceManager keeps a list of all the spaces and
38
+ # can find the best fit space when a new blob needs to be added to the
39
+ # FlatFileDB.
40
+ #
41
+ # The SpaceManager uses two files to store the list. The first is a file
42
+ # with the actual addresses. This is a set of linked address lists. Each
43
+ # list holds the addresses for spaces that have exactly the same size. The
44
+ # second file is a BTree file that serves as the index. It is used to map
45
+ # the length of a space to the address of the linked list for that
46
+ # particular length. The linked list consists of elements that only hold 2
47
+ # items. The actual address in the FlatFileDB and the address of the next
48
+ # entry in the linked list in the list file.
49
+ class SpaceManager
50
+
51
+ attr_reader :added_spaces, :recycled_spaces, :failed_requests
52
+
53
+ def initialize(db_dir, progressmeter, btree_order = 65)
54
+ @db_dir = db_dir
55
+ @progressmeter = progressmeter
56
+
57
+ @index = BTree.new(@db_dir, 'space_index', btree_order, @progressmeter)
58
+ # The space list contains blobs that have each 2 entries. The address of
59
+ # the space in the FlatFile and the address of the next blob in the
60
+ # space list file that is an entry for the same space size. An address
61
+ # of 0 marks the end of the list.
62
+ @list = EquiBlobsFile.new(@db_dir, 'space_list', @progressmeter, 2 * 8, 1)
63
+ end
64
+
65
+ def open
66
+ @index.open
67
+ @list.open
68
+ reset_stats
69
+ end
70
+
71
+ def close
72
+ if @index.is_open?
73
+ PEROBS.log.info "SpaceManager has currently #{@list.total_entries} " +
74
+ "used blobs and #{@list.total_spaces} unused blobs in list " +
75
+ "EquiBlobsFile"
76
+ PEROBS.log.info "#{@added_spaces} were added, #{@recycled_spaces} " +
77
+ "spaces were recycled and #{@failed_requests} requests failed"
78
+
79
+ @list.close
80
+ @index.close
81
+ end
82
+ end
83
+
84
+ def is_open?
85
+ @index.is_open?
86
+ end
87
+
88
+ def sync
89
+ @list.sync
90
+ @index.sync
91
+ end
92
+
93
+ def add_space(address, length)
94
+ if (list_entry_addr = @index.get(length))
95
+ # There is already at least one move entry for this length.
96
+ new_list_entry_addr = insert_space_in_list(address, list_entry_addr)
97
+ else
98
+ new_list_entry_addr = insert_space_in_list(address, 0)
99
+ end
100
+ @index.insert(length, new_list_entry_addr)
101
+ @added_spaces += 1
102
+ end
103
+
104
+ def has_space?(address, length)
105
+ if (list_entry_addr = @index.get(length))
106
+ while list_entry_addr > 0
107
+ blob = @list.retrieve_blob(list_entry_addr)
108
+ space_address, next_entry_addr = blob.unpack('QQ')
109
+ return true if space_address == address
110
+ list_entry_addr = next_entry_addr
111
+ end
112
+ end
113
+
114
+ false
115
+ end
116
+
117
+ def get_space(length)
118
+ # We use a simple exact fit strategy. All attempts to use a more
119
+ # elaborate scheme were actually less efficient. Non-exact matches
120
+ # generate new spaces for the remainder and fragment the blob file with
121
+ # lots of unusable small spaces. Most applications seem to have
122
+ # clustered their blob sizes around a number of popular sizes. So exact
123
+ # match is very efficient to implement and results in the highest
124
+ # probability that a space will be reused soon.
125
+ list_entry_addr = @index.get(length)
126
+
127
+ if list_entry_addr
128
+ blob = @list.retrieve_blob(list_entry_addr)
129
+ space_address, next_entry_addr = blob.unpack('QQ')
130
+ @list.delete_blob(list_entry_addr)
131
+
132
+ if next_entry_addr > 0
133
+ # Update the index entry for the length to point to the
134
+ # following space list entry.
135
+ @index.insert(length, next_entry_addr)
136
+ else
137
+ # The space list for this length is empty. Remove the entry
138
+ # from the index.
139
+ @index.remove(length)
140
+ end
141
+ @recycled_spaces += 1
142
+
143
+ # We return the length to remain compatible with the old SpaceTree
144
+ # API.
145
+ return [ space_address, length ]
146
+ end
147
+
148
+ @failed_requests += 1
149
+ nil
150
+ end
151
+
152
+ def clear
153
+ @list.clear
154
+ @index.clear
155
+ reset_stats
156
+ end
157
+
158
+ def erase
159
+ @list.erase
160
+ @index.erase
161
+ end
162
+
163
+ def check(flat_file = nil)
164
+ sync
165
+ return false unless @index.check
166
+ return false unless @list.check
167
+
168
+ smallest_space = nil
169
+ largest_space = nil
170
+ total_space_bytes = 0
171
+ space_distribution = ::Hash.new(0)
172
+
173
+ @index.each do |length, list_entry_addr|
174
+ if list_entry_addr <= 0
175
+ PEROBS.log.error "list_entry_addr (#{list_entry_addr}) " +
176
+ "must be positive"
177
+ return false
178
+ end
179
+
180
+ # Detect smallest and largest space
181
+ if smallest_space.nil? || length < smallest_space
182
+ smallest_space = length
183
+ end
184
+ if largest_space.nil? || length > largest_space
185
+ largest_space = length
186
+ end
187
+
188
+ known_addresses = [ list_entry_addr ]
189
+ entries = 0
190
+ while list_entry_addr > 0
191
+ entries += 1
192
+ unless (blob = @list.retrieve_blob(list_entry_addr))
193
+ PEROBS.log.error "SpaceManager points to non-existing " +
194
+ "space list entry at address #{list_entry_addr}"
195
+ return false
196
+ end
197
+ space_address, next_entry_addr = blob.unpack('QQ')
198
+
199
+ if known_addresses.include?(next_entry_addr)
200
+ PEROBS.log.error "Space list is cyclic: "
201
+ "#{known_addresses + next_entry_addr}"
202
+ return false
203
+ end
204
+ if flat_file &&
205
+ !flat_file.has_space?(space_address, length)
206
+ PEROBS.log.error "SpaceManager has space at offset " +
207
+ "#{space_address} of size #{length} that isn't " +
208
+ "available in the FlatFile."
209
+ return false
210
+ end
211
+ list_entry_addr = next_entry_addr
212
+ end
213
+
214
+ total_space_bytes += length * entries
215
+ space_distribution[msb(length)] += entries
216
+ end
217
+
218
+ PEROBS.log.info "SpaceManager stats: smallest: #{smallest_space}; " +
219
+ "largest: #{largest_space}; total bytes: #{total_space_bytes}; " +
220
+ "distribution: " +
221
+ "#{space_distribution.map { |l, c| "#{2 ** (l - 1)}-#{2 ** l - 1}:#{c}; " }}"
222
+
223
+ true
224
+ end
225
+
226
+ def to_a
227
+ a = []
228
+
229
+ @index.each do |length, list_entry_addr|
230
+ while list_entry_addr > 0
231
+ blob = @list.retrieve_blob(list_entry_addr)
232
+ space_address, next_entry_addr = blob.unpack('QQ')
233
+
234
+ a << [ space_address, length ]
235
+
236
+ list_entry_addr = next_entry_addr
237
+ end
238
+ end
239
+
240
+ a.sort { |a, b| a[0] <=> b[0] }
241
+ end
242
+
243
+ private
244
+
245
+ def insert_space_in_list(next_element_addr, space_address)
246
+ blob = [ next_element_addr, space_address ].pack('QQ')
247
+ @list.store_blob(blob_addr = @list.free_address, blob)
248
+
249
+ blob_addr
250
+ end
251
+
252
+ def msb(i)
253
+ return 63 if i < 0
254
+
255
+ bit = 0
256
+ while (i > 0)
257
+ bit += 1
258
+ i = i >> 1
259
+ end
260
+
261
+ bit
262
+ end
263
+
264
+ def reset_stats
265
+ @added_spaces = 0
266
+ @recycled_spaces = 0
267
+ @failed_requests = 0
268
+ end
269
+
270
+ end
271
+
272
+ end
273
+
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # = SpaceTree.rb -- Persistent Ruby Object Store
4
4
  #
5
- # Copyright (c) 2016, 2017 by Chris Schlaeger <chris@taskjuggler.org>
5
+ # Copyright (c) 2016, 2017, 2018 by Chris Schlaeger <chris@taskjuggler.org>
6
6
  #
7
7
  # MIT License
8
8
  #
@@ -27,7 +27,7 @@
27
27
 
28
28
  require 'perobs/Log'
29
29
  require 'perobs/EquiBlobsFile'
30
- require 'perobs/SpaceTreeNodeCache'
30
+ require 'perobs/PersistentObjectCache'
31
31
  require 'perobs/SpaceTreeNode'
32
32
  require 'perobs/FlatFile'
33
33
 
@@ -40,40 +40,63 @@ module PEROBS
40
40
  # size which drastically simplifies the backing store operation.
41
41
  class SpaceTree
42
42
 
43
- attr_reader :nodes
43
+ attr_reader :nodes, :cache, :progressmeter
44
44
 
45
45
  # Manage the free spaces tree in the specified directory
46
46
  # @param dir [String] directory path of an existing directory
47
- def initialize(dir)
47
+ def initialize(dir, progressmeter)
48
48
  @dir = dir
49
+ @progressmeter = progressmeter
49
50
 
50
51
  # This EquiBlobsFile contains the nodes of the SpaceTree.
51
- @nodes = EquiBlobsFile.new(@dir, 'database_spaces',
52
+ @nodes = EquiBlobsFile.new(@dir, 'database_spaces', progressmeter,
52
53
  SpaceTreeNode::NODE_BYTES, 1)
53
54
 
54
- @node_cache = SpaceTreeNodeCache.new(128)
55
+ # Benchmark runs showed a cache size of 128 to be a good compromise
56
+ # between read and write performance trade-offs and memory consumption.
57
+ @cache = PersistentObjectCache.new(256, 256, SpaceTreeNode, self)
55
58
  end
56
59
 
57
60
  # Open the SpaceTree file.
58
61
  def open
59
62
  @nodes.open
60
- @node_cache.clear
61
- @root = SpaceTreeNode.new(self, nil, @nodes.total_entries == 0 ?
62
- nil : @nodes.first_entry)
63
- @node_cache.insert(@root)
63
+ @cache.clear
64
+ node = @nodes.total_entries == 0 ?
65
+ SpaceTreeNode::create(self) :
66
+ SpaceTreeNode::load(self, @nodes.first_entry)
67
+ @root_address = node.node_address
64
68
  end
65
69
 
66
70
  # Close the SpaceTree file.
67
71
  def close
72
+ @cache.flush(true)
68
73
  @nodes.close
69
- @root = nil
70
- @node_cache.clear
74
+ @root_address = nil
75
+ @cache.clear
71
76
  end
72
77
 
78
+ # @return true if file is currently open.
79
+ def is_open?
80
+ !@root_address.nil?
81
+ end
82
+
83
+ # Flush all pending writes to the file system.
84
+ def sync
85
+ @cache.flush(true)
86
+ @nodes.sync
87
+ end
88
+
89
+ # Set a new root node for the SpaceTree
90
+ # @param node [SpaceTreeNode]
73
91
  def set_root(node)
74
- @root = node
92
+ @root_address = node.node_address
93
+ @nodes.first_entry = node.node_address
75
94
  end
76
95
 
96
+ # Return the SpaceTreeNode that is the root of the SpaceTree.
97
+ def root
98
+ @root_address ? @cache.get(@root_address) : nil
99
+ end
77
100
 
78
101
  # Erase the SpaceTree file. This method cannot be called while the file is
79
102
  # open.
@@ -88,7 +111,13 @@ module PEROBS
88
111
  if size <= 0
89
112
  PEROBS.log.fatal "Size (#{size}) must be larger than 0."
90
113
  end
91
- @root.add_space(address, size)
114
+ # The following check is fairly costly and should never trigger unless
115
+ # there is a bug in the PEROBS code. Only use this for debugging.
116
+ #if has_space?(address, size)
117
+ # PEROBS.log.fatal "The space with address #{address} and size " +
118
+ # "#{size} can't be added twice."
119
+ #end
120
+ root.add_space(address, size)
92
121
  end
93
122
 
94
123
  # Get a space that has at least the requested size.
@@ -99,10 +128,10 @@ module PEROBS
99
128
  PEROBS.log.fatal "Size (#{size}) must be larger than 0."
100
129
  end
101
130
 
102
- if (address_size = @root.find_matching_space(size))
131
+ if (address_size = root.find_matching_space(size))
103
132
  # First we try to find an exact match.
104
133
  return address_size
105
- elsif (address_size = @root.find_equal_or_larger_space(size))
134
+ elsif (address_size = root.find_equal_or_larger_space(size))
106
135
  return address_size
107
136
  else
108
137
  return nil
@@ -112,38 +141,15 @@ module PEROBS
112
141
  # Delete the node at the given address in the SpaceTree file.
113
142
  # @param address [Integer] address in file
114
143
  def delete_node(address)
115
- @node_cache.delete(address)
144
+ @cache.delete(address)
116
145
  @nodes.delete_blob(address)
117
146
  end
118
147
 
119
148
  # Clear all pools and forget any registered spaces.
120
149
  def clear
121
150
  @nodes.clear
122
- @node_cache.clear
123
- @root = SpaceTreeNode.new(self)
124
- @node_cache.insert(@root)
125
- end
126
-
127
- # Create a new SpaceTreeNode.
128
- # @param parent [SpaceTreeNode] parent node
129
- # @param blob_address [Integer] address of the free space
130
- # @param size [Integer] size of the free space
131
- def new_node(parent, blob_address, size)
132
- node = SpaceTreeNode.new(self, parent, nil, blob_address, size)
133
- @node_cache.insert(node)
134
- end
135
-
136
- # Return the SpaceTreeNode that matches the given node address. If a blob
137
- # address and size are given, a new node is created instead of being read
138
- # from the file.
139
- # @param node_address [Integer] Address of the node in the SpaceTree file
140
- # @return [SpaceTreeNode]
141
- def get_node(node_address)
142
- if (node = @node_cache.get(node_address))
143
- return node
144
- end
145
-
146
- @node_cache.insert(SpaceTreeNode.new(self, nil, node_address))
151
+ @cache.clear
152
+ @root_address = SpaceTreeNode::create(self).node_address
147
153
  end
148
154
 
149
155
  # Check if there is a space in the free space lists that matches the
@@ -152,27 +158,37 @@ module PEROBS
152
158
  # @param [Integer] size Length of the space in bytes
153
159
  # @return [Boolean] True if space is found, false otherwise
154
160
  def has_space?(address, size)
155
- @root.has_space?(address, size)
161
+ root.has_space?(address, size)
156
162
  end
157
163
 
158
164
  # Check if the index is OK and matches the flat_file data (if given).
159
165
  # @param flat_file [FlatFile] Flat file to compare with
160
166
  # @return True if space list matches, flase otherwise
161
167
  def check(flat_file = nil)
162
- @nodes.check
163
- @root.check(flat_file)
168
+ sync
169
+ return false unless @nodes.check
170
+ root.check(flat_file, @nodes.total_entries)
171
+ end
172
+
173
+ # Iterate over all entries and yield address and size.
174
+ def each
175
+ root.each do |node, mode, stack|
176
+ if mode == :on_enter
177
+ yield(node.blob_address, node.size)
178
+ end
179
+ end
164
180
  end
165
181
 
166
182
  # Complete internal tree data structure as textual tree.
167
183
  # @return [String]
168
184
  def to_s
169
- @root.to_tree_s
185
+ root.to_tree_s
170
186
  end
171
187
 
172
188
  # Convert the tree into an Array of [address, size] touples.
173
189
  # @return [Array]
174
190
  def to_a
175
- @root.to_a
191
+ root.to_a
176
192
  end
177
193
 
178
194
  end