nfreeze 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/nfreeze.rb +302 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e33622b9785d00552b5dd507b475463f5a81acc0
|
4
|
+
data.tar.gz: cf7ca0366de733cf11828f5421cd215c42f1b10e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aa8c3487284e6bb93456413b0da61db0570063d2baac6041b3b5c0ad83228dd3dc1a6a52969a4c24cd3e737b23690846d90e930e5bf231f097a16446ad4f0e1c
|
7
|
+
data.tar.gz: 1eb2c2a4df5b9703aa38075ac32a99f76abb307d6d35bfe59f6a2f14bec35e8d2d402d6e75d133ef6d36e9829f82a1d5271e2208f24954985007e836b990199a
|
data/lib/nfreeze.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
#! /your/favourite/path/for/ruby
|
2
|
+
# -*- coding: utf-8; -*-
|
3
|
+
#
|
4
|
+
# Copyright(c) 2013 URABE, Shyouhei.
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
# of this code, to deal in the code without restriction, including without
|
8
|
+
# limitation the rights to use, copy, modify, merge, publish, distribute,
|
9
|
+
# sublicense, and/or sell copies of the code, and to permit persons to whom the
|
10
|
+
# code is furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the code.
|
14
|
+
#
|
15
|
+
# THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# CODE.
|
22
|
+
|
23
|
+
require 'stringio'
|
24
|
+
|
25
|
+
# Adds two methods named nfreeze and thaw to Marshal module.
|
26
|
+
#
|
27
|
+
# @note It is worth mentioning that in spite of its being written using not a
|
28
|
+
# little knowledge of both perl internals and ruby internals, this
|
29
|
+
# library contains absolutely 0.00 octets originates from either
|
30
|
+
# projects (as of this writing, at least). So it is both perl-free and
|
31
|
+
# ruby-free, in the sense of licensing. You should strictly stick to
|
32
|
+
# the terms shown at the top of the source code.
|
33
|
+
#
|
34
|
+
# @note Also, for future updates of this library, do not copy & paste other
|
35
|
+
# projects, including perl and/or ruby. That should contaminate
|
36
|
+
# licenses.
|
37
|
+
class << Marshal
|
38
|
+
|
39
|
+
# Serialize the given object in a way compatible with perl.
|
40
|
+
# @param [Object] obj the target object
|
41
|
+
# @return [String] a serialized version of obj.
|
42
|
+
# @raise [ArgumentError] the obj is not serializable using this method.
|
43
|
+
#
|
44
|
+
# Not all kind of objects are serializable. For instance Classes, which are
|
45
|
+
# serializable using Marshal.dump, cannot be serialized by this method,
|
46
|
+
# because it makes no sense to have a class represented in Perl.
|
47
|
+
#
|
48
|
+
# Also for the sake of simple implementation this method pays relatively
|
49
|
+
# little attention to make the generated binary smaller. There are cases
|
50
|
+
# where more compact expressions is possible. All generated binaries are
|
51
|
+
# properly understood by perl though.
|
52
|
+
def nfreeze obj
|
53
|
+
NFREEZE.new.nfreeze obj
|
54
|
+
end
|
55
|
+
|
56
|
+
# Deserialize perl-generated nfreeze strings into ruby objects.
|
57
|
+
# @param [IO, String] obj the source
|
58
|
+
# @return [Object] deserialized object
|
59
|
+
# @raise [TypeError] the obj is not deserializable
|
60
|
+
#
|
61
|
+
# Not all kind of inputs are understood. One big issue is a reference -- in
|
62
|
+
# perl a [] and a \\[] are different, but in ruby you cannot represent such
|
63
|
+
# difference.
|
64
|
+
#
|
65
|
+
# In practice you would better think this method can understand as far as
|
66
|
+
# JSON or YAML or MessagePack or that sort.
|
67
|
+
def thaw obj
|
68
|
+
THAW.new.thaw obj
|
69
|
+
end
|
70
|
+
|
71
|
+
class NFREEZE
|
72
|
+
def nfreeze obj
|
73
|
+
@io.rewind
|
74
|
+
@io.write "\x5\x8"
|
75
|
+
recur obj
|
76
|
+
return @io.string
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def initialize
|
82
|
+
buf = "".encode Encoding::BINARY
|
83
|
+
@io = StringIO.new buf
|
84
|
+
@seen = Hash.new
|
85
|
+
end
|
86
|
+
|
87
|
+
def recur obj, refp = false
|
88
|
+
# We are not implementing Torjan's topological sort algorithm here
|
89
|
+
# because our restriction is stronger than just unable to represent
|
90
|
+
# infinite loops; we can only serlalize pure trees.
|
91
|
+
if @seen.has_key? obj.object_id
|
92
|
+
raise ArgumentError, "cyclic data structures not supportted for now"+
|
93
|
+
obj.inspect
|
94
|
+
else
|
95
|
+
case obj when NilClass, Integer, String then
|
96
|
+
# immediates
|
97
|
+
else
|
98
|
+
@seen.store obj.object_id, nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
case obj
|
102
|
+
when NilClass then dump 14
|
103
|
+
when TrueClass then dump 15
|
104
|
+
when FalseClass then dump 16
|
105
|
+
when Integer then dump_int obj
|
106
|
+
when Float then dump_double obj
|
107
|
+
when String then dump_string obj
|
108
|
+
when Array then dump 4 if refp; dump_array obj
|
109
|
+
when Hash then dump 4 if refp; dump_hash obj
|
110
|
+
else
|
111
|
+
raise ArgumentError, "unsupported class encountered: #{obj.inspect}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def dump x
|
116
|
+
@io.write x.chr
|
117
|
+
end
|
118
|
+
|
119
|
+
def dump_array obj
|
120
|
+
len = obj.length
|
121
|
+
if len > 2147483647
|
122
|
+
raise ArgumentError, "#{len} elems array is too big for perl"
|
123
|
+
else
|
124
|
+
@io.write [2, len].pack('cN')
|
125
|
+
obj.each do |i|
|
126
|
+
recur i, :ref
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def dump_hash obj
|
132
|
+
len = obj.keys.length
|
133
|
+
if len > 2147483647
|
134
|
+
raise ArgumentError, "#{len} elems hash is too big for perl"
|
135
|
+
else
|
136
|
+
@io.write [3, len].pack('cN')
|
137
|
+
obj.each_pair do |k, v|
|
138
|
+
case k when String then
|
139
|
+
len = k.bytesize
|
140
|
+
if len > 2147483647
|
141
|
+
raise ArgumentError, "#{len} octets key is too big for perl"
|
142
|
+
else
|
143
|
+
recur v, :ref
|
144
|
+
@io.write [len, k].pack('NA*')
|
145
|
+
end
|
146
|
+
else
|
147
|
+
raise ArgumentError, "non-string keys cant be represented:"+
|
148
|
+
k.inspect
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def dump_double obj
|
155
|
+
@io.write [7, obj].pack('cd')
|
156
|
+
end
|
157
|
+
|
158
|
+
def dump_int obj
|
159
|
+
case obj when (-2147483648 ... 2147483648) then
|
160
|
+
@io.write [9, obj].pack('cN')
|
161
|
+
else
|
162
|
+
raise ArgumentError, "#{obj.inspect} is too big for perl"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def dump_string obj
|
167
|
+
# Perl can only understand Unicodes
|
168
|
+
newobj = obj.encode Encoding::UTF_8
|
169
|
+
newlen = newobj.bytesize
|
170
|
+
if newlen > 2147483647
|
171
|
+
raise ArgumentError, "#{newlen} octets string is too big for perl"
|
172
|
+
else
|
173
|
+
@io.write [24, newlen, newobj].pack('cNA*')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
private_constant:NFREEZE
|
178
|
+
|
179
|
+
class THAW
|
180
|
+
def thaw obj
|
181
|
+
@io = (obj.to_io rescue StringIO.new(obj.to_str))
|
182
|
+
version_check
|
183
|
+
recur
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
def initialize
|
189
|
+
@io = nil # assigned later
|
190
|
+
@seen = Hash.new
|
191
|
+
end
|
192
|
+
|
193
|
+
def version_check
|
194
|
+
str = @io.read 2
|
195
|
+
x, minor = str.unpack 'cc'
|
196
|
+
netorder = x & 1
|
197
|
+
major = x >> 1
|
198
|
+
if major != 2 or minor <= 6
|
199
|
+
raise "unsupported version #{major}.#{minor}"
|
200
|
+
elsif netorder != 1
|
201
|
+
raise "machine-endian unpredictalbe for this input"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
@@e = Exception.new
|
206
|
+
|
207
|
+
def recur
|
208
|
+
case type = @io.getbyte
|
209
|
+
when 0x01 then load_binary_large
|
210
|
+
when 0x02 then load_array
|
211
|
+
when 0x03 then load_hash
|
212
|
+
when 0x04 then raise @@e # this is ref
|
213
|
+
when 0x05 then nil
|
214
|
+
when 0x06 then raise "Endian mismatch" # machine-natives
|
215
|
+
when 0x07 then load_double
|
216
|
+
when 0x08 then load_byte
|
217
|
+
when 0x09 then load_int
|
218
|
+
when 0x0a then load_binary_tiny
|
219
|
+
# some cases here...
|
220
|
+
when 0x0e then nil
|
221
|
+
when 0x0f then true
|
222
|
+
when 0x10 then false
|
223
|
+
# some cases here...
|
224
|
+
when 0x17 then load_string_tiny
|
225
|
+
when 0x18 then load_string_large
|
226
|
+
else raise TypeError, "can't understand type ##{type}"
|
227
|
+
end
|
228
|
+
rescue Exception => e
|
229
|
+
if e == @@e
|
230
|
+
retry # ignore refs
|
231
|
+
else
|
232
|
+
raise
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def load_byte
|
237
|
+
@io.getbyte - 128
|
238
|
+
end
|
239
|
+
|
240
|
+
def load_int
|
241
|
+
str = @io.read 4
|
242
|
+
len, = str.unpack 'N'
|
243
|
+
len
|
244
|
+
end
|
245
|
+
|
246
|
+
def load_binary len
|
247
|
+
raise "broken #{len}" if len < 0
|
248
|
+
str = @io.read len
|
249
|
+
str.force_encoding Encoding::BINARY
|
250
|
+
str
|
251
|
+
end
|
252
|
+
|
253
|
+
def load_binary_large
|
254
|
+
load_binary load_int
|
255
|
+
end
|
256
|
+
|
257
|
+
def load_binary_tiny
|
258
|
+
load_binary load_byte + 128
|
259
|
+
end
|
260
|
+
|
261
|
+
def load_string len
|
262
|
+
raise "broken #{len}" if len < 0
|
263
|
+
str = @io.read len
|
264
|
+
str.force_encoding Encoding::UTF_8
|
265
|
+
str
|
266
|
+
end
|
267
|
+
|
268
|
+
def load_string_large
|
269
|
+
load_string load_int
|
270
|
+
end
|
271
|
+
|
272
|
+
def load_string_tiny
|
273
|
+
load_string load_byte + 128
|
274
|
+
end
|
275
|
+
|
276
|
+
def load_array
|
277
|
+
load_int.times.map { recur }
|
278
|
+
end
|
279
|
+
|
280
|
+
def load_hash
|
281
|
+
load_int.times.each_with_object Hash.new do |i, ret|
|
282
|
+
# order matters
|
283
|
+
v = recur
|
284
|
+
k = load_binary_large
|
285
|
+
ret.store k, v
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
private_constant:THAW
|
290
|
+
end
|
291
|
+
|
292
|
+
#
|
293
|
+
# Local Variables:
|
294
|
+
# mode: ruby
|
295
|
+
# coding: utf-8
|
296
|
+
# indent-tabs-mode: nil
|
297
|
+
# tab-width: 3
|
298
|
+
# ruby-indent-level: 3
|
299
|
+
# fill-column: 79
|
300
|
+
# default-justification: full
|
301
|
+
# End:
|
302
|
+
# vi:ts=3:sw=3:
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nfreeze
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Urabe, Shyouhei
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: yard
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rdoc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.13'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.13'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: "Adds two methods, thaw and nfreeze, into Marshal module, so that
|
112
|
+
your\n\t\tprogram can understand Perl-generated nfreeze strings."
|
113
|
+
email:
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- lib/nfreeze.rb
|
119
|
+
homepage: https://github.com/shyouhei/nfreeze
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.9.3
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.0.0
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: Ruby translation of p5-Storable
|
143
|
+
test_files: []
|