data_inspect 0.0.1 → 0.0.3
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.
- data/bin/data_inspect +208 -1
- data/lib/data_inspect.rb +128 -3
- metadata +1 -1
data/bin/data_inspect
CHANGED
@@ -1,5 +1,212 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'data_inspect'
|
4
|
+
require 'awesome_print'
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def main
|
11
|
+
|
12
|
+
options = ARGV
|
13
|
+
filename = options.first
|
14
|
+
options = get_options
|
15
|
+
|
16
|
+
if options[:verbose]
|
17
|
+
puts "Version #{DataInspect.version}"
|
18
|
+
ap options
|
19
|
+
puts "\n\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
DataInspect.readfile(filename) do |reader|
|
28
|
+
|
29
|
+
|
30
|
+
# Seek the reader.
|
31
|
+
if options[:offset] > 0
|
32
|
+
reader.seek options[:offset]
|
33
|
+
end
|
34
|
+
|
35
|
+
offset = options[:offset]
|
36
|
+
|
37
|
+
counter = 0
|
38
|
+
|
39
|
+
care_about_limit = options[:limit] >= 0
|
40
|
+
|
41
|
+
values_to_display = {}
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
while (!reader.atEOS?) && !(care_about_limit && (counter >= options[:limit]))
|
46
|
+
|
47
|
+
result = next_value_and_size(reader, options)
|
48
|
+
value = result[0]
|
49
|
+
|
50
|
+
# puts "#{offset}: #{value}"
|
51
|
+
values_to_display[offset] = value
|
52
|
+
|
53
|
+
offset += result[1]
|
54
|
+
counter += 1
|
55
|
+
end
|
56
|
+
|
57
|
+
# Technically incorrect, as offset is 1 word too far in advance. But this oversight is acceptable.
|
58
|
+
max_length_offset = Math.log10(offset).to_i + 1
|
59
|
+
|
60
|
+
# Display the values
|
61
|
+
values_to_display.each do |offset, value|
|
62
|
+
display_offset = pad_with_spaces(offset.to_s, max_length_offset + 1)
|
63
|
+
puts "#{display_offset}: #{value}"
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns an array of size 2,
|
75
|
+
# first element is the returned value
|
76
|
+
# second element is the size
|
77
|
+
# The value is a string or object that converts to a string, to display.
|
78
|
+
def next_value_and_size(reader, options)
|
79
|
+
|
80
|
+
|
81
|
+
size = options[:size]
|
82
|
+
|
83
|
+
# data types:
|
84
|
+
# unsinged_int, singed_int, float, double
|
85
|
+
# ascii (coming soon), utf8 (coming soon)
|
86
|
+
|
87
|
+
case options[:data_type]
|
88
|
+
when :unsigned_int
|
89
|
+
value = reader.next_unsigned_int(size, options[:byte_order])
|
90
|
+
value = display_in_base(value, options[:unsigned_int_display_base], size)
|
91
|
+
when :signed_int
|
92
|
+
value = reader.next_signed_int(size, options[:byte_order])
|
93
|
+
when :float
|
94
|
+
value = reader.next_ieee_single_precision_float(options[:byte_order])
|
95
|
+
size = 4
|
96
|
+
when :double
|
97
|
+
value = reader.next_ieee_double_precision_float(options[:byte_order])
|
98
|
+
size = 8
|
99
|
+
end
|
100
|
+
|
101
|
+
return [value, size]
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
def get_options
|
108
|
+
|
109
|
+
options = {}
|
110
|
+
|
111
|
+
# index 0 is the filename.
|
112
|
+
arg_options = ARGV.slice(1, ARGV.size)
|
113
|
+
|
114
|
+
boolean_flags = ['h', 'b', 'v']
|
115
|
+
|
116
|
+
# Populate 'flat' options hash.
|
117
|
+
flat_options = {}
|
118
|
+
|
119
|
+
while (arg_options.size > 0)
|
120
|
+
|
121
|
+
flag_string = consume_array_element(arg_options)
|
122
|
+
flag = flag_string[1,flag_string.size]
|
123
|
+
|
124
|
+
if boolean_flags.include? flag
|
125
|
+
flat_options[flag] = true
|
126
|
+
else
|
127
|
+
value = consume_array_element(arg_options)
|
128
|
+
flat_options[flag] = value
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# Expand 'flat' options to more useful hash.
|
135
|
+
|
136
|
+
# :unsigned_int_display_base
|
137
|
+
|
138
|
+
options[:unsigned_int_display_base] = 10
|
139
|
+
|
140
|
+
if flat_options['h']
|
141
|
+
options[:unsigned_int_display_base] = 16
|
142
|
+
end
|
143
|
+
|
144
|
+
if flat_options['b']
|
145
|
+
options[:unsigned_int_display_base] = 2
|
146
|
+
end
|
147
|
+
|
148
|
+
# :offset
|
149
|
+
options[:offset] = flat_options['o'] || '0'
|
150
|
+
options[:offset] = options[:offset].to_i
|
151
|
+
|
152
|
+
# :limit
|
153
|
+
options[:limit] = flat_options['l'] || '-1'
|
154
|
+
options[:limit] = options[:limit].to_i
|
155
|
+
|
156
|
+
# :size
|
157
|
+
options[:size] = flat_options['s'] || '1'
|
158
|
+
options[:size] = options[:size].to_i
|
159
|
+
|
160
|
+
# :byte_order
|
161
|
+
options[:byte_order] = flat_options['bo'] || 'not_applicable'
|
162
|
+
options[:byte_order] = options[:byte_order].to_sym # need this to be in symbol form.
|
163
|
+
|
164
|
+
# :data_type
|
165
|
+
options[:data_type] = flat_options['f'] || 'unsigned_int'
|
166
|
+
options[:data_type] = options[:data_type].to_sym
|
167
|
+
|
168
|
+
# :verbose
|
169
|
+
options[:verbose] = flat_options['v']
|
170
|
+
|
171
|
+
return options
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
# Requires the array must have at least one element.
|
177
|
+
def consume_array_element(array)
|
178
|
+
array.slice!(0, 1)[0]
|
179
|
+
end
|
180
|
+
|
181
|
+
# If base is a power of 2, will pad with 0 to fill up to
|
182
|
+
# size in bytes
|
183
|
+
def display_in_base(value, base, size)
|
184
|
+
str = value.to_s(base)
|
185
|
+
if Math.log2(base) % 1 == 0.0 # If base is power of 2.
|
186
|
+
expected_bits = size * 8
|
187
|
+
coverage_of_digit = Math.log2(base)
|
188
|
+
expected_digits = expected_bits / coverage_of_digit
|
189
|
+
str = pad_with_zeros(str, expected_digits)
|
190
|
+
end
|
191
|
+
str
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
|
196
|
+
def pad_with_zeros(str, max_len)
|
197
|
+
pad_with_character(str, '0', max_len)
|
198
|
+
end
|
199
|
+
|
200
|
+
def pad_with_spaces(str, max_len)
|
201
|
+
pad_with_character(str, ' ', max_len)
|
202
|
+
end
|
203
|
+
|
204
|
+
def pad_with_character(str, char, max_len)
|
205
|
+
difference = max_len - str.size
|
206
|
+
char * difference + str
|
207
|
+
end
|
208
|
+
|
209
|
+
main
|
210
|
+
|
211
|
+
|
4
212
|
|
5
|
-
puts DataInspect.hi
|
data/lib/data_inspect.rb
CHANGED
@@ -1,9 +1,134 @@
|
|
1
1
|
|
2
2
|
class DataInspect
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
|
4
|
+
def self.version
|
5
|
+
'0.0.3'
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
def self.readfile(filename)
|
10
|
+
reader = Reader.new(filename)
|
11
|
+
yield reader
|
6
12
|
end
|
13
|
+
|
7
14
|
end
|
8
15
|
|
9
16
|
|
17
|
+
class Reader
|
18
|
+
|
19
|
+
@@unpack_formats = {
|
20
|
+
unsigned_int: {
|
21
|
+
1 => { not_applicable: 'C' },
|
22
|
+
2 => { big_endian: 'n', little_endian: 'v' },
|
23
|
+
4 => { big_endian: 'N', little_endian: 'V' }
|
24
|
+
},
|
25
|
+
|
26
|
+
signed_int: {
|
27
|
+
1 => { not_applicable: 'c' }
|
28
|
+
},
|
29
|
+
|
30
|
+
ieee_single_precision_float: {
|
31
|
+
4 => { big_endian: 'g', little_endian: 'e' }
|
32
|
+
},
|
33
|
+
|
34
|
+
ieee_double_precision_float: {
|
35
|
+
8 => { big_endian: 'G', little_endian: 'E' }
|
36
|
+
}
|
37
|
+
|
38
|
+
}
|
39
|
+
|
40
|
+
def initialize(filename)
|
41
|
+
@file = File.open(filename, 'rb')
|
42
|
+
end
|
43
|
+
|
44
|
+
def each_byte
|
45
|
+
offset = 0
|
46
|
+
@file.each_byte do |byte|
|
47
|
+
yield byte, offset
|
48
|
+
offset += 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# optional parameter size is the number of bytes to read.
|
53
|
+
# returns a string, representing the binary data.
|
54
|
+
def next_byte(size=1)
|
55
|
+
@file.read(size)
|
56
|
+
end
|
57
|
+
|
58
|
+
def next_unsigned_int(size=1, byte_order=:not_applicable)
|
59
|
+
unpack_format = format(:unsigned_int, size, byte_order)
|
60
|
+
unpack(size, unpack_format)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Ruby doesn't do this for us automatically so we
|
64
|
+
# have to add our own code (UnsignedToSignedInteger class).
|
65
|
+
def next_signed_int(size, byte_order)
|
66
|
+
unsigned_value = next_unsigned_int(size, byte_order)
|
67
|
+
UnsignedToSignedInteger.toSignedInteger(unsigned_value, size)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Next IEEE single-precision floating point number
|
71
|
+
# (4 bytes)
|
72
|
+
def next_ieee_single_precision_float(byte_order)
|
73
|
+
size = 4
|
74
|
+
unpack_format = format(:ieee_single_precision_float, size, byte_order)
|
75
|
+
unpack(size, unpack_format)
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# Next IEEE double-precision floating point number
|
80
|
+
# (8 bytes)
|
81
|
+
def next_ieee_double_precision_float(byte_order)
|
82
|
+
size = 8
|
83
|
+
unpack_format = format(:ieee_double_precision_float, size, byte_order)
|
84
|
+
unpack(size, unpack_format)
|
85
|
+
end
|
86
|
+
|
87
|
+
# If the file is at the end of stream.
|
88
|
+
def atEOS?
|
89
|
+
@file.eof?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Seek to a given offset in the file.
|
93
|
+
def seek(offset)
|
94
|
+
@file.seek(offset)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# returns the String.unpack format string given the
|
100
|
+
# parameter details (data type, size, and byte order).
|
101
|
+
# byte_order is not required when size (bytes) is 1.
|
102
|
+
# size defaults to 1.
|
103
|
+
def format(type, size=1, byte_order=:not_applicable)
|
104
|
+
@@unpack_formats[type][size][byte_order]
|
105
|
+
end
|
106
|
+
|
107
|
+
# Consumes 'size' bytes from the file,
|
108
|
+
# uses String.unpack() to interpret that data,
|
109
|
+
# using the given unpack format string.
|
110
|
+
def unpack(size, unpack_format)
|
111
|
+
@file.read(size).unpack(unpack_format)[0]
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
class UnsignedToSignedInteger
|
117
|
+
|
118
|
+
# Size is in bytes, and should be 1, 2 or 4.
|
119
|
+
def self.toSignedInteger(unsigned_value, size)
|
120
|
+
subtrahend = 2 ** (size * 8)
|
121
|
+
|
122
|
+
puts "subtrahend is #{subtrahend}."
|
123
|
+
|
124
|
+
max_value = 2 ** (size * 8 - 1) - 1
|
125
|
+
puts "max value is #{max_value}"
|
126
|
+
|
127
|
+
result = unsigned_value
|
128
|
+
result -= subtrahend if (unsigned_value > max_value)
|
129
|
+
|
130
|
+
return result
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|