lib/tracksperanto/accumulator.rb in tracksperanto-2.2.4 vs lib/tracksperanto/accumulator.rb in tracksperanto-2.3.0

- old
+ new

@@ -15,58 +15,89 @@ # # a.clear # ensure that the file is deleted class Tracksperanto::Accumulator include Enumerable + DELIM = "\n" + # Returns the number of objects stored so far attr_reader :size def initialize @store = Tracksperanto::BufferIO.new - @size = 0 - @byte_size = 0 super end + def empty? + @size.zero? + end + # Store an object def push(object_to_store) - @store.seek(@byte_size) - blob = marshal_object(object_to_store) @store.write(blob) @size += 1 - @byte_size = @byte_size + blob.size object_to_store end alias_method :<<, :push # Retreive each stored object in succession. All other Enumerable # methods are also available (but be careful with Enumerable#map and to_a) def each - @store.rewind - @size.times { yield(recover_object) } + with_separate_read_io do | iterable | + @size.times { yield(recover_object_from(iterable)) } + end end # Calls close! on the datastore and deletes the objects in it def clear @store.close! @size = 0 - @offsets = [] end + # Retreive a concrete object at index + def [](idx) + idx.respond_to?(:each) ? idx.map{|i| recover_at(i) } : recover_at(idx) + end + private + def recover_at(idx) + with_separate_read_io do | iterable | + iterable.seek(0) + + # Do not unmarshal anything but wind the IO in fixed offsets + idx.times do + skip_bytes = iterable.gets("\t").to_i + iterable.seek(iterable.pos + skip_bytes) + end + + recover_object_from(iterable) + end + end + + # We first ensure that we have a disk-backed file, then reopen it as read-only + # and iterate through that (we will have one IO handle per loop nest) + def with_separate_read_io + iterable = File.open(@store.to_file.path, "r") + yield(iterable) + ensure + iterable.close + end + def marshal_object(object_to_store) d = Marshal.dump(object_to_store) - blob = [d.size, "\t", d, "\n"].join + blob = [d.size, "\t", d, DELIM].join end - def recover_object + def recover_object_from(io) # Up to the tab is the amount of bytes to read - demarshal_bytes = @store.gets("\t").strip.to_i - Marshal.load(@store.read(demarshal_bytes)) + demarshal_bytes = io.gets("\t").to_i + blob = io.read(demarshal_bytes) + + Marshal.load(blob) end end