bindata 0.9.3 → 0.10.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.
Potentially problematic release.
This version of bindata might be problematic. Click here for more details.
- data/ChangeLog +18 -0
- data/NEWS +59 -0
- data/README +22 -23
- data/TODO +18 -12
- data/examples/gzip.rb +4 -4
- data/lib/bindata.rb +4 -3
- data/lib/bindata/array.rb +202 -132
- data/lib/bindata/base.rb +147 -166
- data/lib/bindata/{single.rb → base_primitive.rb} +82 -56
- data/lib/bindata/bits.rb +31 -770
- data/lib/bindata/choice.rb +157 -82
- data/lib/bindata/float.rb +25 -27
- data/lib/bindata/int.rb +144 -177
- data/lib/bindata/io.rb +59 -49
- data/lib/bindata/lazy.rb +80 -50
- data/lib/bindata/params.rb +134 -26
- data/lib/bindata/{single_value.rb → primitive.rb} +71 -64
- data/lib/bindata/{multi_value.rb → record.rb} +52 -70
- data/lib/bindata/registry.rb +49 -17
- data/lib/bindata/rest.rb +6 -10
- data/lib/bindata/sanitize.rb +55 -70
- data/lib/bindata/string.rb +60 -42
- data/lib/bindata/stringz.rb +34 -35
- data/lib/bindata/struct.rb +197 -152
- data/lib/bindata/trace.rb +35 -0
- data/spec/array_spec.rb +128 -112
- data/spec/{single_spec.rb → base_primitive_spec.rb} +102 -61
- data/spec/base_spec.rb +190 -185
- data/spec/bits_spec.rb +126 -98
- data/spec/choice_spec.rb +89 -98
- data/spec/example.rb +19 -0
- data/spec/float_spec.rb +28 -44
- data/spec/int_spec.rb +217 -127
- data/spec/io_spec.rb +41 -24
- data/spec/lazy_spec.rb +95 -49
- data/spec/primitive_spec.rb +191 -0
- data/spec/{multi_value_spec.rb → record_spec.rb} +124 -89
- data/spec/registry_spec.rb +53 -12
- data/spec/rest_spec.rb +2 -3
- data/spec/sanitize_spec.rb +47 -73
- data/spec/spec_common.rb +13 -1
- data/spec/string_spec.rb +34 -23
- data/spec/stringz_spec.rb +10 -18
- data/spec/struct_spec.rb +91 -63
- data/spec/system_spec.rb +291 -0
- metadata +12 -8
- data/spec/single_value_spec.rb +0 -131
data/lib/bindata/rest.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "bindata/
|
1
|
+
require "bindata/base_primitive"
|
2
2
|
|
3
3
|
module BinData
|
4
4
|
# Rest will consume the input stream from the current position to the end of
|
@@ -6,7 +6,7 @@ module BinData
|
|
6
6
|
#
|
7
7
|
# require 'bindata'
|
8
8
|
#
|
9
|
-
# class A < BinData::
|
9
|
+
# class A < BinData::Record
|
10
10
|
# string :a, :read_length => 5
|
11
11
|
# rest :rest
|
12
12
|
# end
|
@@ -15,25 +15,21 @@ module BinData
|
|
15
15
|
# obj.a #=> "abcde"
|
16
16
|
# obj.rest #=" "fghij"
|
17
17
|
#
|
18
|
-
class Rest < BinData::
|
18
|
+
class Rest < BinData::BasePrimitive
|
19
19
|
|
20
|
-
# Register this class
|
21
20
|
register(self.name, self)
|
22
21
|
|
23
22
|
#---------------
|
24
23
|
private
|
25
24
|
|
26
|
-
|
27
|
-
def val_to_str(val)
|
25
|
+
def value_to_binary_string(val)
|
28
26
|
val
|
29
27
|
end
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
io.raw_io.read
|
29
|
+
def read_and_return_value(io)
|
30
|
+
io.read_all_bytes
|
34
31
|
end
|
35
32
|
|
36
|
-
# Returns an empty string as default.
|
37
33
|
def sensible_default
|
38
34
|
""
|
39
35
|
end
|
data/lib/bindata/sanitize.rb
CHANGED
@@ -1,37 +1,29 @@
|
|
1
|
+
require 'bindata/registry'
|
1
2
|
require 'forwardable'
|
2
3
|
|
3
4
|
module BinData
|
4
5
|
|
5
6
|
# A BinData object accepts arbitrary parameters. This class only contains
|
6
|
-
# parameters that have been sanitized
|
7
|
-
# whether they are BinData::Base.internal_parameters or are extra.
|
7
|
+
# parameters that have been sanitized.
|
8
8
|
class SanitizedParameters
|
9
9
|
extend Forwardable
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
@hash = params
|
14
|
-
@internal_parameters = {}
|
15
|
-
@extra_parameters = {}
|
11
|
+
def initialize(the_class, params)
|
12
|
+
@parameters = {}
|
16
13
|
|
17
|
-
|
18
|
-
@hash.each do |k,v|
|
14
|
+
params.each do |k,v|
|
19
15
|
k = k.to_sym
|
20
16
|
if v.nil?
|
21
|
-
raise ArgumentError, "parameter :#{k} has nil value in #{
|
17
|
+
raise ArgumentError, "parameter :#{k} has nil value in #{the_class}"
|
22
18
|
end
|
23
19
|
|
24
|
-
|
25
|
-
@internal_parameters[k] = v
|
26
|
-
else
|
27
|
-
@extra_parameters[k] = v
|
28
|
-
end
|
20
|
+
@parameters[k] = v
|
29
21
|
end
|
30
22
|
end
|
31
23
|
|
32
|
-
attr_reader :
|
24
|
+
attr_reader :parameters
|
33
25
|
|
34
|
-
def_delegators :@
|
26
|
+
def_delegators :@parameters, :[], :has_key?, :include?, :keys
|
35
27
|
end
|
36
28
|
|
37
29
|
# The Sanitizer sanitizes the parameters that are passed when creating a
|
@@ -41,45 +33,21 @@ module BinData
|
|
41
33
|
class Sanitizer
|
42
34
|
|
43
35
|
class << self
|
44
|
-
|
45
|
-
# Sanitize +params+ for +obj+.
|
36
|
+
# Sanitize +params+ for +the_class+.
|
46
37
|
# Returns sanitized parameters.
|
47
|
-
def sanitize(
|
38
|
+
def sanitize(the_class, params)
|
48
39
|
if SanitizedParameters === params
|
49
40
|
params
|
50
41
|
else
|
51
42
|
sanitizer = self.new
|
52
|
-
sanitizer.
|
43
|
+
sanitizer.sanitized_params(the_class, params)
|
53
44
|
end
|
54
45
|
end
|
55
|
-
|
56
|
-
# Returns true if +type+ is registered.
|
57
|
-
def type_exists?(type, endian = nil)
|
58
|
-
lookup(type, endian) != nil
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns the class matching a previously registered +name+.
|
62
|
-
def lookup(name, endian)
|
63
|
-
name = name.to_s
|
64
|
-
klass = Registry.instance.lookup(name)
|
65
|
-
if klass.nil? and endian != nil
|
66
|
-
# lookup failed so attempt endian lookup
|
67
|
-
if /^u?int\d{1,3}$/ =~ name
|
68
|
-
new_name = name + ((endian == :little) ? "le" : "be")
|
69
|
-
klass = Registry.instance.lookup(new_name)
|
70
|
-
elsif ["float", "double"].include?(name)
|
71
|
-
new_name = name + ((endian == :little) ? "_le" : "_be")
|
72
|
-
klass = Registry.instance.lookup(new_name)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
klass
|
76
|
-
end
|
77
46
|
end
|
78
47
|
|
79
|
-
# Create a new Sanitizer.
|
80
48
|
def initialize
|
81
|
-
@seen = []
|
82
49
|
@endian = nil
|
50
|
+
@seen = []
|
83
51
|
end
|
84
52
|
|
85
53
|
# Executes the given block with +endian+ set as the current endian.
|
@@ -94,44 +62,61 @@ module BinData
|
|
94
62
|
end
|
95
63
|
end
|
96
64
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
65
|
+
def lookup_class(type)
|
66
|
+
registered_class = RegisteredClasses.lookup(type, @endian)
|
67
|
+
if registered_class.nil?
|
68
|
+
raise TypeError, "unknown type '#{type}'"
|
69
|
+
end
|
70
|
+
registered_class
|
102
71
|
end
|
103
72
|
|
104
|
-
|
105
|
-
# Returns +sanitized_params+.
|
106
|
-
def sanitize_params(klass, params)
|
73
|
+
def sanitized_params(the_class, params)
|
107
74
|
new_params = params.nil? ? {} : params.dup
|
108
75
|
|
109
|
-
if
|
110
|
-
|
111
|
-
# and delay sanitizing the parameters until later.
|
112
|
-
new_params[:endian] = @endian if can_store_endian?(klass, new_params)
|
113
|
-
ret_val = new_params
|
76
|
+
if can_sanitize_parameters?(the_class)
|
77
|
+
get_sanitized_params(the_class, new_params)
|
114
78
|
else
|
115
|
-
|
79
|
+
store_current_endian!(the_class, new_params)
|
80
|
+
new_params
|
81
|
+
end
|
82
|
+
end
|
116
83
|
|
117
|
-
|
118
|
-
|
119
|
-
|
84
|
+
#---------------
|
85
|
+
private
|
86
|
+
|
87
|
+
def can_sanitize_parameters?(the_class)
|
88
|
+
not need_to_delay_sanitizing?(the_class)
|
89
|
+
end
|
90
|
+
|
91
|
+
def need_to_delay_sanitizing?(the_class)
|
92
|
+
the_class.recursive? and @seen.include?(the_class)
|
93
|
+
end
|
120
94
|
|
121
|
-
|
95
|
+
def get_sanitized_params(the_class, params)
|
96
|
+
result = nil
|
97
|
+
with_class_to_sanitize(the_class) do
|
98
|
+
the_class.sanitize_parameters!(self, params)
|
99
|
+
result = SanitizedParameters.new(the_class, params)
|
122
100
|
end
|
101
|
+
result
|
102
|
+
end
|
123
103
|
|
124
|
-
|
104
|
+
def with_class_to_sanitize(the_class, &block)
|
105
|
+
@seen.push(the_class)
|
106
|
+
yield
|
107
|
+
@seen.pop
|
125
108
|
end
|
126
109
|
|
127
|
-
|
128
|
-
|
110
|
+
def store_current_endian!(the_class, params)
|
111
|
+
if can_store_endian?(the_class, params)
|
112
|
+
params[:endian] = @endian
|
113
|
+
end
|
114
|
+
end
|
129
115
|
|
130
|
-
|
131
|
-
|
132
|
-
|
116
|
+
def can_store_endian?(the_class, params)
|
117
|
+
(@endian != nil and
|
118
|
+
the_class.accepted_internal_parameters.include?(:endian) and
|
133
119
|
not params.has_key?(:endian))
|
134
120
|
end
|
135
121
|
end
|
136
122
|
end
|
137
|
-
|
data/lib/bindata/string.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "bindata/
|
1
|
+
require "bindata/base_primitive"
|
2
2
|
|
3
3
|
module BinData
|
4
4
|
# A String is a sequence of bytes. This is the same as strings in Ruby.
|
@@ -23,16 +23,16 @@ module BinData
|
|
23
23
|
# obj = BinData::String.new(:length => 6, :trim_value => true)
|
24
24
|
# obj.value = "abcd"
|
25
25
|
# obj.value #=> "abcd"
|
26
|
-
# obj.
|
26
|
+
# obj.to_binary_s #=> "abcd\000\000"
|
27
27
|
#
|
28
28
|
# obj = BinData::String.new(:length => 6, :pad_char => 'A')
|
29
29
|
# obj.value = "abcd"
|
30
30
|
# obj.value #=> "abcdAA"
|
31
|
-
# obj.
|
31
|
+
# obj.to_binary_s #=> "abcdAA"
|
32
32
|
#
|
33
33
|
# == Parameters
|
34
34
|
#
|
35
|
-
# String objects accept all the params that BinData::
|
35
|
+
# String objects accept all the params that BinData::BasePrimitive
|
36
36
|
# does, as well as the following:
|
37
37
|
#
|
38
38
|
# <tt>:read_length</tt>:: The length to use when reading a value.
|
@@ -41,75 +41,93 @@ module BinData
|
|
41
41
|
# <tt>:pad_char</tt>:: The character to use when padding a string to a
|
42
42
|
# set length. Valid values are Integers and
|
43
43
|
# Strings of length 1. "\0" is the default.
|
44
|
-
# <tt>:
|
44
|
+
# <tt>:trim_padding</tt>:: Boolean, default false. If set, #value will
|
45
45
|
# return the value with all pad_chars trimmed
|
46
46
|
# from the end of the string. The value will
|
47
47
|
# not be trimmed when writing.
|
48
|
-
class String < BinData::
|
48
|
+
class String < BinData::BasePrimitive
|
49
49
|
|
50
|
-
# Register this class
|
51
50
|
register(self.name, self)
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
bindata_mutually_exclusive_parameters :length, :value
|
52
|
+
optional_parameters :read_length, :length, :trim_padding
|
53
|
+
default_parameters :pad_char => "\0"
|
54
|
+
mutually_exclusive_parameters :read_length, :length
|
55
|
+
mutually_exclusive_parameters :length, :value
|
58
56
|
|
59
57
|
class << self
|
60
58
|
|
61
|
-
|
59
|
+
def deprecate!(params, old_key, new_key)
|
60
|
+
if params.has_key?(old_key)
|
61
|
+
warn ":#{old_key} is deprecated. Replacing with :#{new_key}"
|
62
|
+
params[new_key] = params.delete(old_key)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
62
66
|
def sanitize_parameters!(sanitizer, params)
|
63
67
|
# warn about deprecated param - remove before releasing 1.0
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
+
deprecate!(params, :trim_value, :trim_padding)
|
69
|
+
|
70
|
+
warn_replacement_parameter(params, :initial_length, :read_length)
|
68
71
|
|
69
|
-
# set :pad_char to be a single length character string
|
70
72
|
if params.has_key?(:pad_char)
|
71
73
|
ch = params[:pad_char]
|
72
|
-
|
73
|
-
if ch.length > 1
|
74
|
-
raise ArgumentError, ":pad_char must not contain more than 1 char"
|
75
|
-
end
|
76
|
-
params[:pad_char] = ch
|
74
|
+
params[:pad_char] = sanitized_pad_char(ch)
|
77
75
|
end
|
78
76
|
|
79
77
|
super(sanitizer, params)
|
80
78
|
end
|
81
|
-
end
|
82
79
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
80
|
+
#-------------
|
81
|
+
private
|
82
|
+
|
83
|
+
def sanitized_pad_char(ch)
|
84
|
+
result = ch.respond_to?(:chr) ? ch.chr : ch.to_s
|
85
|
+
if result.length > 1
|
86
|
+
raise ArgumentError, ":pad_char must not contain more than 1 char"
|
87
|
+
end
|
88
|
+
result
|
89
89
|
end
|
90
|
-
v
|
91
90
|
end
|
92
91
|
|
93
92
|
#---------------
|
94
93
|
private
|
95
94
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
def _snapshot
|
96
|
+
# override to ensure length and optionally trim padding
|
97
|
+
result = super
|
98
|
+
if has_parameter?(:length)
|
99
|
+
result = truncate_or_pad_to_length(result)
|
100
|
+
end
|
101
|
+
if get_parameter(:trim_padding) == true
|
102
|
+
result = trim_padding(result)
|
103
|
+
end
|
104
|
+
result
|
105
|
+
end
|
106
|
+
|
107
|
+
def truncate_or_pad_to_length(str)
|
108
|
+
len = eval_parameter(:length) || str.length
|
109
|
+
if str.length == len
|
110
|
+
str
|
111
|
+
elsif str.length > len
|
112
|
+
str.slice(0, len)
|
113
|
+
else
|
114
|
+
str + (eval_parameter(:pad_char) * (len - str.length))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def trim_padding(str)
|
119
|
+
str.sub(/#{eval_parameter(:pad_char)}*$/, "")
|
120
|
+
end
|
101
121
|
|
102
|
-
|
103
|
-
|
122
|
+
def value_to_binary_string(val)
|
123
|
+
truncate_or_pad_to_length(val)
|
104
124
|
end
|
105
125
|
|
106
|
-
|
107
|
-
|
108
|
-
len = eval_param(:read_length) || eval_param(:length) || 0
|
126
|
+
def read_and_return_value(io)
|
127
|
+
len = eval_parameter(:read_length) || eval_parameter(:length) || 0
|
109
128
|
io.readbytes(len)
|
110
129
|
end
|
111
130
|
|
112
|
-
# Returns an empty string as default.
|
113
131
|
def sensible_default
|
114
132
|
""
|
115
133
|
end
|
data/lib/bindata/stringz.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "bindata/
|
1
|
+
require "bindata/base_primitive"
|
2
2
|
|
3
3
|
module BinData
|
4
4
|
# A BinData::Stringz object is a container for a zero ("\0") terminated
|
@@ -16,42 +16,36 @@ module BinData
|
|
16
16
|
# obj.snapshot #=> "abcd"
|
17
17
|
# obj.value #=> "abcd"
|
18
18
|
# obj.num_bytes #=> 5
|
19
|
-
# obj.
|
19
|
+
# obj.to_binary_s #=> "abcd\000"
|
20
20
|
#
|
21
21
|
# == Parameters
|
22
22
|
#
|
23
|
-
# Stringz objects accept all the params that BinData::
|
23
|
+
# Stringz objects accept all the params that BinData::BasePrimitive
|
24
24
|
# does, as well as the following:
|
25
25
|
#
|
26
26
|
# <tt>:max_length</tt>:: The maximum length of the string including the zero
|
27
27
|
# byte.
|
28
|
-
class Stringz < BinData::
|
28
|
+
class Stringz < BinData::BasePrimitive
|
29
29
|
|
30
|
-
# Register this class
|
31
30
|
register(self.name, self)
|
32
31
|
|
33
|
-
|
34
|
-
bindata_optional_parameters :max_length
|
35
|
-
|
36
|
-
# Overrides value to return the value of this data excluding the trailing
|
37
|
-
# zero byte.
|
38
|
-
def value
|
39
|
-
v = super
|
40
|
-
val_to_str(v).chomp("\0")
|
41
|
-
end
|
32
|
+
optional_parameters :max_length
|
42
33
|
|
43
34
|
#---------------
|
44
35
|
private
|
45
36
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
37
|
+
def _snapshot
|
38
|
+
# override to always remove trailing zero bytes
|
39
|
+
result = super
|
40
|
+
trim_and_zero_terminate(result).chomp("\0")
|
41
|
+
end
|
42
|
+
|
43
|
+
def value_to_binary_string(val)
|
44
|
+
trim_and_zero_terminate(val)
|
50
45
|
end
|
51
46
|
|
52
|
-
|
53
|
-
|
54
|
-
max_length = eval_param(:max_length)
|
47
|
+
def read_and_return_value(io)
|
48
|
+
max_length = eval_parameter(:max_length)
|
55
49
|
str = ""
|
56
50
|
i = 0
|
57
51
|
ch = nil
|
@@ -63,24 +57,25 @@ module BinData
|
|
63
57
|
i += 1
|
64
58
|
end
|
65
59
|
|
66
|
-
|
60
|
+
trim_and_zero_terminate(str)
|
67
61
|
end
|
68
62
|
|
69
|
-
# Returns an empty string as default.
|
70
63
|
def sensible_default
|
71
64
|
""
|
72
65
|
end
|
73
66
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
67
|
+
def trim_and_zero_terminate(str)
|
68
|
+
str = truncate_at_first_zero_byte(str)
|
69
|
+
str = trim_to(str, eval_parameter(:max_length))
|
70
|
+
append_zero_byte_if_needed(str)
|
71
|
+
end
|
79
72
|
|
80
|
-
|
81
|
-
|
73
|
+
def truncate_at_first_zero_byte(str)
|
74
|
+
str.sub(/([^\0]*\0).*/, '\1')
|
75
|
+
end
|
82
76
|
|
83
|
-
|
77
|
+
def trim_to(str, max_length = nil)
|
78
|
+
result = str
|
84
79
|
if max_length
|
85
80
|
max_length = 1 if max_length < 1
|
86
81
|
result = result[0, max_length]
|
@@ -88,11 +83,15 @@ module BinData
|
|
88
83
|
result[-1, 1] = "\0"
|
89
84
|
end
|
90
85
|
end
|
91
|
-
|
92
|
-
# ensure last byte in the string is a zero byte
|
93
|
-
result << "\0" if result[-1, 1] != "\0"
|
94
|
-
|
95
86
|
result
|
96
87
|
end
|
88
|
+
|
89
|
+
def append_zero_byte_if_needed(str)
|
90
|
+
if str.length == 0 or str[-1, 1] != "\0"
|
91
|
+
str + "\0"
|
92
|
+
else
|
93
|
+
str
|
94
|
+
end
|
95
|
+
end
|
97
96
|
end
|
98
97
|
end
|