marshal-structure 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.
- data.tar.gz.sig +0 -0
- data/.autotest +9 -0
- data/.gemtest +0 -0
- data/History.txt +5 -0
- data/Manifest.txt +7 -0
- data/README.rdoc +73 -0
- data/Rakefile +27 -0
- data/lib/marshal/structure.rb +619 -0
- data/test/test_marshal_structure.rb +171 -0
- metadata +142 -0
- metadata.gz.sig +1 -0
data.tar.gz.sig
ADDED
Binary file
|
data/.autotest
ADDED
data/.gemtest
ADDED
File without changes
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
= marshal-structure
|
2
|
+
|
3
|
+
* https://github.com/drbrain/marshal-structure
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Dumps a tree based on the Marshal format. Supports the Marshal 4.8 format.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* Works like Marshal.load
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
From the command line:
|
16
|
+
|
17
|
+
ruby -rpp -rmarshal-structure \
|
18
|
+
-e 'pp Marshal::Structure.load Marshal.dump "hello"'
|
19
|
+
|
20
|
+
Fancier usage:
|
21
|
+
|
22
|
+
require 'pp'
|
23
|
+
require 'marshal-structure'
|
24
|
+
|
25
|
+
ms = Marshal::Structure.new Marshal.dump %w[hello world]
|
26
|
+
|
27
|
+
# print the Marshal stream structure
|
28
|
+
pp ms.construct
|
29
|
+
|
30
|
+
# print ruby objects in Marshal stream
|
31
|
+
pp ms.objects
|
32
|
+
|
33
|
+
== REQUIREMENTS:
|
34
|
+
|
35
|
+
* Ruby 1.8.7+
|
36
|
+
|
37
|
+
== INSTALL:
|
38
|
+
|
39
|
+
gem install marshal-structure
|
40
|
+
|
41
|
+
== DEVELOPERS:
|
42
|
+
|
43
|
+
After checking out the source, run:
|
44
|
+
|
45
|
+
$ rake newb
|
46
|
+
|
47
|
+
This task will install any missing dependencies, run the tests/specs,
|
48
|
+
and generate the RDoc.
|
49
|
+
|
50
|
+
== LICENSE:
|
51
|
+
|
52
|
+
(The MIT License)
|
53
|
+
|
54
|
+
Copyright (c) 2011 Eric Hodel
|
55
|
+
|
56
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
57
|
+
a copy of this software and associated documentation files (the
|
58
|
+
'Software'), to deal in the Software without restriction, including
|
59
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
60
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
61
|
+
permit persons to whom the Software is furnished to do so, subject to
|
62
|
+
the following conditions:
|
63
|
+
|
64
|
+
The above copyright notice and this permission notice shall be
|
65
|
+
included in all copies or substantial portions of the Software.
|
66
|
+
|
67
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
68
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
69
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
70
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
71
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
72
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
73
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.plugin :git
|
7
|
+
Hoe.plugin :isolate
|
8
|
+
Hoe.plugin :minitest
|
9
|
+
|
10
|
+
Hoe.spec 'marshal-structure' do
|
11
|
+
developer 'Eric Hodel', 'drbrain@segment7.net'
|
12
|
+
|
13
|
+
rdoc_locations << 'docs.seattlerb.org:/data/www/docs.seattlerb.org/marshal-structure/'
|
14
|
+
|
15
|
+
self.readme_file = 'README.rdoc'
|
16
|
+
self.extra_rdoc_files << 'README.rdoc' # HACK fix in Hoe
|
17
|
+
|
18
|
+
if respond_to? :isolate_dir= then # HACK Hoe issue #7
|
19
|
+
self.isolate_dir = 'tmp/isolate'
|
20
|
+
else
|
21
|
+
warn 'please: gem install isolate'
|
22
|
+
end
|
23
|
+
|
24
|
+
extra_dev_deps << ['ben_string', '~> 1']
|
25
|
+
end
|
26
|
+
|
27
|
+
# vim: syntax=ruby
|
@@ -0,0 +1,619 @@
|
|
1
|
+
##
|
2
|
+
# Marshal::Structure dumps a nested Array describing the structure of a
|
3
|
+
# Marshal stream.
|
4
|
+
#
|
5
|
+
# Marshal format 4.8 is supported.
|
6
|
+
|
7
|
+
class Marshal::Structure
|
8
|
+
|
9
|
+
##
|
10
|
+
# Version of Marshal::Structure you are using
|
11
|
+
|
12
|
+
VERSION = '1.0'
|
13
|
+
|
14
|
+
##
|
15
|
+
# Supported major Marshal version
|
16
|
+
|
17
|
+
MAJOR_VERSION = 4
|
18
|
+
|
19
|
+
##
|
20
|
+
# Supported minor Marshal version
|
21
|
+
|
22
|
+
MINOR_VERSION = 8
|
23
|
+
|
24
|
+
##
|
25
|
+
# nil type prefix
|
26
|
+
|
27
|
+
TYPE_NIL = '0'
|
28
|
+
|
29
|
+
##
|
30
|
+
# true type prefix
|
31
|
+
|
32
|
+
TYPE_TRUE = 'T'
|
33
|
+
|
34
|
+
##
|
35
|
+
# false type prefix
|
36
|
+
|
37
|
+
TYPE_FALSE = 'F'
|
38
|
+
|
39
|
+
##
|
40
|
+
# Fixnum type prefix
|
41
|
+
|
42
|
+
TYPE_FIXNUM = 'i'
|
43
|
+
|
44
|
+
##
|
45
|
+
# An object that has been extended with a module
|
46
|
+
|
47
|
+
TYPE_EXTENDED = 'e'
|
48
|
+
|
49
|
+
##
|
50
|
+
# A subclass of a built-in type
|
51
|
+
|
52
|
+
TYPE_UCLASS = 'C'
|
53
|
+
|
54
|
+
##
|
55
|
+
# A ruby Object
|
56
|
+
|
57
|
+
TYPE_OBJECT = 'o'
|
58
|
+
|
59
|
+
##
|
60
|
+
# A wrapped C pointer
|
61
|
+
|
62
|
+
TYPE_DATA = 'd'
|
63
|
+
|
64
|
+
##
|
65
|
+
# An object saved with _dump
|
66
|
+
|
67
|
+
TYPE_USERDEF = 'u'
|
68
|
+
|
69
|
+
##
|
70
|
+
# An object saved with marshal_dump
|
71
|
+
|
72
|
+
TYPE_USRMARSHAL = 'U'
|
73
|
+
|
74
|
+
##
|
75
|
+
# A Float
|
76
|
+
|
77
|
+
TYPE_FLOAT = 'f'
|
78
|
+
|
79
|
+
##
|
80
|
+
# A Bignum
|
81
|
+
|
82
|
+
TYPE_BIGNUM = 'l'
|
83
|
+
|
84
|
+
##
|
85
|
+
# A String
|
86
|
+
|
87
|
+
TYPE_STRING = '"'
|
88
|
+
|
89
|
+
##
|
90
|
+
# A Regexp
|
91
|
+
|
92
|
+
TYPE_REGEXP = '/'
|
93
|
+
|
94
|
+
##
|
95
|
+
# An Array
|
96
|
+
|
97
|
+
TYPE_ARRAY = '['
|
98
|
+
|
99
|
+
##
|
100
|
+
# A Hash
|
101
|
+
|
102
|
+
TYPE_HASH = '{'
|
103
|
+
|
104
|
+
##
|
105
|
+
# A Hash with a default value (not proc)
|
106
|
+
|
107
|
+
TYPE_HASH_DEF = '}'
|
108
|
+
|
109
|
+
##
|
110
|
+
# A Struct
|
111
|
+
|
112
|
+
TYPE_STRUCT = 'S'
|
113
|
+
|
114
|
+
##
|
115
|
+
# An old-style Module (reference, not content)
|
116
|
+
#
|
117
|
+
# I'm not sure what makes this old. The byte stream is identical to
|
118
|
+
# TYPE_MODULE
|
119
|
+
|
120
|
+
TYPE_MODULE_OLD = 'M'
|
121
|
+
|
122
|
+
##
|
123
|
+
# A class (reference, not content)
|
124
|
+
|
125
|
+
TYPE_CLASS = 'c'
|
126
|
+
|
127
|
+
##
|
128
|
+
# A module (reference, not content)
|
129
|
+
|
130
|
+
TYPE_MODULE = 'm'
|
131
|
+
|
132
|
+
##
|
133
|
+
# A Symbol
|
134
|
+
|
135
|
+
TYPE_SYMBOL = ':'
|
136
|
+
|
137
|
+
##
|
138
|
+
# A reference to a previously Symbol
|
139
|
+
|
140
|
+
TYPE_SYMLINK = ';'
|
141
|
+
|
142
|
+
##
|
143
|
+
# Instance variables for a following object
|
144
|
+
|
145
|
+
TYPE_IVAR = 'I'
|
146
|
+
|
147
|
+
##
|
148
|
+
# A reference to a previously-stored Object
|
149
|
+
|
150
|
+
TYPE_LINK = '@'
|
151
|
+
|
152
|
+
##
|
153
|
+
# Objects found in the Marshal stream. Since objects aren't constructed the
|
154
|
+
# actual object won't be present in this list.
|
155
|
+
|
156
|
+
attr_reader :objects
|
157
|
+
|
158
|
+
##
|
159
|
+
# Symbols found in the Marshal stream
|
160
|
+
|
161
|
+
attr_reader :symbols
|
162
|
+
|
163
|
+
##
|
164
|
+
# Returns the structure of the Marshaled object +obj+ as nested Arrays.
|
165
|
+
#
|
166
|
+
# For +true+, +false+ and +nil+ the symbol +:true+, +:false+, +:nil+ is
|
167
|
+
# returned, respectively.
|
168
|
+
#
|
169
|
+
# For Fixnum the value is returned.
|
170
|
+
#
|
171
|
+
# For other objects the first item is the reference for future occurrences
|
172
|
+
# of the object and the remaining items describe the object.
|
173
|
+
#
|
174
|
+
# Symbols have a separate reference table from all other objects.
|
175
|
+
|
176
|
+
def self.load obj
|
177
|
+
if obj.respond_to? :to_str then
|
178
|
+
data = obj.to_s
|
179
|
+
elsif obj.respond_to? :read then
|
180
|
+
data = obj.read
|
181
|
+
if data.empty? then
|
182
|
+
raise EOFError, "end of file reached"
|
183
|
+
end
|
184
|
+
elsif obj.respond_to? :getc then # FIXME - don't read all of it upfront
|
185
|
+
data = ''
|
186
|
+
data << c while (c = obj.getc.chr)
|
187
|
+
else
|
188
|
+
raise TypeError, "instance of IO needed"
|
189
|
+
end
|
190
|
+
|
191
|
+
major = data[0].ord
|
192
|
+
minor = data[1].ord
|
193
|
+
|
194
|
+
if major != MAJOR_VERSION or minor > MINOR_VERSION then
|
195
|
+
raise TypeError, "incompatible marshal file format (can't be read)\n\tformat version #{MAJOR_VERSION}.#{MINOR_VERSION} required; #{major}.#{minor} given"
|
196
|
+
end
|
197
|
+
|
198
|
+
new(data).construct
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Prepares processing of +stream+
|
203
|
+
|
204
|
+
def initialize stream
|
205
|
+
@objects = []
|
206
|
+
@symbols = []
|
207
|
+
|
208
|
+
@stream = stream
|
209
|
+
@byte_array = stream.bytes.to_a
|
210
|
+
@consumed = 2
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
# Adds +obj+ to the objects list
|
215
|
+
|
216
|
+
def add_object obj
|
217
|
+
return if
|
218
|
+
[NilClass, TrueClass, FalseClass, Symbol, Fixnum].any? { |c| c === obj }
|
219
|
+
|
220
|
+
index = @objects.size
|
221
|
+
@objects << obj
|
222
|
+
index
|
223
|
+
end
|
224
|
+
|
225
|
+
##
|
226
|
+
# Adds +symbol+ to the symbols list
|
227
|
+
|
228
|
+
def add_symlink symbol
|
229
|
+
index = @symbols.size
|
230
|
+
@symbols << symbol
|
231
|
+
index
|
232
|
+
end
|
233
|
+
|
234
|
+
##
|
235
|
+
# Creates the structure for the remaining stream.
|
236
|
+
|
237
|
+
def construct
|
238
|
+
type = consume_character
|
239
|
+
|
240
|
+
case type
|
241
|
+
when TYPE_NIL then
|
242
|
+
:nil
|
243
|
+
when TYPE_TRUE then
|
244
|
+
:true
|
245
|
+
when TYPE_FALSE then
|
246
|
+
:false
|
247
|
+
|
248
|
+
when TYPE_ARRAY then
|
249
|
+
[:array, *construct_array]
|
250
|
+
when TYPE_BIGNUM then
|
251
|
+
[:bignum, *construct_bignum]
|
252
|
+
when TYPE_CLASS then
|
253
|
+
ref = store_unique_object Object.allocate
|
254
|
+
|
255
|
+
[:class, ref, get_byte_sequence]
|
256
|
+
when TYPE_DATA then
|
257
|
+
[:data, *construct_data]
|
258
|
+
when TYPE_EXTENDED then
|
259
|
+
[:extended, get_symbol, construct]
|
260
|
+
when TYPE_FIXNUM then
|
261
|
+
[:fixnum, construct_integer]
|
262
|
+
when TYPE_FLOAT then
|
263
|
+
[:float, *construct_float]
|
264
|
+
when TYPE_HASH then
|
265
|
+
[:hash, *construct_hash]
|
266
|
+
when TYPE_HASH_DEF then
|
267
|
+
[:hash_default, *construct_hash_def]
|
268
|
+
when TYPE_IVAR then
|
269
|
+
[:instance_variables, construct, *construct_instance_variables]
|
270
|
+
when TYPE_LINK then
|
271
|
+
[:link, construct_integer]
|
272
|
+
when TYPE_MODULE, TYPE_MODULE_OLD then
|
273
|
+
ref = store_unique_object Object.allocate
|
274
|
+
|
275
|
+
[:module, ref, get_byte_sequence]
|
276
|
+
when TYPE_OBJECT then
|
277
|
+
[:object, *construct_object]
|
278
|
+
when TYPE_REGEXP then
|
279
|
+
[:regexp, *construct_regexp]
|
280
|
+
when TYPE_STRING then
|
281
|
+
[:string, *construct_string]
|
282
|
+
when TYPE_STRUCT then
|
283
|
+
[:struct, *construct_struct]
|
284
|
+
when TYPE_SYMBOL then
|
285
|
+
[:symbol, *construct_symbol]
|
286
|
+
when TYPE_SYMLINK then
|
287
|
+
[:symbol_link, construct_integer]
|
288
|
+
when TYPE_USERDEF then
|
289
|
+
[:user_defined, *construct_user_defined]
|
290
|
+
when TYPE_USRMARSHAL then
|
291
|
+
[:user_marshal, *construct_user_marshal]
|
292
|
+
when TYPE_UCLASS then
|
293
|
+
name = get_symbol
|
294
|
+
|
295
|
+
[:user_class, name, construct]
|
296
|
+
else
|
297
|
+
raise ArgumentError, "load error, unknown type #{type}"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
##
|
302
|
+
# Creates the body of an +:array+ object
|
303
|
+
|
304
|
+
def construct_array
|
305
|
+
ref = store_unique_object Object.allocate
|
306
|
+
|
307
|
+
obj = [ref]
|
308
|
+
|
309
|
+
items = construct_integer
|
310
|
+
|
311
|
+
obj << items
|
312
|
+
|
313
|
+
items.times do
|
314
|
+
obj << construct
|
315
|
+
end
|
316
|
+
|
317
|
+
obj
|
318
|
+
end
|
319
|
+
|
320
|
+
##
|
321
|
+
# Creates the body of a +:bignum+ object
|
322
|
+
|
323
|
+
def construct_bignum
|
324
|
+
sign = consume_byte == ?- ? -1 : 1
|
325
|
+
size = construct_integer * 2
|
326
|
+
|
327
|
+
result = 0
|
328
|
+
|
329
|
+
data = consume_bytes size
|
330
|
+
|
331
|
+
data.each_with_index do |data, exp|
|
332
|
+
result += (data * 2**(exp*8))
|
333
|
+
end
|
334
|
+
|
335
|
+
ref = store_unique_object Object.allocate
|
336
|
+
|
337
|
+
[ref, sign, size, result]
|
338
|
+
end
|
339
|
+
|
340
|
+
##
|
341
|
+
# Creates the body of a wrapped C pointer object
|
342
|
+
|
343
|
+
def construct_data
|
344
|
+
ref = store_unique_object Object.allocate
|
345
|
+
|
346
|
+
[ref, get_symbol, construct]
|
347
|
+
end
|
348
|
+
|
349
|
+
##
|
350
|
+
# Creates the body of a +:float+ object
|
351
|
+
|
352
|
+
def construct_float
|
353
|
+
float = get_byte_sequence
|
354
|
+
|
355
|
+
ref = store_unique_object Object.allocate
|
356
|
+
|
357
|
+
[ref, float]
|
358
|
+
end
|
359
|
+
|
360
|
+
##
|
361
|
+
# Creates the body of a +:hash+ object
|
362
|
+
|
363
|
+
def construct_hash
|
364
|
+
ref = store_unique_object Object.allocate
|
365
|
+
|
366
|
+
obj = [ref]
|
367
|
+
|
368
|
+
pairs = construct_integer
|
369
|
+
obj << pairs
|
370
|
+
|
371
|
+
pairs.times do
|
372
|
+
obj << construct
|
373
|
+
obj << construct
|
374
|
+
end
|
375
|
+
|
376
|
+
obj
|
377
|
+
end
|
378
|
+
|
379
|
+
##
|
380
|
+
# Creates the body of a +:hash_def+ object
|
381
|
+
|
382
|
+
def construct_hash_def
|
383
|
+
ref, hash = construct_hash
|
384
|
+
|
385
|
+
[ref, hash, construct]
|
386
|
+
end
|
387
|
+
|
388
|
+
##
|
389
|
+
# Instance variables contain an object followed by a count of instance
|
390
|
+
# variables and their contents
|
391
|
+
|
392
|
+
def construct_instance_variables
|
393
|
+
instance_variables = []
|
394
|
+
|
395
|
+
pairs = construct_integer
|
396
|
+
instance_variables << pairs
|
397
|
+
|
398
|
+
pairs.times do
|
399
|
+
instance_variables << get_symbol
|
400
|
+
instance_variables << construct
|
401
|
+
end
|
402
|
+
|
403
|
+
instance_variables
|
404
|
+
end
|
405
|
+
|
406
|
+
##
|
407
|
+
# Decodes a stored Fixnum
|
408
|
+
|
409
|
+
def construct_integer
|
410
|
+
c = consume_byte
|
411
|
+
|
412
|
+
# The format appears to be a simple integer compression format
|
413
|
+
#
|
414
|
+
# The 0-123 cases are easy, and use one byte
|
415
|
+
# We've read c as unsigned char in a way, but we need to honor
|
416
|
+
# the sign bit. We do that by simply comparing with the +128 values
|
417
|
+
return 0 if c == 0
|
418
|
+
return c - 5 if 4 < c and c < 128
|
419
|
+
|
420
|
+
# negative, but checked known it's instead in 2's compliment
|
421
|
+
return c - 251 if 252 > c and c > 127
|
422
|
+
|
423
|
+
# otherwise c (now in the 1 to 4 range) indicates how many
|
424
|
+
# bytes to read to construct the value.
|
425
|
+
#
|
426
|
+
# Because we're operating on a small number of possible values,
|
427
|
+
# it's cleaner to just unroll the calculate of each
|
428
|
+
|
429
|
+
case c
|
430
|
+
when 1
|
431
|
+
consume_byte
|
432
|
+
when 2
|
433
|
+
consume_byte | (consume_byte << 8)
|
434
|
+
when 3
|
435
|
+
consume_byte | (consume_byte << 8) | (consume_byte << 16)
|
436
|
+
when 4
|
437
|
+
consume_byte | (consume_byte << 8) | (consume_byte << 16) |
|
438
|
+
(consume_byte << 24)
|
439
|
+
|
440
|
+
when 255 # -1
|
441
|
+
consume_byte - 256
|
442
|
+
when 254 # -2
|
443
|
+
(consume_byte | (consume_byte << 8)) - 65536
|
444
|
+
when 253 # -3
|
445
|
+
(consume_byte |
|
446
|
+
(consume_byte << 8) |
|
447
|
+
(consume_byte << 16)) - 16777216 # 2 ** 24
|
448
|
+
when 252 # -4
|
449
|
+
(consume_byte |
|
450
|
+
(consume_byte << 8) |
|
451
|
+
(consume_byte << 16) |
|
452
|
+
(consume_byte << 24)) - 4294967296
|
453
|
+
else
|
454
|
+
raise "Invalid integer size: #{c}"
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
##
|
459
|
+
# Creates an Object
|
460
|
+
|
461
|
+
def construct_object
|
462
|
+
ref = store_unique_object Object.allocate
|
463
|
+
|
464
|
+
[ref, get_symbol, construct_instance_variables]
|
465
|
+
end
|
466
|
+
|
467
|
+
##
|
468
|
+
# Creates a Regexp
|
469
|
+
|
470
|
+
def construct_regexp
|
471
|
+
ref =store_unique_object Object.allocate
|
472
|
+
|
473
|
+
[ref, get_byte_sequence, consume_byte]
|
474
|
+
end
|
475
|
+
|
476
|
+
##
|
477
|
+
# Creates a String
|
478
|
+
|
479
|
+
def construct_string
|
480
|
+
ref = store_unique_object Object.allocate
|
481
|
+
|
482
|
+
[ref, get_byte_sequence]
|
483
|
+
end
|
484
|
+
|
485
|
+
##
|
486
|
+
# Creates a Struct
|
487
|
+
|
488
|
+
def construct_struct
|
489
|
+
symbols = []
|
490
|
+
values = []
|
491
|
+
|
492
|
+
obj_ref = store_unique_object Object.allocate
|
493
|
+
|
494
|
+
obj = [obj_ref, get_symbol]
|
495
|
+
|
496
|
+
members = construct_integer
|
497
|
+
obj << members
|
498
|
+
|
499
|
+
members.times do
|
500
|
+
obj << get_symbol
|
501
|
+
obj << construct
|
502
|
+
end
|
503
|
+
|
504
|
+
obj
|
505
|
+
end
|
506
|
+
|
507
|
+
##
|
508
|
+
# Creates a Symbol
|
509
|
+
|
510
|
+
def construct_symbol
|
511
|
+
sym = get_byte_sequence
|
512
|
+
|
513
|
+
ref = store_unique_object sym.to_sym
|
514
|
+
|
515
|
+
[ref, sym]
|
516
|
+
end
|
517
|
+
|
518
|
+
##
|
519
|
+
# Creates an object saved by _dump
|
520
|
+
|
521
|
+
def construct_user_defined
|
522
|
+
name = get_symbol
|
523
|
+
|
524
|
+
data = get_byte_sequence
|
525
|
+
|
526
|
+
ref = store_unique_object Object.allocate
|
527
|
+
|
528
|
+
[ref, name, data]
|
529
|
+
end
|
530
|
+
|
531
|
+
##
|
532
|
+
# Creates an object saved by marshal_dump
|
533
|
+
|
534
|
+
def construct_user_marshal
|
535
|
+
name = get_symbol
|
536
|
+
|
537
|
+
obj = Object.allocate
|
538
|
+
|
539
|
+
obj_ref = store_unique_object obj
|
540
|
+
|
541
|
+
[obj_ref, name, construct]
|
542
|
+
end
|
543
|
+
|
544
|
+
##
|
545
|
+
# Consumes +bytes+ from the marshal stream
|
546
|
+
|
547
|
+
def consume bytes
|
548
|
+
raise ArgumentError, "marshal data too short" if @consumed > @stream.size
|
549
|
+
data = @stream[@consumed, bytes]
|
550
|
+
@consumed += bytes
|
551
|
+
data
|
552
|
+
end
|
553
|
+
|
554
|
+
##
|
555
|
+
# Consumes +count+ bytes from the marshal stream as an Array of bytes
|
556
|
+
|
557
|
+
def consume_bytes count
|
558
|
+
consume(count).bytes.to_a
|
559
|
+
end
|
560
|
+
|
561
|
+
##
|
562
|
+
# Consumes one byte from the marshal stream
|
563
|
+
|
564
|
+
def consume_byte
|
565
|
+
raise ArgumentError, "marshal data too short" if
|
566
|
+
@consumed > @byte_array.size
|
567
|
+
|
568
|
+
data = @byte_array[@consumed]
|
569
|
+
@consumed += 1
|
570
|
+
|
571
|
+
data
|
572
|
+
end
|
573
|
+
|
574
|
+
##
|
575
|
+
# Consumes one byte from the marshal stream and returns a character
|
576
|
+
|
577
|
+
def consume_character
|
578
|
+
consume_byte.chr
|
579
|
+
end
|
580
|
+
|
581
|
+
##
|
582
|
+
# Consumes a sequence of bytes from the marshal stream based on the next
|
583
|
+
# integer
|
584
|
+
|
585
|
+
def get_byte_sequence
|
586
|
+
size = construct_integer
|
587
|
+
consume size
|
588
|
+
end
|
589
|
+
|
590
|
+
##
|
591
|
+
# Constructs a Symbol from a TYPE_SYMBOL or TYPE_SYMLINK
|
592
|
+
|
593
|
+
def get_symbol
|
594
|
+
type = consume_character
|
595
|
+
|
596
|
+
case type
|
597
|
+
when TYPE_SYMBOL then
|
598
|
+
[:symbol, *construct_symbol]
|
599
|
+
when TYPE_SYMLINK then
|
600
|
+
num = construct_integer
|
601
|
+
[:symbol_link, num]
|
602
|
+
else
|
603
|
+
raise ArgumentError, "expected TYPE_SYMBOL or TYPE_SYMLINK, got #{type.inspect}"
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
##
|
608
|
+
# Stores a reference to +obj+
|
609
|
+
|
610
|
+
def store_unique_object obj
|
611
|
+
if Symbol === obj then
|
612
|
+
add_symlink obj
|
613
|
+
else
|
614
|
+
add_object obj
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
end
|
619
|
+
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'marshal/structure'
|
3
|
+
require 'ben_string'
|
4
|
+
require 'openssl'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
class OpenSSL::X509::Name
|
8
|
+
alias _dump_data to_a
|
9
|
+
|
10
|
+
def _load_data ary
|
11
|
+
ary.each do |entry|
|
12
|
+
add_entry(*entry)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class B; end
|
18
|
+
|
19
|
+
module C; end
|
20
|
+
|
21
|
+
module E; end
|
22
|
+
|
23
|
+
class M
|
24
|
+
def marshal_dump
|
25
|
+
'marshal_dump'
|
26
|
+
end
|
27
|
+
|
28
|
+
def marshal_load o
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class U
|
33
|
+
def self._load str
|
34
|
+
new
|
35
|
+
end
|
36
|
+
|
37
|
+
def _dump limit
|
38
|
+
s = '_dump'
|
39
|
+
s.instance_variable_set :@ivar_on_dump_str, 'value on ivar on dump str'
|
40
|
+
s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
S = Struct.new :f
|
45
|
+
|
46
|
+
class TestMarshalStructure < MiniTest::Unit::TestCase
|
47
|
+
|
48
|
+
def mu_pp obj
|
49
|
+
s = ''
|
50
|
+
s = PP.pp obj, s
|
51
|
+
s.chomp
|
52
|
+
end
|
53
|
+
|
54
|
+
def setup
|
55
|
+
@MS = Marshal::Structure
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_construct
|
59
|
+
str =
|
60
|
+
"\004\b{\006:\006a[\031c\006Bm\006C\"\006d/\006e\000i\006" \
|
61
|
+
"f\0322.2999999999999998\000ff" \
|
62
|
+
"l+\n\000\000\000\000\000\000\000\000\001\0000TF}\000i\000" \
|
63
|
+
"S:\006S\006:\006fi\000o:\vObject\000@\017" \
|
64
|
+
"U:\006M\"\021marshal_dump" \
|
65
|
+
"Iu:\006U\n_dump\006" \
|
66
|
+
":\026@ivar_on_dump_str\"\036value on ivar on dump str" \
|
67
|
+
";\000e:\006Eo;\b\000" \
|
68
|
+
"I\"\025string with ivar\006:\v@value\"\017some value" \
|
69
|
+
"C:\016BenString\"\000"
|
70
|
+
|
71
|
+
structure = @MS.load str
|
72
|
+
|
73
|
+
expected = [
|
74
|
+
:hash,
|
75
|
+
0,
|
76
|
+
1,
|
77
|
+
[:symbol, 0, "a"],
|
78
|
+
[:array,
|
79
|
+
1,
|
80
|
+
20,
|
81
|
+
[:class, 2, "B"],
|
82
|
+
[:module, 3, "C"],
|
83
|
+
[:string, 4, "d"],
|
84
|
+
[:regexp, 5, "e", 0],
|
85
|
+
[:fixnum, 1],
|
86
|
+
[:float, 6, "2.2999999999999998\000ff"],
|
87
|
+
[:bignum, 7, 1, 10, 18446744073709551616],
|
88
|
+
:nil,
|
89
|
+
:true,
|
90
|
+
:false,
|
91
|
+
[:hash_default, 8, 0, [:fixnum, 0]],
|
92
|
+
[:struct, 9, [:symbol, 1, "S"], 1, [:symbol, 2, "f"], [:fixnum, 0]],
|
93
|
+
[:object, 10, [:symbol, 3, "Object"], [0]],
|
94
|
+
[:link, 10],
|
95
|
+
[:user_marshal, 11, [:symbol, 4, "M"], [:string, 12, "marshal_dump"]],
|
96
|
+
[:instance_variables,
|
97
|
+
[:user_defined, 13, [:symbol, 5, "U"], "_dump"],
|
98
|
+
1,
|
99
|
+
[:symbol, 6, "@ivar_on_dump_str"],
|
100
|
+
[:string, 14, "value on ivar on dump str"]],
|
101
|
+
[:symbol_link, 0],
|
102
|
+
[:extended, [:symbol, 7, "E"], [:object, 15, [:symbol_link, 3], [0]]],
|
103
|
+
[:instance_variables,
|
104
|
+
[:string, 16, "string with ivar"],
|
105
|
+
1,
|
106
|
+
[:symbol, 8, "@value"],
|
107
|
+
[:string, 17, "some value"]],
|
108
|
+
[:user_class, [:symbol, 9, "BenString"], [:string, 18, ""]]]]
|
109
|
+
|
110
|
+
assert_equal expected, structure
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_construct_data
|
114
|
+
name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example'
|
115
|
+
str = Marshal.dump name
|
116
|
+
|
117
|
+
expected = [
|
118
|
+
:data,
|
119
|
+
0,
|
120
|
+
[:symbol, 0, "OpenSSL::X509::Name"],
|
121
|
+
[:array,
|
122
|
+
1,
|
123
|
+
2,
|
124
|
+
[:array, 2, 3,
|
125
|
+
[:string, 3, "CN"],
|
126
|
+
[:string, 4, "nobody"],
|
127
|
+
[:fixnum, 12]],
|
128
|
+
[:array, 5, 3,
|
129
|
+
[:string, 6, "DC"],
|
130
|
+
[:string, 7, "example"],
|
131
|
+
[:fixnum, 22]]]]
|
132
|
+
|
133
|
+
assert_equal expected, @MS.load(str)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_construct_module_old
|
137
|
+
assert_equal [:module, 0, "M"], @MS.load("\x04\x08M\x06M")
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_consume
|
141
|
+
ms = @MS.new "\x04\x08\x06M"
|
142
|
+
|
143
|
+
assert_equal "\x06M", ms.consume(2)
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_consume_bytes
|
147
|
+
ms = @MS.new "\x04\x08\x06M"
|
148
|
+
|
149
|
+
assert_equal [6, 77], ms.consume_bytes(2)
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_consume_byte
|
153
|
+
ms = @MS.new "\x04\x08M"
|
154
|
+
|
155
|
+
assert_equal 77, ms.consume_byte
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_consume_character
|
159
|
+
ms = @MS.new "\x04\x08M"
|
160
|
+
|
161
|
+
assert_equal 'M', ms.consume_character
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_get_byte_sequence
|
165
|
+
ms = @MS.new "\x04\x08\x06M"
|
166
|
+
|
167
|
+
assert_equal "M", ms.get_byte_sequence
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: marshal-structure
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: "1.0"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Eric Hodel
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain:
|
16
|
+
- |
|
17
|
+
-----BEGIN CERTIFICATE-----
|
18
|
+
MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
|
19
|
+
YWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZFgNu
|
20
|
+
ZXQwHhcNMDcxMjIxMDIwNDE0WhcNMDgxMjIwMDIwNDE0WjBBMRAwDgYDVQQDDAdk
|
21
|
+
cmJyYWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZ
|
22
|
+
FgNuZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbbgLrGLGIDE76
|
23
|
+
LV/cvxdEzCuYuS3oG9PrSZnuDweySUfdp/so0cDq+j8bqy6OzZSw07gdjwFMSd6J
|
24
|
+
U5ddZCVywn5nnAQ+Ui7jMW54CYt5/H6f2US6U0hQOjJR6cpfiymgxGdfyTiVcvTm
|
25
|
+
Gj/okWrQl0NjYOYBpDi+9PPmaH2RmLJu0dB/NylsDnW5j6yN1BEI8MfJRR+HRKZY
|
26
|
+
mUtgzBwF1V4KIZQ8EuL6I/nHVu07i6IkrpAgxpXUfdJQJi0oZAqXurAV3yTxkFwd
|
27
|
+
g62YrrW26mDe+pZBzR6bpLE+PmXCzz7UxUq3AE0gPHbiMXie3EFE0oxnsU3lIduh
|
28
|
+
sCANiQ8BAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
|
29
|
+
BBS5k4Z75VSpdM0AclG2UvzFA/VW5DANBgkqhkiG9w0BAQUFAAOCAQEAHagT4lfX
|
30
|
+
kP/hDaiwGct7XPuVGbrOsKRVD59FF5kETBxEc9UQ1clKWngf8JoVuEoKD774dW19
|
31
|
+
bU0GOVWO+J6FMmT/Cp7nuFJ79egMf/gy4gfUfQMuvfcr6DvZUPIs9P/TlK59iMYF
|
32
|
+
DIOQ3DxdF3rMzztNUCizN4taVscEsjCcgW6WkUJnGdqlu3OHWpQxZBJkBTjPCoc6
|
33
|
+
UW6on70SFPmAy/5Cq0OJNGEWBfgD9q7rrs/X8GGwUWqXb85RXnUVi/P8Up75E0ag
|
34
|
+
14jEc90kN+C7oI/AGCBN0j6JnEtYIEJZibjjDJTSMWlUKKkj30kq7hlUC2CepJ4v
|
35
|
+
x52qPcexcYZR7w==
|
36
|
+
-----END CERTIFICATE-----
|
37
|
+
|
38
|
+
date: 2011-08-06 00:00:00 Z
|
39
|
+
dependencies:
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: minitest
|
42
|
+
prerelease: false
|
43
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
hash: 1
|
49
|
+
segments:
|
50
|
+
- 2
|
51
|
+
- 3
|
52
|
+
- 1
|
53
|
+
version: 2.3.1
|
54
|
+
type: :development
|
55
|
+
version_requirements: *id001
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: ben_string
|
58
|
+
prerelease: false
|
59
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ~>
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 1
|
65
|
+
segments:
|
66
|
+
- 1
|
67
|
+
version: "1"
|
68
|
+
type: :development
|
69
|
+
version_requirements: *id002
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: hoe
|
72
|
+
prerelease: false
|
73
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 17
|
79
|
+
segments:
|
80
|
+
- 2
|
81
|
+
- 9
|
82
|
+
version: "2.9"
|
83
|
+
type: :development
|
84
|
+
version_requirements: *id003
|
85
|
+
description: Dumps a tree based on the Marshal format. Supports the Marshal 4.8 format.
|
86
|
+
email:
|
87
|
+
- drbrain@segment7.net
|
88
|
+
executables: []
|
89
|
+
|
90
|
+
extensions: []
|
91
|
+
|
92
|
+
extra_rdoc_files:
|
93
|
+
- History.txt
|
94
|
+
- Manifest.txt
|
95
|
+
- README.rdoc
|
96
|
+
files:
|
97
|
+
- .autotest
|
98
|
+
- History.txt
|
99
|
+
- Manifest.txt
|
100
|
+
- README.rdoc
|
101
|
+
- Rakefile
|
102
|
+
- lib/marshal/structure.rb
|
103
|
+
- test/test_marshal_structure.rb
|
104
|
+
- .gemtest
|
105
|
+
homepage: https://github.com/drbrain/marshal-structure
|
106
|
+
licenses: []
|
107
|
+
|
108
|
+
metadata: {}
|
109
|
+
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options:
|
112
|
+
- --main
|
113
|
+
- README.rdoc
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: 3
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
version: "0"
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
hash: 3
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
version: "0"
|
134
|
+
requirements: []
|
135
|
+
|
136
|
+
rubyforge_project: marshal-structure
|
137
|
+
rubygems_version: 1.8.6
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: Dumps a tree based on the Marshal format
|
141
|
+
test_files:
|
142
|
+
- test/test_marshal_structure.rb
|
metadata.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@��\�*<�᳷�WM����F�h��;���ȵ0c
|