databrick 0.2.2 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/databrick.rb +48 -27
- data/test.rb +0 -1
- metadata +3 -3
data/databrick.rb
CHANGED
@@ -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}
|
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
|
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
|
-
#
|
91
|
-
def offset_for(part)
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
"
|
113
|
-
val = self.send(p); ".#{p}: #{val.
|
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; "
|
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
|
-
|
178
|
-
|
179
|
-
|
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}
|
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
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:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 4
|
10
|
+
version: 0.2.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Bluebie
|