perobs 3.0.1 → 3.0.2

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