perobs 3.0.1 → 3.0.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.
data/lib/perobs/Hash.rb CHANGED
@@ -92,7 +92,7 @@ module PEROBS
92
92
 
93
93
  # Return a list of all object IDs of all persistend objects that this Hash
94
94
  # is referencing.
95
- # @return [Array of Fixnum or Bignum] IDs of referenced objects
95
+ # @return [Array of Integer] IDs of referenced objects
96
96
  def _referenced_object_ids
97
97
  @data.each_value.select { |v| v && v.respond_to?(:is_poxreference?) }.
98
98
  map { |o| o.id }
@@ -100,7 +100,7 @@ module PEROBS
100
100
 
101
101
  # This method should only be used during store repair operations. It will
102
102
  # delete all referenced to the given object ID.
103
- # @param id [Fixnum/Bignum] targeted object ID
103
+ # @param id [Integer] targeted object ID
104
104
  def _delete_reference_to_id(id)
105
105
  @data.delete_if do |k, v|
106
106
  v && v.respond_to?(:is_poxreference?) && v.id == id
data/lib/perobs/Object.rb CHANGED
@@ -140,7 +140,7 @@ module PEROBS
140
140
 
141
141
  # Return a list of all object IDs that the attributes of this instance are
142
142
  # referencing.
143
- # @return [Array of Fixnum or Bignum] IDs of referenced objects
143
+ # @return [Array of Integer] IDs of referenced objects
144
144
  def _referenced_object_ids
145
145
  ids = []
146
146
  _all_attributes.each do |attr|
@@ -152,7 +152,7 @@ module PEROBS
152
152
 
153
153
  # This method should only be used during store repair operations. It will
154
154
  # delete all references to the given object ID.
155
- # @param id [Fixnum/Bignum] targeted object ID
155
+ # @param id [Integer] targeted object ID
156
156
  def _delete_reference_to_id(id)
157
157
  _all_attributes.each do |attr|
158
158
  ivar = ('@' + attr.to_s).to_sym
@@ -207,7 +207,7 @@ module PEROBS
207
207
  end
208
208
 
209
209
  # Restore the object state from the storage back-end.
210
- # @param level [Fixnum] the transaction nesting level
210
+ # @param level [Integer] the transaction nesting level
211
211
  def _restore(level)
212
212
  # Find the most recently stored state of this object. This could be on
213
213
  # any previous stash level or in the regular object DB. If the object
@@ -40,7 +40,7 @@ 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
44
44
 
45
45
  # Manage the free spaces tree in the specified directory
46
46
  # @param dir [String] directory path of an existing directory
@@ -51,29 +51,44 @@ module PEROBS
51
51
  @nodes = EquiBlobsFile.new(@dir, 'database_spaces',
52
52
  SpaceTreeNode::NODE_BYTES, 1)
53
53
 
54
- @node_cache = SpaceTreeNodeCache.new(128)
54
+ @cache = SpaceTreeNodeCache.new(self, 128)
55
55
  end
56
56
 
57
57
  # Open the SpaceTree file.
58
58
  def open
59
59
  @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)
60
+ @cache.clear
61
+ node = @nodes.total_entries == 0 ?
62
+ SpaceTreeNode::create(self) :
63
+ SpaceTreeNode::load(self, @nodes.first_entry)
64
+ @root_address = node.node_address
64
65
  end
65
66
 
66
67
  # Close the SpaceTree file.
67
68
  def close
69
+ @cache.flush
68
70
  @nodes.close
69
- @root = nil
70
- @node_cache.clear
71
+ @root_address = nil
72
+ @cache.clear
71
73
  end
72
74
 
75
+ # Flush all pending writes to the file system.
76
+ def sync
77
+ @cache.flush
78
+ @nodes.sync
79
+ end
80
+
81
+ # Set a new root node for the SpaceTree
82
+ # @param node [SpaceTreeNode]
73
83
  def set_root(node)
74
- @root = node
84
+ @root_address = node.node_address
85
+ @nodes.first_entry = node.node_address
75
86
  end
76
87
 
88
+ # Return the SpaceTreeNode that is the root of the SpaceTree.
89
+ def root
90
+ @root_address ? @cache.get(@root_address) : nil
91
+ end
77
92
 
78
93
  # Erase the SpaceTree file. This method cannot be called while the file is
79
94
  # open.
@@ -88,7 +103,11 @@ module PEROBS
88
103
  if size <= 0
89
104
  PEROBS.log.fatal "Size (#{size}) must be larger than 0."
90
105
  end
91
- @root.add_space(address, size)
106
+ if has_space?(address, size)
107
+ PEROBS.log.fatal "The space with address #{address} and size #{size} " +
108
+ "can't be added twice."
109
+ end
110
+ root.add_space(address, size)
92
111
  end
93
112
 
94
113
  # Get a space that has at least the requested size.
@@ -99,10 +118,10 @@ module PEROBS
99
118
  PEROBS.log.fatal "Size (#{size}) must be larger than 0."
100
119
  end
101
120
 
102
- if (address_size = @root.find_matching_space(size))
121
+ if (address_size = root.find_matching_space(size))
103
122
  # First we try to find an exact match.
104
123
  return address_size
105
- elsif (address_size = @root.find_equal_or_larger_space(size))
124
+ elsif (address_size = root.find_equal_or_larger_space(size))
106
125
  return address_size
107
126
  else
108
127
  return nil
@@ -112,38 +131,15 @@ module PEROBS
112
131
  # Delete the node at the given address in the SpaceTree file.
113
132
  # @param address [Integer] address in file
114
133
  def delete_node(address)
115
- @node_cache.delete(address)
134
+ @cache.delete(address)
116
135
  @nodes.delete_blob(address)
117
136
  end
118
137
 
119
138
  # Clear all pools and forget any registered spaces.
120
139
  def clear
121
140
  @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))
141
+ @cache.clear
142
+ @root_address = SpaceTreeNode::create(self).node_address
147
143
  end
148
144
 
149
145
  # Check if there is a space in the free space lists that matches the
@@ -152,7 +148,7 @@ module PEROBS
152
148
  # @param [Integer] size Length of the space in bytes
153
149
  # @return [Boolean] True if space is found, false otherwise
154
150
  def has_space?(address, size)
155
- @root.has_space?(address, size)
151
+ root.has_space?(address, size)
156
152
  end
157
153
 
158
154
  # Check if the index is OK and matches the flat_file data (if given).
@@ -160,19 +156,28 @@ module PEROBS
160
156
  # @return True if space list matches, flase otherwise
161
157
  def check(flat_file = nil)
162
158
  @nodes.check
163
- @root.check(flat_file)
159
+ root.check(flat_file)
160
+ end
161
+
162
+ # Iterate over all entries and yield address and size.
163
+ def each
164
+ root.each do |node, mode, stack|
165
+ if mode == :on_enter
166
+ yield(node.blob_address, node.size)
167
+ end
168
+ end
164
169
  end
165
170
 
166
171
  # Complete internal tree data structure as textual tree.
167
172
  # @return [String]
168
173
  def to_s
169
- @root.to_tree_s
174
+ root.to_tree_s
170
175
  end
171
176
 
172
177
  # Convert the tree into an Array of [address, size] touples.
173
178
  # @return [Array]
174
179
  def to_a
175
- @root.to_a
180
+ root.to_a
176
181
  end
177
182
 
178
183
  end
@@ -51,43 +51,97 @@ module PEROBS
51
51
  # Create a new SpaceTreeNode object. If node_address is not nil, the data
52
52
  # will be read from the SpaceTree file at the given node_address.
53
53
  # @param tree [SpaceTree] Tree that the object should belong to
54
- # @param parent [SpaceTreeNode] Parent node in the tree
55
54
  # @param node_address [Integer] Address of the node in the file
56
55
  # @param blob_address [Integer] Address of the free space blob
57
56
  # @param size [Integer] Size of the free space blob
58
- def initialize(tree, parent = nil, node_address = nil, blob_address = 0,
59
- size = 0)
57
+ # @param parent [SpaceTreeNode] Parent node in the tree
58
+ # @param smaller [SpaceTreeNode] smaller node in the tree
59
+ # @param equal [SpaceTreeNode] equal node in the tree
60
+ # @param larger [SpaceTreeNode] larger node in the tree
61
+ def initialize(tree, node_address, blob_address = 0, size = 0,
62
+ parent = nil, smaller = nil, equal = nil, larger = nil)
60
63
  @tree = tree
61
- if blob_address < 0
64
+ if node_address <= 0
62
65
  PEROBS.log.fatal "Node address (#{node_address}) must be larger than 0"
63
66
  end
67
+ @node_address = node_address
68
+ if blob_address < 0
69
+ PEROBS.log.fatal "Blob address (#{node_address}) must be larger than 0"
70
+ end
64
71
  @blob_address = blob_address
65
72
  @size = size
66
- @smaller = @equal = @larger = nil
67
- @node_address = node_address
73
+ @parent = parent
74
+ @smaller = smaller
75
+ @equal = equal
76
+ @larger = larger
77
+
78
+ ObjectSpace.define_finalizer(
79
+ self, SpaceTreeNode._finalize(@tree, @node_address))
80
+ @tree.cache.insert_unmodified(self)
81
+ end
68
82
 
69
- unless node_address.nil? || node_address.is_a?(Integer)
70
- PEROBS.log.fatal "node_address is not Integer: #{node_address.class}"
71
- end
83
+ # This method generates the destructor for the objects of this class. It
84
+ # is done this way to prevent the Proc object hanging on to a reference to
85
+ # self which would prevent the object from being collected. This internal
86
+ # method is not intended for users to call.
87
+ def SpaceTreeNode._finalize(tree, node_address)
88
+ proc { tree.cache._collect(node_address) }
89
+ end
72
90
 
73
- if node_address
74
- # This must be an existing node. Try to read it and fill the instance
75
- # variables.
76
- if size != 0
77
- PEROBS.log.fatal "If node_address is not nil size must be 0"
78
- end
79
- if blob_address != 0
80
- PEROBS.log.fatal "If node_address is not nil blob_address must be 0"
81
- end
82
- unless read_node
83
- PEROBS.log.fatal "SpaceTree node at address #{node_address} " +
84
- "does not exist"
85
- end
86
- else
87
- # This is a new node. Make sure the data is written to the file.
88
- @node_address = @tree.nodes.free_address
89
- self.parent = parent
91
+ # Create a new SpaceTreeNode. This method should be used for the creation
92
+ # of new nodes instead of calling the constructor directly.
93
+ # @param tree [SpaceTree] The tree the node should belong to
94
+ # @param node_address [Integer] Address of the node in the file
95
+ # @param blob_address [Integer] Address of the free space blob
96
+ # @param size [Integer] Size of the free space blob
97
+ # @param parent [SpaceTreeNode] Parent node in the tree
98
+ def SpaceTreeNode::create(tree, blob_address = 0, size = 0, parent = nil)
99
+ node_address = tree.nodes.free_address
100
+
101
+ node = SpaceTreeNode.new(tree, node_address, blob_address, size, parent)
102
+ node.save
103
+
104
+ node
105
+ end
106
+
107
+ # Restore a node from the backing store at the given address and tree.
108
+ # @param tree [SpaceTree] The tree the node belongs to
109
+ # @param node_address [Integer] The address in the file.
110
+ def SpaceTreeNode::load(tree, node_address)
111
+ unless node_address > 0
112
+ PEROBS.log.fatal "node_address (#{node_address}) must be larger than 0"
113
+ end
114
+ unless (bytes = tree.nodes.retrieve_blob(node_address))
115
+ PEROBS.log.fatal "SpaceTreeNode at address #{node_address} does " +
116
+ "not exist"
90
117
  end
118
+
119
+ blob_address, size, parent_node_address,
120
+ smaller_node_address, equal_node_address,
121
+ larger_node_address = bytes.unpack(NODE_BYTES_FORMAT)
122
+
123
+ parent = parent_node_address != 0 ?
124
+ SpaceTreeNodeLink.new(tree, parent_node_address) : nil
125
+ smaller = smaller_node_address != 0 ?
126
+ SpaceTreeNodeLink.new(tree, smaller_node_address) : nil
127
+ equal = equal_node_address != 0 ?
128
+ SpaceTreeNodeLink.new(tree, equal_node_address) : nil
129
+ larger = larger_node_address != 0 ?
130
+ SpaceTreeNodeLink.new(tree, larger_node_address) : nil
131
+
132
+ node = SpaceTreeNode.new(tree, node_address, blob_address, size,
133
+ parent, smaller, equal, larger)
134
+
135
+ node
136
+ end
137
+
138
+ def save
139
+ bytes = [ @blob_address, @size,
140
+ @parent ? @parent.node_address : 0,
141
+ @smaller ? @smaller.node_address : 0,
142
+ @equal ? @equal.node_address : 0,
143
+ @larger ? @larger.node_address : 0].pack(NODE_BYTES_FORMAT)
144
+ @tree.nodes.store_blob(@node_address, bytes)
91
145
  end
92
146
 
93
147
  # Add a new node for the given address and size to the tree.
@@ -110,7 +164,7 @@ module PEROBS
110
164
  # There is no smaller node yet, so we create a new one as a
111
165
  # smaller child of the current node.
112
166
  node.set_link('@smaller',
113
- @tree.new_node(node, address, size))
167
+ SpaceTreeNode::create(@tree, address, size, node))
114
168
  break
115
169
  end
116
170
  elsif size > node.size
@@ -122,13 +176,13 @@ module PEROBS
122
176
  # There is no larger node yet, so we create a new one as a larger
123
177
  # child of the current node.
124
178
  node.set_link('@larger',
125
- @tree.new_node(node, address, size))
179
+ SpaceTreeNode::create(@tree, address, size, node))
126
180
  break
127
181
  end
128
182
  else
129
183
  # Same size as current node. Insert new node as equal child at top of
130
184
  # equal list.
131
- new_node = @tree.new_node(node, address, size)
185
+ new_node = SpaceTreeNode::create(@tree, address, size, node)
132
186
  new_node.set_link('@equal', node.equal)
133
187
 
134
188
  node.set_link('@equal', new_node)
@@ -147,7 +201,7 @@ module PEROBS
147
201
  node = self
148
202
  loop do
149
203
  if node.blob_address == address
150
- return true
204
+ return size == node.size
151
205
  elsif size < node.size && node.smaller
152
206
  node = node.smaller
153
207
  elsif size > node.size && node.larger
@@ -242,7 +296,7 @@ module PEROBS
242
296
  PEROBS.log.fatal "Cannot unlink unknown child node with address " +
243
297
  "#{child_node.node_address} from #{to_s}"
244
298
  end
245
- write_node
299
+ @tree.cache.insert_modified(self)
246
300
  end
247
301
 
248
302
  # Depth-first iterator for all nodes. The iterator yields the given block
@@ -339,7 +393,7 @@ module PEROBS
339
393
  @parent.set_link('@larger', node)
340
394
  else
341
395
  PEROBS.log.fatal "Cannot relink unknown child node with address " +
342
- "#{node.node_address} from #{to_s}"
396
+ "#{node.node_address} from #{parent.to_s}"
343
397
  end
344
398
  else
345
399
  if node
@@ -382,7 +436,7 @@ module PEROBS
382
436
  def set_size_and_address(size, address)
383
437
  @size = size
384
438
  @blob_address = address
385
- write_node
439
+ @tree.cache.insert_modified(self)
386
440
  end
387
441
 
388
442
  def set_link(name, node_or_address)
@@ -398,12 +452,12 @@ module PEROBS
398
452
  # Clear the node link.
399
453
  instance_variable_set(name, nil)
400
454
  end
401
- write_node
455
+ @tree.cache.insert_modified(self)
402
456
  end
403
457
 
404
458
  def parent=(p)
405
459
  @parent = p ? SpaceTreeNodeLink.new(@tree, p) : nil
406
- write_node
460
+ @tree.cache.insert_modified(self)
407
461
  end
408
462
  # Compare this node to another node.
409
463
  # @return [Boolean] true if node address is identical, false otherwise
@@ -633,40 +687,6 @@ module PEROBS
633
687
  str
634
688
  end
635
689
 
636
- private
637
-
638
- def write_node
639
- bytes = [ @blob_address, @size,
640
- @parent ? @parent.node_address : 0,
641
- @smaller ? @smaller.node_address : 0,
642
- @equal ? @equal.node_address : 0,
643
- @larger ? @larger.node_address : 0].pack(NODE_BYTES_FORMAT)
644
- @tree.nodes.store_blob(@node_address, bytes)
645
- end
646
-
647
- def read_node
648
- unless @node_address > 0
649
- PEROBS.log.fatal "@node_address must be larger than 0"
650
- end
651
- return false unless (bytes = @tree.nodes.retrieve_blob(@node_address))
652
-
653
- @blob_address, @size, parent_node_address,
654
- smaller_node_address, equal_node_address,
655
- larger_node_address = bytes.unpack(NODE_BYTES_FORMAT)
656
- # The parent address can also be 0 as the parent can rightly point back
657
- # to the root node which always has the address 0.
658
- @parent = parent_node_address != 0 ?
659
- SpaceTreeNodeLink.new(@tree, parent_node_address) : nil
660
- @smaller = smaller_node_address != 0 ?
661
- SpaceTreeNodeLink.new(@tree, smaller_node_address) : nil
662
- @equal = equal_node_address != 0 ?
663
- SpaceTreeNodeLink.new(@tree, equal_node_address) : nil
664
- @larger = larger_node_address != 0 ?
665
- SpaceTreeNodeLink.new(@tree, larger_node_address) : nil
666
-
667
- true
668
- end
669
-
670
690
  end
671
691
 
672
692
  end