databrick 0.2.2 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/databrick.rb +48 -27
  2. data/test.rb +0 -1
  3. metadata +3 -3
@@ -41,14 +41,19 @@ class DataBrick
41
41
  define_method("#{name}=") do |value|
42
42
  begin; was_at = @source.tell
43
43
  @source.seek @position
44
- update({name => value}, @source);
44
+ update({name => value});
45
45
  ensure; @source.seek was_at; end
46
46
  end
47
47
  else
48
48
  define_method("#{name}=") do |value|
49
49
  begin; was_at = @source.tell
50
50
  @source.seek @position + offset_for(name)
51
- @source.write self.class.send("blob_#{type}", value, {}, options)
51
+ @source.write(blobbed = self.class.send("blob_#{type}", value, {}, options))
52
+ if blobbed.bytesize != send("length_for_#{name}")
53
+ # refresh these silly bits because lengths are all wonky!
54
+ part_list = self.parts; next_parts = part_list[part_list.index(name) ... part_list.size]
55
+ next_parts.each { |nname| self.send("#{nname}=", self.send(nname)) }
56
+ end
52
57
  @source.flush
53
58
  ensure; @source.seek was_at; end
54
59
  end
@@ -77,21 +82,25 @@ class DataBrick
77
82
  end
78
83
 
79
84
  def update properties
85
+ was_at = @source.tell
80
86
  blob = self.class.create(to_h.merge(properties))
81
- raise 'Length of brick is longer! cannot safely update without overflowing! Aborting!' if length < blob.length
87
+ #raise 'Length of brick is longer! cannot safely update without overflowing! Aborting!' if length < blob.length
82
88
  @source.seek @position
83
- @source.write updated
89
+ @source.write blob
84
90
  @source.flush
85
- end
91
+ ensure; @source.seek was_at; end
92
+ alias_method :merge!, :update
86
93
 
87
94
  # returns the byte length of this block serialized
88
95
  def length; parts.inject(0) { |sofar, part| sofar + send("length_for_#{part}") }; end
89
96
 
90
- # add all the lengths for the bits before the specified one, to figure out the byte offset
91
- def offset_for(part)
92
- parts[0 ... parts.index(part)].inject(0) do |sofar, piece|
93
- sofar + self.send("length_for_#{piece}")
94
- end
97
+ # get the start position of a defined piece, considering any variable length bits
98
+ def offset_for(part); parts[0...parts.index(part)].inject(0) { |sofar, part| sofar + send("length_for_#{part}") }; end
99
+
100
+ # get them all
101
+ def offsets
102
+ sofar = 0
103
+ ([0] + parts.map { |part| sofar += self.send("length_for_#{part}") })
95
104
  end
96
105
 
97
106
  # returns a hash version of this DataBrick
@@ -109,20 +118,25 @@ class DataBrick
109
118
 
110
119
  # returns a stringy representation of this DataBrick's unique content
111
120
  def inspect
112
- "<#{self.class.name}##{position}: #{parts.map {|p|
113
- val = self.send(p); ".#{p}: #{val.is_a?(DataBrick) ? val.micro_inspect : val.inspect}"
114
- }.join(', ')}>"
121
+ "[#{self.class.name}+#{position}: #{parts.map {|p|
122
+ val = self.send(p); ".#{p}: #{val.respond_to?(:micro_inspect) ? val.micro_inspect : val.inspect}"
123
+ }.join(', ')}]"
115
124
  end
116
125
 
117
- def micro_inspect; "<#{self.class.name}##{position}>"; end
126
+ def micro_inspect; "[#{self.class.name}+#{position}]"; end
127
+
128
+ # If loaded via a :pointer, this lets you get back to the parent, or set that pointer to some other thing
129
+ def parent; @parent; end
130
+ def parents_child= value; @parental_setter.call(value); end
118
131
 
119
132
  protected
133
+ def set_parent_accessors(parent, &set); @parent = parent; @parental_setter = set; self end
120
134
  def parts; self.class.instance_variable_get(:@parts); end
121
135
  PointerDefaults = {:bits => 32, :nil_if => 0xFFFFFFFF}
122
136
 
123
137
  # defines a simple string - defaults 8 bit length, :bits => 16 or 32 for longer strings!
124
138
  def self.define_one_string(name, opts)
125
- define_piece "#{name}_length", :string_length, {:string_name => name}.merge(opts)
139
+ define_piece "#{name}_length", :string_length, {:string_name => name}.merge(opts) unless opts[:length] || opts[:length_from]
126
140
  define_piece name, :raw_string, {:length_from => "#{name}_length".to_sym}.merge(opts)
127
141
  end
128
142
 
@@ -137,14 +151,18 @@ class DataBrick
137
151
  source.read(options[:bits] / 8).unpack(IntPacker[options[:bits]]).first
138
152
  end
139
153
 
140
- def read_raw_string(io, options); io.read(options[:length] || send(options[:length_from])); end
154
+ def read_raw_string(io, options); io.read(options[:length] || send(options[:length_from])).freeze; end
141
155
  alias_method :read_string_length, :read_integer
142
156
 
143
157
  def read_pointer(io, options)
144
158
  options = PointerDefaults.merge(options)
159
+ pos = io.tell
145
160
  ref = read_integer(io, options)
146
161
  return nil if ref == options[:nil_if]
147
- (options[:type] || self.class).new(io, ref)
162
+ obj = (options[:type] || self.class).new(io, ref)
163
+ obj.send(:set_parent_accessors, self) do |value|
164
+ w = io.tell; io.seek pos; io.write self.blob_pointer(value, {}, options); io.seek w
165
+ end
148
166
  end
149
167
 
150
168
  def read_array(io, options)
@@ -157,10 +175,7 @@ class DataBrick
157
175
  [int.to_i].pack(IntPacker[options[:bits] || 8])
158
176
  end
159
177
 
160
- def self.blob_raw_string(str, props, options = {})
161
- if options[:length] then (str.to_s + ("\000" * options[:length]))[0 ... options[:length]]
162
- else str.to_s end
163
- end
178
+ def self.blob_raw_string(str, props, options = {}); str.to_s; end
164
179
 
165
180
  def self.blob_pointer(pointee, props, options = {})
166
181
  options = PointerDefaults.merge(options)
@@ -174,9 +189,9 @@ class DataBrick
174
189
  end
175
190
 
176
191
  def self.blob_array(arr, props, options = {})
177
- accumulate = ''; options[:length].times do |i|
178
- accumulate += send("blob_#{options[:innards]}", arr[i], props, options[:innards_options]).to_s
179
- end; return accumulate
192
+ arr.inject('') { |result, item|
193
+ result += send("blob_#{options[:innards]}", item, props, options[:innards_options]).to_s
194
+ }
180
195
  end
181
196
 
182
197
  # lengths for offset calculation
@@ -195,7 +210,10 @@ class DataBrick::FixedArray
195
210
  include Enumerable
196
211
 
197
212
  # get a part of the array. :)
198
- def [] key
213
+ def [] key, length = nil
214
+ key = (key.to_i ... key.to_i + length.to_i) if length
215
+ return key.map { |i| self[i] } if key.is_a?(Range)
216
+
199
217
  was_at = @io.tell; seek_to key
200
218
  return @parent.send("read_#{@options[:innards]}", @io, @options[:innards_options] || {})
201
219
  ensure @io.seek was_at end
@@ -213,10 +231,12 @@ class DataBrick::FixedArray
213
231
  (0 ... length).each { |index| blk.call(self[index]) }
214
232
  end
215
233
 
216
- def size; @options[:length] || @options[:length_from]; end
234
+ def size; @options[:length] || @parent.send(@options[:length_from]); end
217
235
  alias_method :length, :size
218
236
 
219
- def inspect; "<#{self.class.name}:#{@start}*#{length}>"; end
237
+ def inspect; "<#{self.class.name}:#{@start}+#{length}>"; end
238
+
239
+ def to_a; (0...size).map { |i| self[i] }; end
220
240
 
221
241
  protected
222
242
  def seek_to key
@@ -272,6 +292,7 @@ end
272
292
  #
273
293
 
274
294
  #### TODO:
295
+ # !!! Update the raw_string writer to only rewrite stuff _after_ the changed string, and only if it's length actually changed.
275
296
  # @ Add float types
276
297
  # @ Maybe something for signed numbers?
277
298
  # @ Booleans type! (length = bools / 8)
data/test.rb CHANGED
@@ -32,7 +32,6 @@ until catman == nil
32
32
  catman = catman.next
33
33
  end
34
34
 
35
- File.unlink('cats.db')
36
35
  #### TEST ARRAYS!
37
36
 
38
37
  class ArrThing < DataBrick
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: databrick
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 2
10
- version: 0.2.2
9
+ - 4
10
+ version: 0.2.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Bluebie