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.
Files changed (3) hide show
  1. data/bin/data_inspect +208 -1
  2. data/lib/data_inspect.rb +128 -3
  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
- def self.hi
4
- puts "Hi from DataInspect!!"
5
- return 512
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
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: data_inspect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: