msgpack-inspect 0.1.1 → 0.1.2

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.
@@ -0,0 +1,34 @@
1
+ ---
2
+ - format: fixarray
3
+ header: "ff"
4
+ length: 3
5
+ children:
6
+ - format: string
7
+ header: "e0"
8
+ length: 10
9
+ data: "0102030405060708090a"
10
+ value: "aaaaaaaaaa"
11
+ - format: int
12
+ header: "10"
13
+ data: "10"
14
+ value: 10
15
+ - format: nil
16
+ header: "a0"
17
+ data: "a0"
18
+ value: null
19
+ - format: fixmap
20
+ header: "ff"
21
+ length: 2
22
+ children:
23
+ - key:
24
+ format: nil
25
+ data: "a0"
26
+ value:
27
+ format: nil
28
+ data: "a0"
29
+ - key:
30
+ format: nil
31
+ data: "a0"
32
+ value:
33
+ format: nil
34
+ data: "a0"
@@ -1,81 +1,69 @@
1
1
  module MessagePack
2
2
  module Inspect
3
- # Node(Hash)
4
- # header: hex (binary)
5
- # exttype: hex (value)
6
- # length: numeric
7
- # data: hex
8
- # value: object
9
- # children: (array of Node for array, array of Hash, which has keys of :key and :value for map)
10
-
11
3
  class Inspector
12
- FORMATS = [:yaml, :json]
13
-
14
- attr_reader :data
4
+ FORMATS = [:yaml, :json, :jsonl, nil] # nil is for test (without stream dump)
15
5
 
16
- def initialize(io)
6
+ def initialize(io, format = :yaml, opt)
7
+ # return_values: false, output_io: STDOUT
17
8
  @io = io
18
- @data = [] # top level objects
19
- until io.eof?
20
- dig(io){|obj| @data << obj }
21
- end
9
+ @format = format
10
+ @streamer = MessagePack::Inspect::Streamer.get(@format)
11
+ @return_values = opt.fetch(:return_values, false)
12
+ @output_io = opt.fetch(:output_io, STDOUT)
22
13
  end
23
14
 
24
- def hex(str)
25
- str.unpack("H*").first
26
- end
27
-
28
- def node(fmt, header)
29
- case fmt
30
- when :fixint, :uint8, :uint16, :uint32, :uint64, :int8, :int16, :int32, :int64
31
- {format: fmt, header: hex(header), data: nil, value: nil}
32
- when :fixmap, :map16, :map32
33
- {format: fmt, header: hex(header), length: nil, children: []}
34
- when :fixarray, :array16, :array32
35
- {format: fmt, header: hex(header), length: nil, children: []}
36
- when :fixstr, :str8, :str16, :str32
37
- {format: fmt, header: hex(header), length: nil, data: nil, value: nil}
38
- when :nil
39
- {format: fmt, header: hex(header), data: hex(header), value: nil}
40
- when :false
41
- {format: fmt, header: hex(header), data: hex(header), value: false}
42
- when :true
43
- {format: fmt, header: hex(header), data: hex(header), value: true}
44
- when :bin8, :bin16, :bin32
45
- {format: fmt, header: hex(header), length: nil, data: nil, value: nil}
46
- when :ext8, :ext16, :ext32, :fixext1, :fixext2, :fixext4, :fixext8, :fixext16
47
- {format: fmt, header: hex(header), exttype: nil, length: nil, data: nil} # value will be set if MessagePack is installed
48
- when :float32, :float64
49
- {format: fmt, header: hex(header), data: nil, value: nil}
50
- when :never_used
51
- {format: fmt, header: hex(header), data: hex(header), error: "msgpack format 'NEVER USED' specified"}
52
- else
53
- raise "unknown format specifier: #{fmt}"
15
+ def inspect
16
+ data = []
17
+ @streamer.objects(@output_io, 0) do
18
+ i = 0
19
+ until @io.eof?
20
+ @streamer.object(@output_io, 1, i) do
21
+ obj = dig(1)
22
+ data << obj if @return_values
23
+ end
24
+ i += 1
25
+ end
54
26
  end
27
+ @return_values ? data : nil
55
28
  end
56
29
 
57
- def dump(format)
58
- case format
59
- when :yaml
60
- require 'yaml'
61
- YAML.dump(@data)
62
- when :json
63
- begin
64
- require 'yajl'
65
- Yajl::Encoder.encode(@data)
66
- rescue LoadError
67
- require 'json'
68
- JSON.dump(@data)
30
+ def dig(depth, heading = true)
31
+ header_byte = @io.read(1)
32
+ # TODO: error handling for header_byte:nil or raised exception
33
+ header = header_byte.b
34
+ fmt = parse_header(header)
35
+ node = MessagePack::Inspect::Node.new(fmt, header)
36
+ node.extend @streamer
37
+ node.io = @output_io
38
+ node.depth = depth # Streamer#depth=
39
+ node.heading = heading
40
+
41
+ node.attributes(@io)
42
+
43
+ if node.is_array?
44
+ node.elements do |i|
45
+ @streamer.object(@output_io, depth + 2, i) do
46
+ obj = dig(depth + 2) # children -> array
47
+ node << obj if @return_values
48
+ end
49
+ end
50
+ elsif node.is_map?
51
+ node.elements do |i|
52
+ key = node.element_key do
53
+ @streamer.object(@output_io, depth + 3, 0) do
54
+ dig(depth + 3, false) # chilren -> array -> key
55
+ end
56
+ end
57
+ value = node.element_value do
58
+ @streamer.object(@output_io, depth + 3, 0) do
59
+ dig(depth + 3, false) # children -> array -> value
60
+ end
61
+ end
62
+ node[key] = value if @return_values
69
63
  end
70
- else
71
- raise "unknown format: #{format}"
72
64
  end
73
- end
74
65
 
75
- def dig(io, &block)
76
- header = io.read(1).b
77
- fmt = parse_header(header)
78
- yield generate(io, fmt, header, node(fmt, header))
66
+ node
79
67
  end
80
68
 
81
69
  HEADER_FIXINT_POSITIVE = Range.new(0x00, 0x7f)
@@ -159,228 +147,6 @@ module MessagePack
159
147
  raise "never reach here."
160
148
  end
161
149
  end
162
-
163
- def generate_array(io, fmt, header, current)
164
- length = case fmt
165
- when :fixarray
166
- header.unpack('C').first & 0x0f
167
- when :array16
168
- io.read(2).unpack('n').first
169
- when :array32
170
- io.read(4).unpack('N').first
171
- else
172
- raise "unknown array fmt #{fmt}"
173
- end
174
- current[:length] = length
175
- length.times do |i|
176
- dig(io){|obj| current[:children] << obj }
177
- end
178
- current
179
- end
180
-
181
- def generate_map(io, fmt, header, current)
182
- length = case fmt
183
- when :fixmap
184
- header.unpack('C').first & 0x0f
185
- when :map16
186
- io.read(2).unpack('n').first
187
- when :map32
188
- io.read(4).unpack('N').first
189
- else
190
- raise "unknown map fmt #{fmt}"
191
- end
192
- current[:length] = length
193
- length.times do |i|
194
- pair = {}
195
- dig(io){|key| pair[:key] = key }
196
- dig(io){|value| pair[:value] = value }
197
- current[:children] << pair
198
- end
199
- current
200
- end
201
-
202
- MAX_INT16 = 2 ** 16
203
- MAX_INT32 = 2 ** 32
204
- MAX_INT64 = 2 ** 64
205
-
206
- def generate_int(io, fmt, header, current)
207
- if fmt == :fixint
208
- current[:data] = hex(header)
209
- v = header.unpack('C').first
210
- if v & 0b11100000 > 0 # negative fixint
211
- current[:value] = header.unpack('c').first
212
- else # positive fixint
213
- current[:value] = header.unpack('C').first
214
- end
215
- return current
216
- end
217
-
218
- case fmt
219
- when :uint8
220
- v = io.read(1)
221
- current[:data] = hex(v)
222
- current[:value] = v.unpack('C').first
223
- when :uint16
224
- v = io.read(2)
225
- current[:data] = hex(v)
226
- current[:value] = v.unpack('n').first
227
- when :uint32
228
- v = io.read(4)
229
- current[:data] = hex(v)
230
- current[:value] = v.unpack('N').first
231
- when :uint64
232
- v1 = io.read(4)
233
- v2 = io.read(4)
234
- current[:data] = hex(v1) + hex(v2)
235
- current[:value] = (v1.unpack('N').first << 32) | v2.unpack('N').first
236
- when :int8
237
- v = io.read(1)
238
- current[:data] = hex(v)
239
- current[:value] = v.unpack('c').first
240
- when :int16
241
- v = io.read(2)
242
- current[:data] = hex(v)
243
- n = v.unpack('n').first
244
- if (n & 0x8000) > 0 # negative
245
- current[:value] = n - MAX_INT16
246
- else
247
- current[:value] = n
248
- end
249
- when :int32
250
- v = io.read(4)
251
- current[:data] = hex(v)
252
- n = v.unpack('N').first
253
- if n & 0x80000000 > 0 # negative
254
- current[:value] = n - MAX_INT32
255
- else
256
- current[:value] = n
257
- end
258
- when :int64
259
- v1 = io.read(4)
260
- v2 = io.read(4)
261
- current[:data] = hex(v1) + hex(v2)
262
- n = (v1.unpack('N').first << 32) | v2.unpack('N').first
263
- if n & 0x8000_0000_0000_0000 > 0 # negative
264
- current[:value] = n - MAX_INT64
265
- else
266
- current[:value] = n
267
- end
268
- else
269
- raise "unknown int format #{fmt}"
270
- end
271
- current
272
- end
273
-
274
- def generate_float(io, fmt, header, current)
275
- case fmt
276
- when :float32
277
- v = io.read(4)
278
- current[:data] = hex(v)
279
- current[:value] = v.unpack('g').first
280
- when :float64
281
- v = io.read(8)
282
- current[:data] = hex(v)
283
- current[:value] = v.unpack('G').first
284
- else
285
- raise "unknown float format #{fmt}"
286
- end
287
- current
288
- end
289
-
290
- def generate_string(io, fmt, header, current)
291
- length = case fmt
292
- when :fixstr
293
- header.unpack('C').first & 0b00011111
294
- when :str8, :bin8
295
- io.read(1).unpack('C').first
296
- when :str16, :bin16
297
- io.read(2).unpack('n').first
298
- when :str32, :bin32
299
- io.read(4).unpack('N').first
300
- else
301
- raise "unknown string format #{fmt}"
302
- end
303
- current[:length] = length
304
- v = io.read(length)
305
- current[:data] = hex(v)
306
- if [:fixstr, :str8, :str16, :str32].include?(fmt)
307
- begin
308
- current[:value] = v.force_encoding('UTF-8')
309
- rescue
310
- current[:error] = $!.message
311
- end
312
- else
313
- current[:value] = v.b
314
- end
315
- current
316
- end
317
-
318
- begin
319
- require 'msgpack'
320
- rescue LoadError
321
- # ignore
322
- end
323
- MSGPACK_LOADED = MessagePack.const_defined?('Unpacker')
324
-
325
- def generate_ext(io, fmt, header, current)
326
- data = ''.b
327
- length = case fmt
328
- when :fixext1 then 1
329
- when :fixext2 then 2
330
- when :fixext4 then 4
331
- when :fixext8 then 8
332
- when :fixext16 then 16
333
- when :ext8
334
- v = io.read(1)
335
- data << v
336
- v.unpack('C').first
337
- when :ext16
338
- v = io.read(2)
339
- data << v
340
- v.unpack('n').first
341
- when :ext32
342
- v = io.read(4)
343
- data << v
344
- v.unpack('N').first
345
- else
346
- raise "unknown ext format #{fmt}"
347
- end
348
- v = io.read(1)
349
- data << v
350
- type = v.unpack('c').first
351
- current[:length] = length
352
- current[:exttype] = type
353
- val = io.read(length)
354
- data << val
355
- current[:data] = hex(val)
356
- if MSGPACK_LOADED
357
- current[:value] = MessagePack.unpack(val)
358
- end
359
- current
360
- end
361
-
362
- def generate(io, fmt, header, current)
363
- case fmt
364
- when :fixarray, :array16, :array32
365
- generate_array(io, fmt, header, current)
366
- when :fixmap, :map16, :map32
367
- generate_map(io, fmt, header, current)
368
- when :nil, :false, :true, :never_used
369
- # nothing to do...
370
- when :fixint, :uint8, :uint16, :uint32, :uint64, :int8, :int16, :int32, :int64
371
- generate_int(io, fmt, header, current)
372
- when :float32, :float64
373
- generate_float(io, fmt, header, current)
374
- when :fixstr, :str8, :str16, :str32, :bin8, :bin16, :bin32
375
- generate_string(io, fmt, header, current)
376
- when :ext8, :ext16, :ext32, :fixext1, :fixext2, :fixext4, :fixext8, :fixext16
377
- generate_ext(io, fmt, header, current)
378
- else
379
- raise "unknown format #{fmt}"
380
- end
381
-
382
- current
383
- end
384
150
  end
385
151
  end
386
152
  end
@@ -0,0 +1,286 @@
1
+ module MessagePack
2
+ module Inspect
3
+ class Node
4
+ # header: hex (binary)
5
+ # exttype: hex (value)
6
+ # length: numeric
7
+ # data: hex
8
+ # value: object
9
+ # children: (array of Node for array, array of Hash, which has keys of :key and :value for map)
10
+ attr_accessor :format, :header, :length, :exttype, :data, :value, :children, :error
11
+ attr_accessor :io, :depth, :heading
12
+
13
+ FORMATS = [
14
+ :fixint, :uint8, :uint16, :uint32, :uint64, :int8, :int16, :int32, :int64,
15
+ :fixmap, :map16, :map32,
16
+ :fixarray, :array16, :array32,
17
+ :fixstr, :str8, :str16, :str32,
18
+ :nil, :false, :true,
19
+ :bin8, :bin16, :bin32,
20
+ :ext8, :ext16, :ext32, :fixext1, :fixext2, :fixext4, :fixext8, :fixext16,
21
+ :float32, :float64,
22
+ :never_used,
23
+ ]
24
+ ARRAYS = [:fixarray, :array16, :array32]
25
+ MAPS = [:fixmap, :map16, :map32]
26
+
27
+ def initialize(format, header)
28
+ raise "unknown format specifier: #{format}" unless FORMATS.include?(format)
29
+ @format = format
30
+ @header = header # call #hex to dump
31
+ @length = @exttype = @data = @value = @children = @error = nil
32
+
33
+ case @format
34
+ when :fixmap, :map16, :map32, :fixarray, :array16, :array32
35
+ @children = []
36
+ when :nil
37
+ @data = hex(header)
38
+ @value = nil
39
+ when :false
40
+ @data = hex(header)
41
+ @value = false
42
+ when :true
43
+ @data = hex(header)
44
+ @value = true
45
+ when :never_used
46
+ @data = hex(header)
47
+ @error = "msgpack format 'NEVER USED' specified"
48
+ end
49
+
50
+ @first_line = true
51
+ @first_obj = true
52
+ @first_kv_pair = true
53
+ end
54
+
55
+ def hex(str)
56
+ str.unpack("H*").first
57
+ end
58
+
59
+ def elements(&block)
60
+ @length.times do |i|
61
+ yield i
62
+ end
63
+ end
64
+
65
+ def element_key
66
+ yield
67
+ end
68
+
69
+ def element_value
70
+ yield
71
+ end
72
+
73
+ def attributes(io)
74
+ # read attributes from io
75
+ case @format
76
+ when :fixarray, :array16, :array32
77
+ attributes_array(io)
78
+ when :fixmap, :map16, :map32
79
+ attributes_map(io)
80
+ when :nil, :false, :true, :never_used
81
+ # nothing to do...
82
+ when :fixint, :uint8, :uint16, :uint32, :uint64, :int8, :int16, :int32, :int64
83
+ attributes_int(io)
84
+ when :float32, :float64
85
+ attributes_float(io)
86
+ when :fixstr, :str8, :str16, :str32, :bin8, :bin16, :bin32
87
+ attributes_string(io)
88
+ when :ext8, :ext16, :ext32, :fixext1, :fixext2, :fixext4, :fixext8, :fixext16
89
+ attributes_ext(io)
90
+ end
91
+ end
92
+
93
+ def attributes_array(io)
94
+ @length = case @format
95
+ when :fixarray
96
+ @header.unpack('C').first & 0x0f
97
+ when :array16
98
+ io.read(2).unpack('n').first
99
+ when :array32
100
+ io.read(4).unpack('N').first
101
+ else
102
+ raise "unknown array fmt #{@format}"
103
+ end
104
+ end
105
+
106
+ def attributes_map(io)
107
+ @length = case @format
108
+ when :fixmap
109
+ @header.unpack('C').first & 0x0f
110
+ when :map16
111
+ io.read(2).unpack('n').first
112
+ when :map32
113
+ io.read(4).unpack('N').first
114
+ else
115
+ raise "unknown map fmt #{@format}"
116
+ end
117
+ end
118
+
119
+ MAX_INT16 = 2 ** 16
120
+ MAX_INT32 = 2 ** 32
121
+ MAX_INT64 = 2 ** 64
122
+
123
+ def attributes_int(io)
124
+ if @format == :fixint
125
+ @data = hex(@header)
126
+ v = @header.unpack('C').first
127
+ @value = if v & 0b11100000 > 0 # negative fixint
128
+ @header.unpack('c').first
129
+ else # positive fixint
130
+ @header.unpack('C').first
131
+ end
132
+ return
133
+ end
134
+
135
+ case @format
136
+ when :uint8
137
+ v = io.read(1)
138
+ @data = hex(v)
139
+ @value = v.unpack('C').first
140
+ when :uint16
141
+ v = io.read(2)
142
+ @data = hex(v)
143
+ @value = v.unpack('n').first
144
+ when :uint32
145
+ v = io.read(4)
146
+ @data = hex(v)
147
+ @value = v.unpack('N').first
148
+ when :uint64
149
+ v1 = io.read(4)
150
+ v2 = io.read(4)
151
+ @data = hex(v1) + hex(v2)
152
+ @value = (v1.unpack('N').first << 32) | v2.unpack('N').first
153
+ when :int8
154
+ v = io.read(1)
155
+ @data = hex(v)
156
+ @value = v.unpack('c').first
157
+ when :int16
158
+ v = io.read(2)
159
+ @data = hex(v)
160
+ n = v.unpack('n').first
161
+ @value = if (n & 0x8000) > 0 # negative
162
+ n - MAX_INT16
163
+ else
164
+ n
165
+ end
166
+ when :int32
167
+ v = io.read(4)
168
+ @data = hex(v)
169
+ n = v.unpack('N').first
170
+ @value = if n & 0x80000000 > 0 # negative
171
+ n - MAX_INT32
172
+ else
173
+ n
174
+ end
175
+ when :int64
176
+ v1 = io.read(4)
177
+ v2 = io.read(4)
178
+ @data = hex(v1) + hex(v2)
179
+ n = (v1.unpack('N').first << 32) | v2.unpack('N').first
180
+ @value = if n & 0x8000_0000_0000_0000 > 0 # negative
181
+ n - MAX_INT64
182
+ else
183
+ n
184
+ end
185
+ else
186
+ raise "unknown int format #{@format}"
187
+ end
188
+ end
189
+
190
+ def attributes_float(io)
191
+ case @format
192
+ when :float32
193
+ v = io.read(4)
194
+ @data = hex(v)
195
+ @value = v.unpack('g').first
196
+ when :float64
197
+ v = io.read(8)
198
+ @data = hex(v)
199
+ @value = v.unpack('G').first
200
+ else
201
+ raise "unknown float format #{@format}"
202
+ end
203
+ end
204
+
205
+ def attributes_string(io)
206
+ @length = case @format
207
+ when :fixstr
208
+ @header.unpack('C').first & 0b00011111
209
+ when :str8, :bin8
210
+ io.read(1).unpack('C').first
211
+ when :str16, :bin16
212
+ io.read(2).unpack('n').first
213
+ when :str32, :bin32
214
+ io.read(4).unpack('N').first
215
+ else
216
+ raise "unknown string format #{@format}"
217
+ end
218
+ v = io.read(@length)
219
+ @data = hex(v)
220
+ if [:fixstr, :str8, :str16, :str32].include?(@format)
221
+ begin
222
+ @value = v.force_encoding('UTF-8')
223
+ rescue
224
+ @error = $!.message
225
+ end
226
+ else
227
+ @value = v.b
228
+ end
229
+ end
230
+
231
+ MSGPACK_LOADED = MessagePack.const_defined?('Unpacker')
232
+
233
+ def attributes_ext(io)
234
+ data = ''.b
235
+ @length = case @format
236
+ when :fixext1 then 1
237
+ when :fixext2 then 2
238
+ when :fixext4 then 4
239
+ when :fixext8 then 8
240
+ when :fixext16 then 16
241
+ when :ext8
242
+ v = io.read(1)
243
+ data << v
244
+ v.unpack('C').first
245
+ when :ext16
246
+ v = io.read(2)
247
+ data << v
248
+ v.unpack('n').first
249
+ when :ext32
250
+ v = io.read(4)
251
+ data << v
252
+ v.unpack('N').first
253
+ else
254
+ raise "unknown ext format #{@format}"
255
+ end
256
+ v = io.read(1)
257
+ @exttype = v.unpack('c').first
258
+ val = io.read(@length)
259
+ @data = hex(val)
260
+ @value = if MSGPACK_LOADED
261
+ MessagePack.unpack(val)
262
+ else
263
+ nil
264
+ end
265
+ end
266
+
267
+ def is_array?
268
+ ARRAYS.include?(@format)
269
+ end
270
+
271
+ def is_map?
272
+ MAPS.include?(@format)
273
+ end
274
+
275
+ def <<(child)
276
+ raise "adding child object to non-array object" unless is_array?
277
+ @children << child
278
+ end
279
+
280
+ def []=(key, value)
281
+ raise "adding key-value objects to non-map object" unless is_map?
282
+ @children << {key => value}
283
+ end
284
+ end
285
+ end
286
+ end