knu-perlstorable 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/perlstorable.rb +428 -0
- metadata +54 -0
data/lib/perlstorable.rb
ADDED
@@ -0,0 +1,428 @@
|
|
1
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
2
|
+
#--
|
3
|
+
# perlstorable.rb - a library that emulates deserialization of Perl's Storable module
|
4
|
+
#++
|
5
|
+
# Copyright (c) 2009 Akinori MUSHA <knu@iDaemons.org>
|
6
|
+
#
|
7
|
+
# All rights reserved. You can redistribute and/or modify it under the same
|
8
|
+
# terms as Ruby.
|
9
|
+
#
|
10
|
+
# == Overview
|
11
|
+
#
|
12
|
+
# This library deals with data serialized by Perl's Storable module.
|
13
|
+
#
|
14
|
+
# This library requires ruby 1.8.7 or better (including 1.9) at the moment.
|
15
|
+
|
16
|
+
require 'stringio'
|
17
|
+
|
18
|
+
#
|
19
|
+
# This module handles the data structure defined and implemented by
|
20
|
+
# Perl's Storable module.
|
21
|
+
#
|
22
|
+
module PerlStorable
|
23
|
+
#
|
24
|
+
# call-seq:
|
25
|
+
# PerlStorable.thaw(str) => object
|
26
|
+
#
|
27
|
+
# Deserializes a string serialized by Perl's Storable module.
|
28
|
+
#
|
29
|
+
# Only data frozen by Storable::nfreeze() is supported at the
|
30
|
+
# moment.
|
31
|
+
#
|
32
|
+
# Blessed Perl objects can be distinguished by using
|
33
|
+
# PerlStorable.blessed?(), and the package name of a blessed object
|
34
|
+
# can be obtained by PerlBlessed#perl_class.
|
35
|
+
#
|
36
|
+
# A list of currently unsupported data types includes:
|
37
|
+
# - Tied objects (scalar/array/hash etc.)
|
38
|
+
# - Weak reference
|
39
|
+
# - Code references
|
40
|
+
#
|
41
|
+
def self.thaw(string_or_iolike)
|
42
|
+
if string_or_iolike.respond_to?(:read)
|
43
|
+
io = string_or_iolike
|
44
|
+
need_close = false
|
45
|
+
else
|
46
|
+
io = StringIO.new(string_or_iolike)
|
47
|
+
need_close = true
|
48
|
+
end
|
49
|
+
|
50
|
+
case magic = io.read(2)
|
51
|
+
when "\x05\x07"
|
52
|
+
# data frozen by Storable::nfreeze()
|
53
|
+
else
|
54
|
+
raise ArgumentError, 'unsupported format'
|
55
|
+
end
|
56
|
+
|
57
|
+
PerlStorable::Reader.new(io).read
|
58
|
+
ensure
|
59
|
+
io.close if need_close
|
60
|
+
end
|
61
|
+
|
62
|
+
SX_OBJECT = 0 # Already stored object
|
63
|
+
SX_LSCALAR = 1 # Scalar (large binary) follows (length, data)
|
64
|
+
SX_ARRAY = 2 # Array forthcominng (size, item list)
|
65
|
+
SX_HASH = 3 # Hash forthcoming (size, key/value pair list)
|
66
|
+
SX_REF = 4 # Reference to object forthcoming
|
67
|
+
SX_UNDEF = 5 # Undefined scalar
|
68
|
+
SX_INTEGER = 6 # Integer forthcoming
|
69
|
+
SX_DOUBLE = 7 # Double forthcoming
|
70
|
+
SX_BYTE = 8 # (signed) byte forthcoming
|
71
|
+
SX_NETINT = 9 # Integer in network order forthcoming
|
72
|
+
SX_SCALAR = 10 # Scalar (binary, small) follows (length, data)
|
73
|
+
SX_TIED_ARRAY = 11 # Tied array forthcoming
|
74
|
+
SX_TIED_HASH = 12 # Tied hash forthcoming
|
75
|
+
SX_TIED_SCALAR = 13 # Tied scalar forthcoming
|
76
|
+
SX_SV_UNDEF = 14 # Perl's immortal PL_sv_undef
|
77
|
+
SX_SV_YES = 15 # Perl's immortal PL_sv_yes
|
78
|
+
SX_SV_NO = 16 # Perl's immortal PL_sv_no
|
79
|
+
SX_BLESS = 17 # Object is blessed
|
80
|
+
SX_IX_BLESS = 18 # Object is blessed, classname given by index
|
81
|
+
SX_HOOK = 19 # Stored via hook, user-defined
|
82
|
+
SX_OVERLOAD = 20 # Overloaded reference
|
83
|
+
SX_TIED_KEY = 21 # Tied magic key forthcoming
|
84
|
+
SX_TIED_IDX = 22 # Tied magic index forthcoming
|
85
|
+
SX_UTF8STR = 23 # UTF-8 string forthcoming (small)
|
86
|
+
SX_LUTF8STR = 24 # UTF-8 string forthcoming (large)
|
87
|
+
SX_FLAG_HASH = 25 # Hash with flags forthcoming (size, flags, key/flags/value triplet list)
|
88
|
+
SX_CODE = 26 # Code references as perl source code
|
89
|
+
SX_WEAKREF = 27 # Weak reference to object forthcoming
|
90
|
+
SX_WEAKOVERLOAD = 28 # Overloaded weak reference
|
91
|
+
SX_ERROR = 29 # Error
|
92
|
+
|
93
|
+
SHF_TYPE_MASK = 0x03
|
94
|
+
SHF_LARGE_CLASSLEN = 0x04
|
95
|
+
SHF_LARGE_STRLEN = 0x08
|
96
|
+
SHF_LARGE_LISTLEN = 0x10
|
97
|
+
SHF_IDX_CLASSNAME = 0x20
|
98
|
+
SHF_NEED_RECURSE = 0x40
|
99
|
+
SHF_HAS_LIST = 0x80
|
100
|
+
|
101
|
+
SHT_SCALAR = 0
|
102
|
+
SHT_ARRAY = 1
|
103
|
+
SHT_HASH = 2
|
104
|
+
SHT_EXTRA = 3
|
105
|
+
|
106
|
+
SHT_TSCALAR = 4 # 4 + 0 -- tied scalar
|
107
|
+
SHT_TARRAY = 5 # 4 + 1 -- tied array
|
108
|
+
SHT_THASH = 6 # 4 + 2 -- tied hash
|
109
|
+
|
110
|
+
def self.inspect_value(value, const_regex = nil) # :rdoc
|
111
|
+
names = constants.select { |name|
|
112
|
+
(!const_regex || const_regex.match(name)) &&
|
113
|
+
const_get(name) == value
|
114
|
+
}
|
115
|
+
if names.empty?
|
116
|
+
value.inspect
|
117
|
+
else
|
118
|
+
"%s (%s)" % [value.inspect, names.join(" | ")]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class PlaceHolder # :nodoc:
|
123
|
+
end
|
124
|
+
|
125
|
+
PH_REF = PlaceHolder.new
|
126
|
+
PH_TIED = PlaceHolder.new
|
127
|
+
|
128
|
+
class Reader # :nodoc: all
|
129
|
+
def initialize(io)
|
130
|
+
@io = io
|
131
|
+
@objects = []
|
132
|
+
@packages = []
|
133
|
+
end
|
134
|
+
|
135
|
+
def read_byte
|
136
|
+
@io.getbyte
|
137
|
+
end
|
138
|
+
|
139
|
+
def read_netint32
|
140
|
+
n = @io.read(4).unpack('N').first
|
141
|
+
if n <= 2147483647
|
142
|
+
n
|
143
|
+
else
|
144
|
+
n - 4294967296
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def read_int32
|
149
|
+
read_netint32
|
150
|
+
end
|
151
|
+
|
152
|
+
def read_flexlen
|
153
|
+
len = read_byte
|
154
|
+
if (len & 0x80) != 0
|
155
|
+
read_int32
|
156
|
+
else
|
157
|
+
len
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def read_blob(len)
|
162
|
+
@io.read(len)
|
163
|
+
end
|
164
|
+
|
165
|
+
if defined?(::Encoding)
|
166
|
+
def read_string(len)
|
167
|
+
@io.read(len).force_encoding('UTF-8')
|
168
|
+
end
|
169
|
+
else
|
170
|
+
def read_string(len)
|
171
|
+
@io.read(len)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def remember_object(object)
|
176
|
+
if @bless
|
177
|
+
object = PerlStorable.bless(object, @bless)
|
178
|
+
end
|
179
|
+
unless object.nil?
|
180
|
+
@objects.each_index { |i|
|
181
|
+
@objects[i] = object if @objects[i].is_a?(PlaceHolder)
|
182
|
+
}
|
183
|
+
end
|
184
|
+
@objects << object
|
185
|
+
object
|
186
|
+
end
|
187
|
+
|
188
|
+
def remember_tied()
|
189
|
+
@objects << PH_TIED
|
190
|
+
end
|
191
|
+
|
192
|
+
def remember_ref()
|
193
|
+
@objects << PH_REF
|
194
|
+
end
|
195
|
+
|
196
|
+
def remember_ref_undo()
|
197
|
+
@objects.pop while @objects.last.equal?(PH_REF)
|
198
|
+
end
|
199
|
+
|
200
|
+
def lookup_object(index)
|
201
|
+
@objects[index]
|
202
|
+
end
|
203
|
+
|
204
|
+
def remember_package(package)
|
205
|
+
@packages << package
|
206
|
+
package
|
207
|
+
end
|
208
|
+
|
209
|
+
def read_object(type)
|
210
|
+
case type
|
211
|
+
when SX_SCALAR
|
212
|
+
remember_object(read_blob(read_byte))
|
213
|
+
when SX_LSCALAR
|
214
|
+
remember_object(read_blob(read_int32))
|
215
|
+
when SX_BYTE
|
216
|
+
remember_object(read_byte - 128)
|
217
|
+
when SX_NETINT
|
218
|
+
remember_object(read_netint32)
|
219
|
+
when SX_UTF8STR
|
220
|
+
remember_object(read_string(read_byte))
|
221
|
+
when SX_LUTF8STR
|
222
|
+
remember_object(read_string(read_int32))
|
223
|
+
when SX_ARRAY
|
224
|
+
len = read_int32
|
225
|
+
ary = Array.new(len)
|
226
|
+
remember_object(ary)
|
227
|
+
len.times { |i|
|
228
|
+
ary[i] = read
|
229
|
+
}
|
230
|
+
ary
|
231
|
+
when SX_HASH
|
232
|
+
size = read_int32
|
233
|
+
hash = Hash.new
|
234
|
+
remember_object(hash)
|
235
|
+
size.times {
|
236
|
+
value = read
|
237
|
+
key = read_object(SX_LSCALAR)
|
238
|
+
hash[key] = value
|
239
|
+
}
|
240
|
+
hash
|
241
|
+
when SX_FLAG_HASH
|
242
|
+
frozen = (read_byte != 0)
|
243
|
+
size = read_int32
|
244
|
+
hash = Hash.new
|
245
|
+
remember_object(hash)
|
246
|
+
size.times {
|
247
|
+
value = read
|
248
|
+
flag = (read_byte != 0)
|
249
|
+
if flag
|
250
|
+
key = read_object(SX_LUTF8STR)
|
251
|
+
else
|
252
|
+
key = read_object(SX_LSCALAR)
|
253
|
+
end
|
254
|
+
hash[key] = value
|
255
|
+
}
|
256
|
+
hash.freeze if frozen
|
257
|
+
hash
|
258
|
+
when SX_REF
|
259
|
+
# In Perl, both an object and a reference to it must be
|
260
|
+
# remembered but in Ruby, there is no difference between them
|
261
|
+
# because everything is a reference, so remember the object
|
262
|
+
# twice. remember_ref puts a placeholder for forward reference.
|
263
|
+
remember_ref
|
264
|
+
read
|
265
|
+
when SX_OBJECT
|
266
|
+
# The following object is already remembered, so cancel
|
267
|
+
# remembering.
|
268
|
+
remember_ref_undo
|
269
|
+
lookup_object(read_int32)
|
270
|
+
when SX_OVERLOAD
|
271
|
+
read
|
272
|
+
when SX_BLESS
|
273
|
+
package = read_blob(read_flexlen)
|
274
|
+
remember_package(package)
|
275
|
+
# Blessing an object is simply labeling (does not produce a
|
276
|
+
# new reference), so there is no extra remember_object()
|
277
|
+
# needed here, but it has to be made sure that the following
|
278
|
+
# object is blessed and that is exactly what read_blessed()
|
279
|
+
# does.
|
280
|
+
object = read_blessed(package)
|
281
|
+
when SX_IX_BLESS
|
282
|
+
package = @packages[read_flexlen]
|
283
|
+
object = read_blessed(package)
|
284
|
+
when SX_HOOK
|
285
|
+
flags = read_byte
|
286
|
+
|
287
|
+
if (flags & SHF_IDX_CLASSNAME) != 0
|
288
|
+
package = @packages[read_int32]
|
289
|
+
elsif (flags & SHF_LARGE_CLASSLEN) != 0
|
290
|
+
package = read_blob(read_int32)
|
291
|
+
else
|
292
|
+
package = read_blob(read_byte)
|
293
|
+
end
|
294
|
+
|
295
|
+
if (flags & SHF_LARGE_STRLEN) != 0
|
296
|
+
string = read_blob(read_int32)
|
297
|
+
else
|
298
|
+
string = read_blob(read_byte)
|
299
|
+
end
|
300
|
+
|
301
|
+
remember_object(string)
|
302
|
+
|
303
|
+
if (flags & SHF_HAS_LIST) != 0
|
304
|
+
raise TypeError, 'SX_HOOK having a list not implemented'
|
305
|
+
case flags & SHF_TYPE_MASK
|
306
|
+
when SHT_TSCALAR
|
307
|
+
when SHT_TARRAY
|
308
|
+
when SHT_THASH
|
309
|
+
end
|
310
|
+
else
|
311
|
+
PerlStorable.bless(string, package)
|
312
|
+
end
|
313
|
+
when SX_TIED_SCALAR, SX_TIED_ARRAY, SX_TIED_HASH
|
314
|
+
# Tying an object produces a new object, so there has to be a
|
315
|
+
# placeholder just like the SX_REF case. However, it may not
|
316
|
+
# be affected by remember_ref_undo() because tie always
|
317
|
+
# produces a new object, hence remember_tied() instead of
|
318
|
+
# remember_ref().
|
319
|
+
remember_tied
|
320
|
+
read
|
321
|
+
when SX_SV_YES
|
322
|
+
true
|
323
|
+
when SX_SV_NO
|
324
|
+
false
|
325
|
+
when SX_UNDEF, SX_SV_UNDEF
|
326
|
+
nil
|
327
|
+
else
|
328
|
+
raise TypeError, 'unknown data type: %s' % PerlStorable.inspect_value(type, /^SX_/)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def read_blessed(package)
|
333
|
+
# Make sure the following object is blessed before it is
|
334
|
+
# remembered.
|
335
|
+
@bless = package
|
336
|
+
read
|
337
|
+
ensure
|
338
|
+
@bless = nil
|
339
|
+
end
|
340
|
+
|
341
|
+
# Reads an object at the posision.
|
342
|
+
def read
|
343
|
+
read_object(read_byte)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# This module is used to represent a Perl object blessed in a
|
348
|
+
# package by extending an object to hold a package name.
|
349
|
+
module PerlBlessed
|
350
|
+
# Returns the Perl class the object was blessed into.
|
351
|
+
attr_reader :perl_class
|
352
|
+
|
353
|
+
# call-seq:
|
354
|
+
# perl_bless(perl_class) => self
|
355
|
+
#
|
356
|
+
# Blesses the object into +perl_class+ (String).
|
357
|
+
def perl_bless(perl_class)
|
358
|
+
@perl_class = perl_class
|
359
|
+
self
|
360
|
+
end
|
361
|
+
|
362
|
+
# :stopdoc:
|
363
|
+
def inspect_blessed
|
364
|
+
'#<PerlBlessed(%s, %s)>' % [inspect_unblessed, perl_class.inspect]
|
365
|
+
end
|
366
|
+
|
367
|
+
def self.included(mod)
|
368
|
+
mod.module_eval {
|
369
|
+
alias inspect_unblessed inspect
|
370
|
+
alias inspect inspect_blessed
|
371
|
+
}
|
372
|
+
end
|
373
|
+
# :startdoc:
|
374
|
+
|
375
|
+
end
|
376
|
+
|
377
|
+
class PerlScalar
|
378
|
+
def initialize(value)
|
379
|
+
@value = value
|
380
|
+
end
|
381
|
+
|
382
|
+
def inspect
|
383
|
+
'#<%s:%s>' % [self.class, @value.inspect]
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# call-seq:
|
388
|
+
# bless(object, perl_class) => self
|
389
|
+
#
|
390
|
+
# Blesses a given object into +perl_class+ (String).
|
391
|
+
def self.bless(obj, perl_class)
|
392
|
+
if !obj.is_a?(PerlBlessed)
|
393
|
+
# Use include instead of extend for PerlBlessed.included to be
|
394
|
+
# called
|
395
|
+
begin
|
396
|
+
class << obj
|
397
|
+
include PerlBlessed
|
398
|
+
end
|
399
|
+
rescue
|
400
|
+
obj = PerlScalar.new(obj)
|
401
|
+
class << obj
|
402
|
+
include PerlBlessed
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
obj.perl_bless(perl_class)
|
407
|
+
end
|
408
|
+
|
409
|
+
# call-seq:
|
410
|
+
# blessed?(object) => boolean
|
411
|
+
#
|
412
|
+
# Tests if an object is blessed.
|
413
|
+
def self.blessed?(obj)
|
414
|
+
obj.is_a?(PerlBlessed)
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
if $0 == __FILE__
|
419
|
+
eval DATA.read, nil, $0, __LINE__+4
|
420
|
+
end
|
421
|
+
|
422
|
+
__END__
|
423
|
+
|
424
|
+
# TODO: Real tests needed
|
425
|
+
|
426
|
+
require 'pp'
|
427
|
+
obj = PerlStorable.thaw(ARGF.read)
|
428
|
+
pp obj
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: knu-perlstorable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Akinori MUSHA
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-13 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A Ruby module that emulates deserialization of Perl's Storable module.
|
17
|
+
email: knu@idaemons.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/perlstorable.rb
|
26
|
+
has_rdoc: true
|
27
|
+
homepage: http://github.com/knu/ruby-perlstorable
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options:
|
30
|
+
- --inline-source
|
31
|
+
- --charset=UTF-8
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 1.8.7
|
39
|
+
version:
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
requirements: []
|
47
|
+
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.2.0
|
50
|
+
signing_key:
|
51
|
+
specification_version: 2
|
52
|
+
summary: A Ruby module that emulates deserialization of Perl's Storable module.
|
53
|
+
test_files: []
|
54
|
+
|