nfreeze 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/nfreeze.rb +302 -0
  3. 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: []