bistro 2.1.0
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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +103 -0
- data/lib/bistro.rb +229 -0
- data/lib/bistro/version.rb +3 -0
- data/spec/bistro_spec.rb +133 -0
- data/spec/data/test.gif +0 -0
- data/spec/endian_spec.rb +203 -0
- data/spec/gif_spec.rb +44 -0
- data/spec/spec_helper.rb +79 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d3005111ea2c6d80cebb2b9fbb18942b70b8c90d6c785bb1b54a65db01e978fa
|
4
|
+
data.tar.gz: 62fdd3216e53989c4fcb18b1a8a38c3399b44c2daac249c9cabfcafd4d907415
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e656723aae47b109c66804e0ec03838193c464b3a4d0377f7691f8ea02f0c783fbfc5e4ad04fcda4bdab88a8012d23f242f25ac688ecab8e86c84406b6a51fd8
|
7
|
+
data.tar.gz: ae09a95c27537d26b0bfcfa6c418e72cbe95c358ef0e3d10976924ecb0d2c8894341060887427c97fad8b3abcd7b3765bf77f9449a859b11e2a8902090c69761
|
data/.rspec
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2019 Dev Null Productions <devnullproductions@gmail.com>
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Bistro
|
2
|
+
|
3
|
+
Bistro is a class for dealing with BInary STRuctured data. It simplifies
|
4
|
+
expressing what the binary structure looks like, with the ability to name the
|
5
|
+
parts. Given this definition, it is easy to encode/decode the binary structure
|
6
|
+
from/to a Hash.
|
7
|
+
|
8
|
+
## Example Usage
|
9
|
+
|
10
|
+
As an example, we will show reading and writing a .gif header. This example is
|
11
|
+
also in spec/gif_spec.rb.
|
12
|
+
|
13
|
+
### Create the structure definition
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gif_header = Bistro.new([
|
17
|
+
"a3", :magic,
|
18
|
+
"a3", :version,
|
19
|
+
"S", :width,
|
20
|
+
"S", :height,
|
21
|
+
"a", :flags,
|
22
|
+
"C", :bg_color_index,
|
23
|
+
"C", :pixel_aspect_ratio
|
24
|
+
])
|
25
|
+
```
|
26
|
+
|
27
|
+
### Read the header
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
header_size = gif_header.size
|
31
|
+
header = File.open("test.gif", "rb") { |f| f.read(header_size) }
|
32
|
+
gif_header.decode(header)
|
33
|
+
|
34
|
+
=> {
|
35
|
+
:magic => "GIF",
|
36
|
+
:version => "89a",
|
37
|
+
:width => 16,
|
38
|
+
:height => 16,
|
39
|
+
:flags => "\x80",
|
40
|
+
:bg_color_index => 0,
|
41
|
+
:pixel_aspect_ratio => 0
|
42
|
+
}
|
43
|
+
```
|
44
|
+
|
45
|
+
### Write the header
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
header = gif_header.encode({
|
49
|
+
:magic => "GIF",
|
50
|
+
:version => "89a",
|
51
|
+
:width => 16,
|
52
|
+
:height => 16,
|
53
|
+
:flags => "\x80",
|
54
|
+
:bg_color_index => 0,
|
55
|
+
:pixel_aspect_ratio => 0
|
56
|
+
})
|
57
|
+
File.open("test.gif", "wb") { |f| f.write(header) }
|
58
|
+
|
59
|
+
=> "GIF89a\x10\x00\x10\x00\x80\x00\x00"
|
60
|
+
```
|
61
|
+
|
62
|
+
## Note about bit and nibble formats
|
63
|
+
|
64
|
+
Bistro supports bit formats and nibble formats, however note that the
|
65
|
+
underlying Ruby methods, [pack](http://ruby-doc.org/core-2.2.0/Array.html#method-i-pack)
|
66
|
+
and [unpack](http://ruby-doc.org/core-2.2.0/String.html#method-i-unpack), support
|
67
|
+
less than 8 bits by reading an entire byte, even if all of the bits are not used.
|
68
|
+
|
69
|
+
For example,
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
s = "\xFF\x00" # binary: 1111111100000000
|
73
|
+
s.unpack("b8b8") # => ["11111111", "00000000"]
|
74
|
+
s.unpack("b4b4b4b4") # => ["1111", "0000", "", ""]
|
75
|
+
```
|
76
|
+
|
77
|
+
One might expect that the latter would read 4 bits, then the next 4 bits, etc,
|
78
|
+
yielding `["1111", "1111", "0000", "0000"]`, but that is not the case. Instead,
|
79
|
+
the first b4 reads a full byte's worth, then discards the unused 4 bits, and the
|
80
|
+
same happens for the next b4. The third and fourth b4 have nothing left to read,
|
81
|
+
and so just return empty strings.
|
82
|
+
|
83
|
+
## Installation
|
84
|
+
|
85
|
+
Add this line to your application's Gemfile:
|
86
|
+
|
87
|
+
gem 'bistro'
|
88
|
+
|
89
|
+
And then execute:
|
90
|
+
|
91
|
+
$ bundle
|
92
|
+
|
93
|
+
Or install it yourself as:
|
94
|
+
|
95
|
+
$ gem install bistro
|
96
|
+
|
97
|
+
## Contributing
|
98
|
+
|
99
|
+
1. Fork it
|
100
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
101
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
102
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
103
|
+
5. Create new Pull Request
|
data/lib/bistro.rb
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
require "bistro/version"
|
2
|
+
require "enumerator"
|
3
|
+
|
4
|
+
class Bistro
|
5
|
+
SIZES = {
|
6
|
+
'A' => 1, # String with trailing NULs and spaces removed
|
7
|
+
'a' => 1, # String
|
8
|
+
'B' => 1, # Extract bits from each character (MSB first)
|
9
|
+
'b' => 1, # Extract bits from each character (LSB first)
|
10
|
+
'C' => 1, # Extract a character as an unsigned integer
|
11
|
+
'c' => 1, # Extract a character as a signed integer
|
12
|
+
'E' => nil, # Treat sizeof(double) characters as a double in little-endian byte order
|
13
|
+
'e' => nil, # Treat sizeof(float) characters as a float in little-endian byte order
|
14
|
+
'G' => nil, # Treat sizeof(double) characters as a double in network byte order
|
15
|
+
'g' => nil, # Treat sizeof(float) characters as a float in network byte order
|
16
|
+
'H' => 1, # Extract hex nibbles from each character (most significant first)
|
17
|
+
'h' => 1, # Extract hex nibbles from each character (least significant first)
|
18
|
+
'I' => 4, # Treat sizeof(int) successive characters as an unsigned native integer
|
19
|
+
'i' => 4, # Treat sizeof(int) successive characters as a signed native integer
|
20
|
+
'L' => 4, # Treat 4 successive characters as an unsigned native long integer
|
21
|
+
'l' => 4, # Treat 4 successive characters as a signed native long integer
|
22
|
+
'M' => 1, # Extract a quoted printable string
|
23
|
+
'm' => 1, # Extract a Base64 encoded string
|
24
|
+
'N' => 4, # Treat 4 characters as an unsigned long in network byte order
|
25
|
+
'n' => 2, # Treat 2 characters as an unsigned short in network byte order
|
26
|
+
'P' => nil, # Treat sizeof(char *) characters as a pointer, and return len characters from the referenced location
|
27
|
+
'p' => nil, # Treat sizeof(char *) characters as a pointer to a null-terminated string
|
28
|
+
'Q' => 8, # Treat 8 characters as an unsigned quad word (64 bits)
|
29
|
+
'q' => 8, # Treat 8 characters as a signed quad word (64 bits)
|
30
|
+
'S' => 2, # Treat 2 successive characters as an unsigned short in native byte order
|
31
|
+
's' => 2, # Treat 2 successive characters as a signed short in native byte order
|
32
|
+
'U' => nil, # Extract UTF-8 characters as unsigned integers
|
33
|
+
'u' => nil, # Extract a UU-encoded string
|
34
|
+
'V' => 4, # Treat 4 characters as an unsigned long in little-endian byte order
|
35
|
+
'v' => 2, # Treat 2 characters as an unsigned short in little-endian byte order
|
36
|
+
'w' => nil, # BER-compressed integer
|
37
|
+
'X' => -1, # Skip backward one character
|
38
|
+
'x' => 1, # Skip forward one character
|
39
|
+
'Z' => 1, # String with trailing NULs removed
|
40
|
+
}
|
41
|
+
|
42
|
+
STRING_FORMATS = %w(A a B b H h M m u)
|
43
|
+
BIT_FORMATS = %w(B b)
|
44
|
+
NIBBLE_FORMATS = %w(H h)
|
45
|
+
ENDIAN_FORMATS = %w(I i L l Q q S s)
|
46
|
+
ENDIAN_MODIFIERS = %w(> <)
|
47
|
+
MODIFIERS = ENDIAN_MODIFIERS
|
48
|
+
|
49
|
+
def initialize(definition = nil)
|
50
|
+
self.definition = definition unless definition.nil?
|
51
|
+
end
|
52
|
+
|
53
|
+
def definition
|
54
|
+
@definition
|
55
|
+
end
|
56
|
+
|
57
|
+
def definition=(value)
|
58
|
+
if value.kind_of?(self.class)
|
59
|
+
@definition = value.definition.dup
|
60
|
+
else
|
61
|
+
value = value.to_a.map(&:reverse).flatten if value.kind_of?(Hash)
|
62
|
+
value = Array(value)
|
63
|
+
self.class.validate_definition(value)
|
64
|
+
@definition = value
|
65
|
+
end
|
66
|
+
@size = @decode_format = @decode_name = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def size
|
70
|
+
@size ||= self.class.get_size(@definition)
|
71
|
+
end
|
72
|
+
|
73
|
+
def decode(data, num = 1)
|
74
|
+
values = self.decode_to_array(data, num)
|
75
|
+
return self.decoded_array_to_hash!(values) if num == 1
|
76
|
+
|
77
|
+
result = []
|
78
|
+
num.times { result << self.decoded_array_to_hash!(values) }
|
79
|
+
return result
|
80
|
+
end
|
81
|
+
|
82
|
+
def decode_to_array(data, num = 1)
|
83
|
+
raise ArgumentError, "data cannot be nil" if data.nil?
|
84
|
+
@decode_format, @decode_names = self.class.prep_decode(@definition) if @decode_format.nil?
|
85
|
+
format = (num == 1) ? @decode_format : @decode_format * num
|
86
|
+
return data.unpack(format)
|
87
|
+
end
|
88
|
+
|
89
|
+
def decoded_array_to_hash!(array)
|
90
|
+
hash = {}
|
91
|
+
@decode_names.each do |k|
|
92
|
+
v = array.shift
|
93
|
+
next if k.nil?
|
94
|
+
hash[k] = v
|
95
|
+
end
|
96
|
+
return hash
|
97
|
+
end
|
98
|
+
|
99
|
+
def encode(hash)
|
100
|
+
return encode_hash(hash) unless hash.kind_of?(Array)
|
101
|
+
|
102
|
+
data = ""
|
103
|
+
hash.each { |h| data << self.encode_hash(h) }
|
104
|
+
return data
|
105
|
+
end
|
106
|
+
|
107
|
+
def encode_hash(hash)
|
108
|
+
data = ""
|
109
|
+
@definition.each_slice(2) do |format, name|
|
110
|
+
raise "member not found: #{name}" unless name.nil? || hash.has_key?(name)
|
111
|
+
value = unless name.nil?
|
112
|
+
hash[name]
|
113
|
+
else
|
114
|
+
STRING_FORMATS.include?(format[0, 1]) ? '0' : 0
|
115
|
+
end
|
116
|
+
data << [value].pack(format)
|
117
|
+
end
|
118
|
+
return data
|
119
|
+
end
|
120
|
+
|
121
|
+
def ==(other)
|
122
|
+
self.definition == other.definition
|
123
|
+
end
|
124
|
+
|
125
|
+
def each(&block)
|
126
|
+
self.definition.each_slice(2, &block)
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Methods to handle the old style of calling
|
131
|
+
#
|
132
|
+
|
133
|
+
@@structs_by_definition = {}
|
134
|
+
|
135
|
+
def self.clear_structs_by_definition_cache
|
136
|
+
@@structs_by_definition.clear
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.sizeof(definition)
|
140
|
+
struct_by_definition(definition).size
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.decode(data, definition)
|
144
|
+
struct_by_definition(definition).decode(data)
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.encode(hash, definition)
|
148
|
+
struct_by_definition(definition).encode(hash)
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def self.struct_by_definition(definition)
|
154
|
+
@@structs_by_definition[definition] ||= definition.kind_of?(self) ? definition : self.new(definition)
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.validate_definition(definition)
|
158
|
+
raise "definition must be an array of format/name pairs" if definition.empty? || definition.length % 2 != 0
|
159
|
+
definition.each_slice(2) do |format, _|
|
160
|
+
type, count = format[0, 1], format[1..-1]
|
161
|
+
modifier, modcount = count[0, 1], count[1..-1]
|
162
|
+
validate_definition_entry_type(type)
|
163
|
+
if valid_definition_entry_modifier?(modifier)
|
164
|
+
validate_definition_endian_modifier(modifier, type)
|
165
|
+
validate_definition_entry_count(modcount)
|
166
|
+
else
|
167
|
+
validate_definition_entry_count(count)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.validate_definition_entry_type(type)
|
173
|
+
raise "unrecognized format: #{type}" unless SIZES.has_key?(type)
|
174
|
+
raise "unsupported format: #{type}" if SIZES[type].nil?
|
175
|
+
return true
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.validate_definition_entry_count(count)
|
179
|
+
return true if count.empty? || count == '*'
|
180
|
+
|
181
|
+
begin
|
182
|
+
count = Integer(count)
|
183
|
+
rescue
|
184
|
+
raise "unsupported count: #{count}"
|
185
|
+
end
|
186
|
+
raise "unsupported count: #{count}" if count < 0
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.valid_definition_entry_modifier?(modifier)
|
190
|
+
MODIFIERS.include? modifier
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.validate_definition_endian_modifier(modifier, type)
|
194
|
+
if ENDIAN_MODIFIERS.include? modifier
|
195
|
+
raise "unsupported type attribute #{type} for endian modifier #{modifier}" unless ENDIAN_FORMATS.include? type
|
196
|
+
return true
|
197
|
+
end
|
198
|
+
false
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.get_size(definition)
|
202
|
+
size = 0
|
203
|
+
definition.each_slice(2) do |format, _|
|
204
|
+
type, count = format[0, 1], format[1..-1]
|
205
|
+
modifier, modcount = count[0, 1], count[1..-1]
|
206
|
+
count = modcount if valid_definition_entry_modifier?(modifier)
|
207
|
+
count = count.empty? ? 1 : count.to_i
|
208
|
+
size +=
|
209
|
+
if BIT_FORMATS.include?(type)
|
210
|
+
(count / 8.0).ceil
|
211
|
+
elsif NIBBLE_FORMATS.include?(type)
|
212
|
+
(count / 2.0).ceil
|
213
|
+
else
|
214
|
+
count * SIZES[type]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
size
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.prep_decode(definition)
|
221
|
+
formats = ""
|
222
|
+
names = []
|
223
|
+
definition.each_slice(2) do |format, name|
|
224
|
+
formats << format
|
225
|
+
names << name
|
226
|
+
end
|
227
|
+
return formats, names
|
228
|
+
end
|
229
|
+
end
|
data/spec/bistro_spec.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
describe Bistro do
|
2
|
+
STRUCT_DEF = [
|
3
|
+
'Q', :quad,
|
4
|
+
'L', 'long',
|
5
|
+
'S', :short,
|
6
|
+
'C', nil,
|
7
|
+
'b5', :binary,
|
8
|
+
'a0', 'none',
|
9
|
+
'a', nil,
|
10
|
+
'a2', 'bc',
|
11
|
+
]
|
12
|
+
STRUCT_DEF_SIZE = 19
|
13
|
+
|
14
|
+
STRUCT_DEF_HASH = {
|
15
|
+
:quad => 'Q',
|
16
|
+
'long' => 'L',
|
17
|
+
:short => 'S',
|
18
|
+
nil => 'C',
|
19
|
+
:binary => 'b5',
|
20
|
+
'none' => 'a0',
|
21
|
+
:unused => 'a',
|
22
|
+
'bc' => 'a2',
|
23
|
+
}
|
24
|
+
|
25
|
+
STRUCT_DEF_ASTERISK = ['a*', :word]
|
26
|
+
STRUCT_DEF_ASTERISK_SIZE = 0 # '*' is ignored
|
27
|
+
|
28
|
+
STRUCT_DEF_UNRECOGNIZED_FORMAT = ['D', nil]
|
29
|
+
STRUCT_DEF_UNSUPPORTED_FORMAT = ['U', nil]
|
30
|
+
STRUCT_DEF_UNSUPPORTED_COUNT_NEG = ['a-1', nil]
|
31
|
+
STRUCT_DEF_UNSUPPORTED_COUNT_INV = ['aX', nil]
|
32
|
+
|
33
|
+
STRUCT_ENCODED_STR = "\000\111\222\333\444\555\666\777\000\111\222\333\000\111\000\0320BC".force_encoding("ASCII-8BIT")
|
34
|
+
STRUCT_DECODED_HASH = {
|
35
|
+
:quad => 18_426_034_930_503_010_560,
|
36
|
+
"long" => 3_683_797_248,
|
37
|
+
:short => 18_688,
|
38
|
+
:binary => "01011",
|
39
|
+
"bc" => "BC",
|
40
|
+
"none" => ""
|
41
|
+
}
|
42
|
+
STRUCT_DECODED_HASH2 = STRUCT_DECODED_HASH.merge(:unused => "0")
|
43
|
+
|
44
|
+
it('.new') { expect { Bistro.new }.not_to raise_error }
|
45
|
+
it('.new with definition') { expect { Bistro.new(STRUCT_DEF) }.not_to raise_error }
|
46
|
+
it('.new with definition with *') { expect { Bistro.new(STRUCT_DEF_ASTERISK) }.not_to raise_error }
|
47
|
+
it('.new with Hash defintion') { expect { Bistro.new(STRUCT_DEF_HASH) }.not_to raise_error }
|
48
|
+
it '.new with another Bistro' do
|
49
|
+
s = Bistro.new(STRUCT_DEF)
|
50
|
+
s2 = Bistro.new(s)
|
51
|
+
expect(s2).to eq(s)
|
52
|
+
expect(s2).not_to equal(s)
|
53
|
+
end
|
54
|
+
|
55
|
+
it('.new with unrecognized format') { expect { Bistro.new(STRUCT_DEF_UNRECOGNIZED_FORMAT) }.to raise_error(RuntimeError) }
|
56
|
+
it('.new with unsupported format') { expect { Bistro.new(STRUCT_DEF_UNSUPPORTED_FORMAT) }.to raise_error(RuntimeError) }
|
57
|
+
it('.new with unsupported negative count') { expect { Bistro.new(STRUCT_DEF_UNSUPPORTED_COUNT_NEG) }.to raise_error(RuntimeError) }
|
58
|
+
it('.new with unsupported invalid count') { expect { Bistro.new(STRUCT_DEF_UNSUPPORTED_COUNT_INV) }.to raise_error(RuntimeError) }
|
59
|
+
|
60
|
+
it('#definition=') { expect { Bistro.new.definition = STRUCT_DEF }.not_to raise_error }
|
61
|
+
it('#definition= with definition with *') { expect { Bistro.new.definition = STRUCT_DEF_ASTERISK }.not_to raise_error }
|
62
|
+
it('#definition= with Hash defintion') { expect { Bistro.new.definition = STRUCT_DEF_HASH }.not_to raise_error }
|
63
|
+
|
64
|
+
it('#definition= with unrecognized format') { expect { Bistro.new.definition = STRUCT_DEF_UNRECOGNIZED_FORMAT }.to raise_error(RuntimeError) }
|
65
|
+
it('#definition= with unsupported format') { expect { Bistro.new.definition = STRUCT_DEF_UNSUPPORTED_FORMAT }.to raise_error(RuntimeError) }
|
66
|
+
it('#definition= with unsupported negative count') { expect { Bistro.new.definition = STRUCT_DEF_UNSUPPORTED_COUNT_NEG }.to raise_error(RuntimeError) }
|
67
|
+
it('#definition= with unsupported invalid count') { expect { Bistro.new.definition = STRUCT_DEF_UNSUPPORTED_COUNT_INV }.to raise_error(RuntimeError) }
|
68
|
+
|
69
|
+
it('#size') { expect(Bistro.new(STRUCT_DEF).size).to eq(STRUCT_DEF_SIZE) }
|
70
|
+
it('#size with definition with *') { expect(Bistro.new(STRUCT_DEF_ASTERISK).size).to eq(STRUCT_DEF_ASTERISK_SIZE) }
|
71
|
+
|
72
|
+
it '#decode' do
|
73
|
+
expect(Bistro.new(STRUCT_DEF).decode(STRUCT_ENCODED_STR)).to eq(STRUCT_DECODED_HASH)
|
74
|
+
end
|
75
|
+
|
76
|
+
it '#decode with definition with *' do
|
77
|
+
expect(Bistro.new(STRUCT_DEF_ASTERISK).decode("Testing")).to eq(:word => "Testing")
|
78
|
+
end
|
79
|
+
|
80
|
+
it '#decode with Hash defintion' do
|
81
|
+
expect(Bistro.new(STRUCT_DEF_HASH).decode(STRUCT_ENCODED_STR)).to eq(STRUCT_DECODED_HASH2)
|
82
|
+
end
|
83
|
+
|
84
|
+
it '#decode against multiple records' do
|
85
|
+
expect(Bistro.new(STRUCT_DEF).decode(STRUCT_ENCODED_STR * 10, 10)).to eq([STRUCT_DECODED_HASH] * 10)
|
86
|
+
end
|
87
|
+
|
88
|
+
it '#encode' do
|
89
|
+
expect(Bistro.new(STRUCT_DEF).encode(STRUCT_DECODED_HASH)).to eq(STRUCT_ENCODED_STR.force_encoding("ASCII-8BIT"))
|
90
|
+
end
|
91
|
+
|
92
|
+
it '#encode with definition with *' do
|
93
|
+
expect(Bistro.new(STRUCT_DEF_ASTERISK).encode(:word => "Testing")).to eq("Testing")
|
94
|
+
end
|
95
|
+
|
96
|
+
it '#encode against multiple records' do
|
97
|
+
s = Bistro.new(STRUCT_DEF).encode([STRUCT_DECODED_HASH] * 10)
|
98
|
+
expect(s).to eq(STRUCT_ENCODED_STR.force_encoding("ASCII-8BIT") * 10)
|
99
|
+
end
|
100
|
+
|
101
|
+
it '#== against another Bistro' do
|
102
|
+
expect(Bistro.new(STRUCT_DEF)).to eq(Bistro.new(STRUCT_DEF))
|
103
|
+
expect(Bistro.new(STRUCT_DEF)).not_to eq(Bistro.new(STRUCT_DEF_ASTERISK))
|
104
|
+
end
|
105
|
+
|
106
|
+
it '#each will iterate over definition' do
|
107
|
+
dup_def = []
|
108
|
+
Bistro.new(STRUCT_DEF).each { |field, name| dup_def << field << name }
|
109
|
+
expect(dup_def).to eq(STRUCT_DEF)
|
110
|
+
end
|
111
|
+
|
112
|
+
context "old style methods" do
|
113
|
+
after(:each) { Bistro.clear_structs_by_definition_cache }
|
114
|
+
|
115
|
+
it '#sizeof' do
|
116
|
+
expect(Bistro.sizeof(STRUCT_DEF)).to eq(STRUCT_DEF_SIZE)
|
117
|
+
# Do it twice for consistency reasons
|
118
|
+
expect(Bistro.sizeof(STRUCT_DEF)).to eq(STRUCT_DEF_SIZE)
|
119
|
+
end
|
120
|
+
|
121
|
+
it '#decode' do
|
122
|
+
expect(Bistro.decode(STRUCT_ENCODED_STR, STRUCT_DEF)).to eq(STRUCT_DECODED_HASH)
|
123
|
+
# Do it twice for consistency reasons
|
124
|
+
expect(Bistro.decode(STRUCT_ENCODED_STR, STRUCT_DEF)).to eq(STRUCT_DECODED_HASH)
|
125
|
+
end
|
126
|
+
|
127
|
+
it '#encode' do
|
128
|
+
expect(Bistro.encode(STRUCT_DECODED_HASH, STRUCT_DEF)).to eq(STRUCT_ENCODED_STR.force_encoding("ASCII-8BIT"))
|
129
|
+
# Do it twice for consistency reasons
|
130
|
+
expect(Bistro.encode(STRUCT_DECODED_HASH, STRUCT_DEF)).to eq(STRUCT_ENCODED_STR.force_encoding("ASCII-8BIT"))
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/spec/data/test.gif
ADDED
Binary file
|
data/spec/endian_spec.rb
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
describe Bistro do
|
2
|
+
BIG_STRUCT_DEF = [
|
3
|
+
'Q>', :quad,
|
4
|
+
'L>', 'long',
|
5
|
+
'S>', :short,
|
6
|
+
'C', nil,
|
7
|
+
'b5', :binary,
|
8
|
+
'a0', 'none',
|
9
|
+
'a', nil,
|
10
|
+
'a2', 'bc',
|
11
|
+
]
|
12
|
+
BIG_STRUCT_DEF_SIZE = 19
|
13
|
+
|
14
|
+
BIG_E_QUAD_STRUCT_DEF = ['Q>2', :quad]
|
15
|
+
BIG_E_QUAD_DEF_SIZE = 16
|
16
|
+
|
17
|
+
BIG_STRUCT_DEF_STAR = ['i>*', :word]
|
18
|
+
BIG_STRUCT_DEF_STAR_SIZE = 0 # '*' is ignored
|
19
|
+
|
20
|
+
BIG_STRUCT_DEF_UNRECOG_ENDIAN_FMT = ['Y>', nil]
|
21
|
+
BIG_STRUCT_DEF_UNSUPPORTED_ENDIAN_ATTRIBUTE = ['A>', nil]
|
22
|
+
BIG_STRUCT_DEF_INVALID_ENDIAN_MODIFIER = ['Q_', nil]
|
23
|
+
|
24
|
+
LIL_STRUCT_DEF = [
|
25
|
+
'Q<', :quad,
|
26
|
+
'L<', 'long',
|
27
|
+
'S<', :short,
|
28
|
+
'C', nil,
|
29
|
+
'b5', :binary,
|
30
|
+
'a0', 'none',
|
31
|
+
'a', nil,
|
32
|
+
'a2', 'bc',
|
33
|
+
]
|
34
|
+
LIL_STRUCT_DEF_SIZE = 19
|
35
|
+
|
36
|
+
LIL_E_QUAD_STRUCT_DEF = ['Q<2', :quad]
|
37
|
+
LIL_E_QUAD_DEF_SIZE = 16
|
38
|
+
|
39
|
+
LIL_STRUCT_DEF_STAR = ['i<*', :word]
|
40
|
+
LIL_STRUCT_DEF_STAR_SIZE = 0 # '*' is ignored
|
41
|
+
|
42
|
+
LIL_STRUCT_DEF_UNRECOG_ENDIAN_FMT = ['Y<', nil]
|
43
|
+
LIL_STRUCT_DEF_UNSUPPORTED_ENDIAN_ATTRIBUTE = ['A<', nil]
|
44
|
+
|
45
|
+
END_STRUCT_ENCODED_STR = "\000\111\222\333\444\555\666\777\000\111\222\333\000\111\000\0320BC"
|
46
|
+
|
47
|
+
LIL_ENDIAN_STRUCT_DECODED_HASH =
|
48
|
+
{
|
49
|
+
:quad => 18_426_034_930_503_010_560,
|
50
|
+
"long" => 3_683_797_248,
|
51
|
+
:short => 18_688,
|
52
|
+
:binary => "01011",
|
53
|
+
"none" => "",
|
54
|
+
"bc" => "BC",
|
55
|
+
}
|
56
|
+
|
57
|
+
BIG_ENDIAN_STRUCT_DECODED_HASH =
|
58
|
+
{
|
59
|
+
:quad => 20_709_143_206_541_055,
|
60
|
+
"long" => 4_821_723,
|
61
|
+
:short => 73,
|
62
|
+
:binary => "01011",
|
63
|
+
"none" => "",
|
64
|
+
"bc" => "BC"
|
65
|
+
}
|
66
|
+
|
67
|
+
it('.new') { expect { Bistro.new }.not_to raise_error }
|
68
|
+
it('.new with big definition') { expect { Bistro.new(BIG_STRUCT_DEF) }.not_to raise_error }
|
69
|
+
it('.new with little definition') { expect { Bistro.new(LIL_STRUCT_DEF) }.not_to raise_error }
|
70
|
+
it('.new with big definition with *') { expect { Bistro.new(BIG_STRUCT_DEF_STAR) }.not_to raise_error }
|
71
|
+
it('.new with little definition with *') { expect { Bistro.new(LIL_STRUCT_DEF_STAR) }.not_to raise_error }
|
72
|
+
it '.new with another big endian Bistro' do
|
73
|
+
s = Bistro.new(BIG_STRUCT_DEF)
|
74
|
+
s2 = Bistro.new(s)
|
75
|
+
expect(s2).to eq(s)
|
76
|
+
expect(s2).not_to equal(s)
|
77
|
+
end
|
78
|
+
|
79
|
+
it '.new with another little endian Bistro' do
|
80
|
+
s = Bistro.new(LIL_STRUCT_DEF)
|
81
|
+
s2 = Bistro.new(s)
|
82
|
+
expect(s2).to eq(s)
|
83
|
+
expect(s2).not_to equal(s)
|
84
|
+
end
|
85
|
+
|
86
|
+
it '.new big endian Bistro with a little endian one' do
|
87
|
+
s = Bistro.new(BIG_STRUCT_DEF)
|
88
|
+
s2 = Bistro.new(LIL_STRUCT_DEF)
|
89
|
+
expect(s2).not_to eq(s)
|
90
|
+
expect(s2).not_to equal(s)
|
91
|
+
end
|
92
|
+
|
93
|
+
it('.new with unrecognized big e format') do
|
94
|
+
expect { Bistro.new(BIG_STRUCT_DEF_UNRECOG_ENDIAN_FMT) }
|
95
|
+
.to raise_error(RuntimeError)
|
96
|
+
end
|
97
|
+
|
98
|
+
it('.new with unsupported big e attribute') do
|
99
|
+
expect { Bistro.new(BIG_STRUCT_DEF_UNSUPPORTED_ENDIAN_ATTRIBUTE) }
|
100
|
+
.to raise_error(RuntimeError)
|
101
|
+
end
|
102
|
+
|
103
|
+
it('.new with invalid endian modifier') do
|
104
|
+
expect { Bistro.new(BIG_STRUCT_DEF_INVALID_ENDIAN_MODIFIER) }
|
105
|
+
.to raise_error(RuntimeError)
|
106
|
+
end
|
107
|
+
|
108
|
+
it('.new with unrecognized little e format') do
|
109
|
+
expect { Bistro.new(LIL_STRUCT_DEF_UNRECOG_ENDIAN_FMT) }
|
110
|
+
.to raise_error(RuntimeError)
|
111
|
+
end
|
112
|
+
|
113
|
+
it('.new with unsupported little e attribute') do
|
114
|
+
expect { Bistro.new(LIL_STRUCT_DEF_UNSUPPORTED_ENDIAN_ATTRIBUTE) }
|
115
|
+
.to raise_error(RuntimeError)
|
116
|
+
end
|
117
|
+
|
118
|
+
it('#definition= with definition with *') do
|
119
|
+
expect { Bistro.new.definition = BIG_STRUCT_DEF_STAR }
|
120
|
+
.not_to raise_error
|
121
|
+
end
|
122
|
+
|
123
|
+
it('#definition= with unrecognized big e format') do
|
124
|
+
expect { Bistro.new.definition = BIG_STRUCT_DEF_UNRECOG_ENDIAN_FMT }
|
125
|
+
.to raise_error(RuntimeError)
|
126
|
+
end
|
127
|
+
it('#definition= with unsupported big e attribute') do
|
128
|
+
expect { Bistro.new.definition = BIG_STRUCT_DEF_UNSUPPORTED_ENDIAN_ATTRIBUTE }
|
129
|
+
.to raise_error(RuntimeError)
|
130
|
+
end
|
131
|
+
it('#definition= with unrecognized little e format') do
|
132
|
+
expect { Bistro.new.definition = LIL_STRUCT_DEF_UNRECOG_ENDIAN_FMT }
|
133
|
+
.to raise_error(RuntimeError)
|
134
|
+
end
|
135
|
+
it('#definition= with unsupported little e attribute') do
|
136
|
+
expect { Bistro.new.definition = LIL_STRUCT_DEF_UNSUPPORTED_ENDIAN_ATTRIBUTE }
|
137
|
+
.to raise_error(RuntimeError)
|
138
|
+
end
|
139
|
+
|
140
|
+
it('#size') { expect(Bistro.new(BIG_STRUCT_DEF).size).to eq(BIG_STRUCT_DEF_SIZE) }
|
141
|
+
it('#size') { expect(Bistro.new(LIL_STRUCT_DEF).size).to eq(LIL_STRUCT_DEF_SIZE) }
|
142
|
+
it('#size with definition with *') { expect(Bistro.new(BIG_STRUCT_DEF_STAR).size).to eq(BIG_STRUCT_DEF_STAR_SIZE) }
|
143
|
+
it('#size with definition with *') { expect(Bistro.new(LIL_STRUCT_DEF_STAR).size).to eq(LIL_STRUCT_DEF_STAR_SIZE) }
|
144
|
+
|
145
|
+
it '#decode with different endian definitions' do
|
146
|
+
expect(Bistro.new(BIG_STRUCT_DEF).decode(END_STRUCT_ENCODED_STR))
|
147
|
+
.not_to equal(Bistro.new(LIL_STRUCT_DEF).decode(END_STRUCT_ENCODED_STR))
|
148
|
+
end
|
149
|
+
it '#encode with different endian definitions' do
|
150
|
+
big = Bistro.new(BIG_STRUCT_DEF).encode(BIG_ENDIAN_STRUCT_DECODED_HASH)
|
151
|
+
little = Bistro.new(LIL_STRUCT_DEF).encode(LIL_ENDIAN_STRUCT_DECODED_HASH)
|
152
|
+
expect(big).not_to equal(little)
|
153
|
+
end
|
154
|
+
|
155
|
+
it '#decode little endian struct' do
|
156
|
+
expect(Bistro.new(LIL_STRUCT_DEF).decode(END_STRUCT_ENCODED_STR)).to eq(LIL_ENDIAN_STRUCT_DECODED_HASH)
|
157
|
+
end
|
158
|
+
it '#decode big endian struct' do
|
159
|
+
expect(Bistro.new(BIG_STRUCT_DEF).decode(END_STRUCT_ENCODED_STR)).to eq(BIG_ENDIAN_STRUCT_DECODED_HASH)
|
160
|
+
end
|
161
|
+
|
162
|
+
it '#== against another little endian Bistro' do
|
163
|
+
expect(Bistro.new(LIL_STRUCT_DEF)).to eq(Bistro.new(LIL_STRUCT_DEF))
|
164
|
+
end
|
165
|
+
it '#== big endian against another little endian Bistro' do
|
166
|
+
expect(Bistro.new(BIG_STRUCT_DEF)).not_to eq(Bistro.new(LIL_STRUCT_DEF))
|
167
|
+
end
|
168
|
+
it '#== against another big endian Bistro' do
|
169
|
+
expect(Bistro.new(BIG_STRUCT_DEF)).to eq(Bistro.new(BIG_STRUCT_DEF))
|
170
|
+
end
|
171
|
+
|
172
|
+
it '#each will iterate over definition' do
|
173
|
+
dup_def = []
|
174
|
+
Bistro.new(BIG_STRUCT_DEF).each { |field, name| dup_def << field << name }
|
175
|
+
expect(dup_def).to eq(BIG_STRUCT_DEF)
|
176
|
+
end
|
177
|
+
|
178
|
+
it '#each will iterate over definition' do
|
179
|
+
dup_def = []
|
180
|
+
Bistro.new(LIL_STRUCT_DEF).each { |field, name| dup_def << field << name }
|
181
|
+
expect(dup_def).to eq(LIL_STRUCT_DEF)
|
182
|
+
end
|
183
|
+
|
184
|
+
context "old style methods" do
|
185
|
+
after(:each) { Bistro.clear_structs_by_definition_cache }
|
186
|
+
|
187
|
+
it '#sizeof' do
|
188
|
+
expect(Bistro.sizeof(BIG_STRUCT_DEF)).to eq(BIG_STRUCT_DEF_SIZE)
|
189
|
+
end
|
190
|
+
# Do it twice for consistency reasons
|
191
|
+
it '#sizeof' do
|
192
|
+
expect(Bistro.sizeof(BIG_STRUCT_DEF)).to eq(BIG_STRUCT_DEF_SIZE)
|
193
|
+
end
|
194
|
+
|
195
|
+
it '#sizeof' do
|
196
|
+
expect(Bistro.sizeof(LIL_STRUCT_DEF)).to eq(LIL_STRUCT_DEF_SIZE)
|
197
|
+
end
|
198
|
+
# Do it twice for consistency reasons
|
199
|
+
it '#sizeof' do
|
200
|
+
expect(Bistro.sizeof(LIL_STRUCT_DEF)).to eq(LIL_STRUCT_DEF_SIZE)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
data/spec/gif_spec.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
describe Bistro do
|
2
|
+
let(:gif_header) do
|
3
|
+
Bistro.new([
|
4
|
+
"a3", :magic,
|
5
|
+
"a3", :version,
|
6
|
+
"S", :width,
|
7
|
+
"S", :height,
|
8
|
+
"a", :flags,
|
9
|
+
"C", :bg_color_index,
|
10
|
+
"C", :pixel_aspect_ratio
|
11
|
+
])
|
12
|
+
end
|
13
|
+
|
14
|
+
it "read a gif header" do
|
15
|
+
filename = File.join(File.dirname(__FILE__), %w(data test.gif))
|
16
|
+
decoded_header_hash = {
|
17
|
+
:magic => "GIF",
|
18
|
+
:version => "89a",
|
19
|
+
:width => 16,
|
20
|
+
:height => 16,
|
21
|
+
:flags => "\x80",
|
22
|
+
:bg_color_index => 0,
|
23
|
+
:pixel_aspect_ratio => 0,
|
24
|
+
}
|
25
|
+
encoded_header = gif_header.encode(decoded_header_hash)
|
26
|
+
header_size = gif_header.size
|
27
|
+
header = File.open(filename, "rb") { |f| f.read(header_size) }
|
28
|
+
expect(encoded_header).to eq(header)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "write a gif header" do
|
32
|
+
header = gif_header.encode(
|
33
|
+
:magic => "GIF",
|
34
|
+
:version => "89a",
|
35
|
+
:width => 16,
|
36
|
+
:height => 16,
|
37
|
+
:flags => "\x80",
|
38
|
+
:bg_color_index => 0,
|
39
|
+
:pixel_aspect_ratio => 0
|
40
|
+
)
|
41
|
+
|
42
|
+
expect(header).to eq("GIF89a\x10\x00\x10\x00\x80\x00\x00".force_encoding("ASCII-8BIT"))
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
4
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
5
|
+
#
|
6
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
7
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
8
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
9
|
+
# individual file that may not need all of that loaded. Instead, make a
|
10
|
+
# separate helper file that requires this one and then use it only in the specs
|
11
|
+
# that actually need it.
|
12
|
+
#
|
13
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
14
|
+
# users commonly want.
|
15
|
+
#
|
16
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
17
|
+
RSpec.configure do |config|
|
18
|
+
# The settings below are suggested to provide a good initial experience
|
19
|
+
# with RSpec, but feel free to customize to your heart's content.
|
20
|
+
|
21
|
+
# These two settings work together to allow you to limit a spec run
|
22
|
+
# to individual examples or groups you care about by tagging them with
|
23
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
24
|
+
# get run.
|
25
|
+
config.filter_run :focus
|
26
|
+
config.run_all_when_everything_filtered = true
|
27
|
+
|
28
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
29
|
+
# file, and it's useful to allow more verbose output when running an
|
30
|
+
# individual spec file.
|
31
|
+
if config.files_to_run.one?
|
32
|
+
# Use the documentation formatter for detailed output,
|
33
|
+
# unless a formatter has already been configured
|
34
|
+
# (e.g. via a command-line flag).
|
35
|
+
config.default_formatter = 'doc'
|
36
|
+
end
|
37
|
+
|
38
|
+
# Print the 10 slowest examples and example groups at the
|
39
|
+
# end of the spec run, to help surface which specs are running
|
40
|
+
# particularly slow.
|
41
|
+
# config.profile_examples = 10
|
42
|
+
|
43
|
+
# Run specs in random order to surface order dependencies. If you find an
|
44
|
+
# order dependency and want to debug it, you can fix the order by providing
|
45
|
+
# the seed, which is printed after each run.
|
46
|
+
# --seed 1234
|
47
|
+
config.order = :random
|
48
|
+
|
49
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
50
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
51
|
+
# test failures related to randomization by passing the same `--seed` value
|
52
|
+
# as the one that triggered the failure.
|
53
|
+
Kernel.srand config.seed
|
54
|
+
|
55
|
+
# rspec-expectations config goes here. You can use an alternate
|
56
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
57
|
+
# assertions if you prefer.
|
58
|
+
config.expect_with :rspec do |expectations|
|
59
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
60
|
+
# For more details, see:
|
61
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
62
|
+
expectations.syntax = :expect
|
63
|
+
end
|
64
|
+
|
65
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
66
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
67
|
+
config.mock_with :rspec do |mocks|
|
68
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
69
|
+
# For more details, see:
|
70
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
71
|
+
mocks.syntax = :expect
|
72
|
+
|
73
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
74
|
+
# a real object. This is generally recommended.
|
75
|
+
mocks.verify_partial_doubles = true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
require 'bistro'
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bistro
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dev Null Productions
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-04-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: Bistro is a class for dealing with BInary STRuctured data
|
56
|
+
email:
|
57
|
+
- devnullproductions@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".rspec"
|
63
|
+
- LICENSE.txt
|
64
|
+
- README.md
|
65
|
+
- lib/bistro.rb
|
66
|
+
- lib/bistro/version.rb
|
67
|
+
- spec/bistro_spec.rb
|
68
|
+
- spec/data/test.gif
|
69
|
+
- spec/endian_spec.rb
|
70
|
+
- spec/gif_spec.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
homepage: http://github.com/DevNullProd/bistro
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 1.9.3
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.7.6
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Bistro is a class for dealing with BInary STRuctured data.
|
96
|
+
test_files:
|
97
|
+
- spec/bistro_spec.rb
|
98
|
+
- spec/data/test.gif
|
99
|
+
- spec/endian_spec.rb
|
100
|
+
- spec/gif_spec.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
- ".rspec"
|