rencoder 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.ruby-version +1 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/lib/rencoder/coder.rb +12 -0
- data/lib/rencoder/decoder.rb +122 -0
- data/lib/rencoder/encoder.rb +96 -0
- data/lib/rencoder/version.rb +3 -0
- data/lib/rencoder.rb +63 -0
- data/rencoder.gemspec +23 -0
- data/spec/rencoder/decoder_spec.rb +95 -0
- data/spec/rencoder/encoder_spec.rb +135 -0
- data/spec/spec_helper.rb +80 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2e4a5fe649e623f7058b7e081b03710bb11a16dc
|
4
|
+
data.tar.gz: f1a19bd39b9d269f72ca11d22deb1f12abbb859a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d5f773dac8f19d1409d5febdac6cb94f2360b7fb21962fcafa59c662725b955804594ccf93d802e02df950ae5a3e24423268b0cf97c59d10dcaeabc3d5179685
|
7
|
+
data.tar.gz: 716bfbe002718eac3ea92b3803e8a3426e788d3d882405d1862afd62cd0cec33a3fc4a21671cf22bd6d4d42fcaabf167662c85f857a22aeacbe52a02fbaa432d
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.4
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Igor Yamolov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Rencoder
|
2
|
+
|
3
|
+
Rencoder is pure Ruby implementation of Rencoder serialization format encoding/decoding.
|
4
|
+
|
5
|
+
Rencoder is *FULLY* compliant with Python implementation, and uses all optimizations (by-type-offset integers, strings, arrays, hashes) both in encoding and decoding.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
### Serialization
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'rencoder'
|
13
|
+
|
14
|
+
Rencode.dump("Hello World") # Strings
|
15
|
+
|
16
|
+
Rencode.dump(100) # Integer
|
17
|
+
|
18
|
+
Rencoder.dump(1.0001) # Floats
|
19
|
+
|
20
|
+
Rencoder.dump({ hello: "world" }) # Hashes
|
21
|
+
|
22
|
+
Rencoder.dump(["hello", :world, 123]) # Arrays
|
23
|
+
```
|
24
|
+
|
25
|
+
**Float precion notice**
|
26
|
+
Rencoder uses 64-bit precision by default.
|
27
|
+
It's highly recommended to stay that way.
|
28
|
+
If there is strong reason to use 32-bit precision, then please specify
|
29
|
+
``float32: true`` option for ``Rencoder.dump``:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
Rencoder.dump(1.000001, float32: true)
|
33
|
+
```
|
34
|
+
***Using 32-bit precision is highly NOT recommended***
|
35
|
+
|
36
|
+
### Deserialization
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'rencoder'
|
40
|
+
|
41
|
+
Rencoder.load(hash_data)
|
42
|
+
# => { 'hello': 'world' }
|
43
|
+
|
44
|
+
Rencoder.load(string_data)
|
45
|
+
# => "Hello World"
|
46
|
+
|
47
|
+
# etc
|
48
|
+
```
|
49
|
+
|
50
|
+
## Installation
|
51
|
+
|
52
|
+
Add this line to your application's Gemfile:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
gem 'rencoder'
|
56
|
+
```
|
57
|
+
|
58
|
+
And then execute:
|
59
|
+
|
60
|
+
$ bundle
|
61
|
+
|
62
|
+
Or install it yourself as:
|
63
|
+
|
64
|
+
$ gem install rencoder
|
65
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Rencoder
|
2
|
+
module Decoder
|
3
|
+
INTEGER_DECODING_MAP = {
|
4
|
+
CHR_INT1 => [1, 'c'],
|
5
|
+
CHR_INT2 => [2, 's>'],
|
6
|
+
CHR_INT4 => [4, 'l>'],
|
7
|
+
CHR_INT8 => [8, 'q>']
|
8
|
+
}
|
9
|
+
|
10
|
+
def decode(buffer)
|
11
|
+
buffer = StringIO.new(buffer) unless buffer.respond_to?(:read) # IO object
|
12
|
+
|
13
|
+
type = buffer.getbyte
|
14
|
+
|
15
|
+
case type
|
16
|
+
when STR_HEADER, STR_FIXED
|
17
|
+
decode_string(buffer, type)
|
18
|
+
when CHR_INT, CHR_INT1, CHR_INT2, CHR_INT4, CHR_INT8, INT_POS_FIXED, INT_NEG_FIXED
|
19
|
+
decode_integer(buffer, type)
|
20
|
+
when CHR_FLOAT32, CHR_FLOAT64
|
21
|
+
decode_float(buffer, type)
|
22
|
+
when CHR_TRUE, CHR_FALSE
|
23
|
+
decode_boolean(buffer, type)
|
24
|
+
when CHR_NONE
|
25
|
+
decode_nil(buffer, type)
|
26
|
+
when CHR_LIST, LIST_FIXED
|
27
|
+
decode_array(buffer, type)
|
28
|
+
when CHR_DICT, DICT_FIXED
|
29
|
+
decode_hash(buffer, type)
|
30
|
+
when CHR_TERM
|
31
|
+
:rencode_term
|
32
|
+
else
|
33
|
+
raise "Unknown type '#{type.inspect}'"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def decode_integer(buffer, type)
|
40
|
+
case type
|
41
|
+
when CHR_INT
|
42
|
+
read_till(buffer).to_i
|
43
|
+
when CHR_INT1, CHR_INT2, CHR_INT4, CHR_INT8
|
44
|
+
size, template = INTEGER_DECODING_MAP[type]
|
45
|
+
|
46
|
+
buffer.read(size).unpack(template).first
|
47
|
+
when INT_POS_FIXED
|
48
|
+
type - INT_POS_FIXED_START
|
49
|
+
when INT_NEG_FIXED
|
50
|
+
-1-(type - INT_NEG_FIXED_START)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def decode_float(buffer, type)
|
55
|
+
case type
|
56
|
+
when CHR_FLOAT32
|
57
|
+
buffer.read(4).unpack('g')
|
58
|
+
when CHR_FLOAT64
|
59
|
+
buffer.read(8).unpack('G')
|
60
|
+
end.first
|
61
|
+
end
|
62
|
+
|
63
|
+
def decode_string(buffer, type)
|
64
|
+
case type
|
65
|
+
when STR_FIXED
|
66
|
+
buffer.read(type - STR_FIXED_START)
|
67
|
+
when STR_HEADER
|
68
|
+
length = type.chr + read_till(buffer, ':')
|
69
|
+
|
70
|
+
buffer.read(length.to_i)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def decode_boolean(buffer, type)
|
75
|
+
type == CHR_TRUE
|
76
|
+
end
|
77
|
+
|
78
|
+
def decode_array(buffer, type)
|
79
|
+
case type
|
80
|
+
when CHR_LIST
|
81
|
+
result = []
|
82
|
+
|
83
|
+
while (item = decode(buffer)) != :rencode_term
|
84
|
+
result << item
|
85
|
+
end
|
86
|
+
|
87
|
+
result
|
88
|
+
when LIST_FIXED
|
89
|
+
size = type - LIST_FIXED_START
|
90
|
+
|
91
|
+
size.times.map do |i|
|
92
|
+
decode(buffer)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def decode_hash(buffer, type)
|
98
|
+
case type
|
99
|
+
when CHR_DICT
|
100
|
+
result = {}
|
101
|
+
|
102
|
+
while (key = decode(buffer)) != :rencode_term
|
103
|
+
result[key] = decode(buffer)
|
104
|
+
end
|
105
|
+
|
106
|
+
result
|
107
|
+
when DICT_FIXED
|
108
|
+
size = type - DICT_FIXED_START
|
109
|
+
|
110
|
+
Hash[size.times.map { [decode(buffer), decode(buffer)] }]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def decode_nil(buffer, type)
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def read_till(buffer, separator = CHR_TERM.chr)
|
119
|
+
buffer.gets(separator).chomp(separator)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Rencoder
|
2
|
+
module Encoder
|
3
|
+
class EncodingError < StandardError; end
|
4
|
+
|
5
|
+
def encode(object)
|
6
|
+
case object
|
7
|
+
when String, Symbol then encode_string(object)
|
8
|
+
when Fixnum then encode_integer(object)
|
9
|
+
when Float then encode_float(object)
|
10
|
+
when TrueClass, FalseClass then encode_boolean(object)
|
11
|
+
when NilClass then encode_nil(object)
|
12
|
+
when Array then encode_array(object)
|
13
|
+
when Hash then encode_hash(object)
|
14
|
+
else
|
15
|
+
raise EncodingError, "Unable to serialize '#{object.class}'"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def encode_integer(object)
|
20
|
+
case object
|
21
|
+
when (0...INT_POS_FIXED_COUNT) # predefined positive intger
|
22
|
+
[INT_POS_FIXED_START + object].pack('C')
|
23
|
+
when (-INT_NEG_FIXED_COUNT...0) # predefined negative integer
|
24
|
+
[INT_NEG_FIXED_START-1-object].pack('C')
|
25
|
+
when (-128...128)
|
26
|
+
[CHR_INT1, object].pack('Cc') # 8-bit signed
|
27
|
+
when (-32768...32768)
|
28
|
+
[CHR_INT2, object].pack('Cs>') # 16-bit signed
|
29
|
+
when (-2147483648...2147483648)
|
30
|
+
[CHR_INT4, object].pack('Cl>') # 32-bit signed
|
31
|
+
when (-9223372036854775808...9223372036854775808)
|
32
|
+
[CHR_INT8, object].pack('Cq>') # 64-bit signed
|
33
|
+
else # encode as ASCII
|
34
|
+
bytes = object.to_s.bytes
|
35
|
+
|
36
|
+
if bytes.size >= MAX_INT_LENGTH
|
37
|
+
raise EncodingError, "Unable to serialize Fixnum #{object} due to overflow"
|
38
|
+
end
|
39
|
+
|
40
|
+
[CHR_INT, *bytes, CHR_TERM].pack('C*')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def encode_float(object)
|
45
|
+
# Always serialize floats as 64-bit, since single-precision serialization is a poo
|
46
|
+
# If you don't believe me try this:
|
47
|
+
#
|
48
|
+
# [1.1].pack('F').unpack('F').first
|
49
|
+
# => 1.100000023841858
|
50
|
+
#
|
51
|
+
if options[:float32] # not recommended
|
52
|
+
[CHR_FLOAT32, object].pack('Cg')
|
53
|
+
else
|
54
|
+
[CHR_FLOAT64, object].pack('CG')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def encode_string(object)
|
59
|
+
bytes = object.to_s
|
60
|
+
|
61
|
+
if bytes.size < STR_FIXED_COUNT
|
62
|
+
(STR_FIXED_START + bytes.size).chr + bytes
|
63
|
+
else
|
64
|
+
"#{bytes.bytesize.to_s}:#{bytes}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def encode_boolean(object)
|
69
|
+
[object ? CHR_TRUE : CHR_FALSE].pack('C')
|
70
|
+
end
|
71
|
+
|
72
|
+
def encode_array(object)
|
73
|
+
array_data = object.map { |item| encode(item) }.join
|
74
|
+
|
75
|
+
if object.size < LIST_FIXED_COUNT
|
76
|
+
(LIST_FIXED_START + object.size).chr + array_data
|
77
|
+
else
|
78
|
+
CHR_LIST.chr + array_data + CHR_TERM.chr
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def encode_hash(object)
|
83
|
+
hash_data = object.map { |key, value| encode(key) + encode(value) }.join
|
84
|
+
|
85
|
+
if object.size < DICT_FIXED_COUNT
|
86
|
+
(DICT_FIXED_START + object.size).chr + hash_data
|
87
|
+
else
|
88
|
+
CHR_DICT.chr + hash_data + CHR_TERM.chr
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def encode_nil(object)
|
93
|
+
[CHR_NONE].pack('C')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/rencoder.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rencoder/version'
|
2
|
+
|
3
|
+
module Rencoder
|
4
|
+
# Rencoder Constants
|
5
|
+
MAX_INT_LENGTH = 64
|
6
|
+
|
7
|
+
# Type constants
|
8
|
+
CHR_LIST = 59
|
9
|
+
CHR_DICT = 60
|
10
|
+
CHR_INT = 61
|
11
|
+
CHR_INT1 = 62
|
12
|
+
CHR_INT2 = 63
|
13
|
+
CHR_INT4 = 64
|
14
|
+
CHR_INT8 = 65
|
15
|
+
CHR_FLOAT32 = 66
|
16
|
+
CHR_FLOAT64 = 44
|
17
|
+
CHR_TRUE = 67
|
18
|
+
CHR_FALSE = 68
|
19
|
+
CHR_NONE = 69
|
20
|
+
CHR_TERM = 127
|
21
|
+
|
22
|
+
# Dictionaries with length embedded in typecode.
|
23
|
+
DICT_FIXED_START = 102
|
24
|
+
DICT_FIXED_COUNT = 25
|
25
|
+
DICT_FIXED = (DICT_FIXED_START...DICT_FIXED_START + DICT_FIXED_COUNT)
|
26
|
+
|
27
|
+
# Positive integers with value embedded in typecode.
|
28
|
+
INT_POS_FIXED_START = 0
|
29
|
+
INT_POS_FIXED_COUNT = 44
|
30
|
+
INT_POS_FIXED = (INT_POS_FIXED_START...INT_POS_FIXED_START + INT_POS_FIXED_COUNT)
|
31
|
+
|
32
|
+
# Negative integers with value embedded in typecode.
|
33
|
+
INT_NEG_FIXED_START = 70
|
34
|
+
INT_NEG_FIXED_COUNT = 32
|
35
|
+
INT_NEG_FIXED = (INT_NEG_FIXED_START...INT_NEG_FIXED_START + INT_NEG_FIXED_COUNT)
|
36
|
+
|
37
|
+
# String length header
|
38
|
+
STR_HEADER = ('0'.ord..'9'.ord)
|
39
|
+
|
40
|
+
# Strings with length embedded in typecode.
|
41
|
+
STR_FIXED_START = 128
|
42
|
+
STR_FIXED_COUNT = 64
|
43
|
+
STR_FIXED = (STR_FIXED_START..STR_FIXED_START + STR_FIXED_COUNT)
|
44
|
+
|
45
|
+
# Lists with length embedded in typecode.
|
46
|
+
LIST_FIXED_START = STR_FIXED_START+STR_FIXED_COUNT
|
47
|
+
LIST_FIXED_COUNT = 64
|
48
|
+
LIST_FIXED = (LIST_FIXED_START..LIST_FIXED_START + LIST_FIXED_COUNT)
|
49
|
+
|
50
|
+
require_relative 'rencoder/encoder'
|
51
|
+
require_relative 'rencoder/decoder'
|
52
|
+
require_relative 'rencoder/coder'
|
53
|
+
|
54
|
+
def load(buffer, options = {})
|
55
|
+
Rencoder::Coder.new(options).decode(buffer)
|
56
|
+
end
|
57
|
+
|
58
|
+
def dump(object, options = {})
|
59
|
+
Rencoder::Coder.new(options).encode(object)
|
60
|
+
end
|
61
|
+
|
62
|
+
module_function :dump, :load
|
63
|
+
end
|
data/rencoder.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rencoder/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'rencoder'
|
8
|
+
spec.version = Rencoder::VERSION
|
9
|
+
spec.authors = ['Igor Yamolov']
|
10
|
+
spec.email = ['clouster@yandex.ru']
|
11
|
+
spec.summary = %q{Rencoder is pure Ruby implementation of Rencoder serialization format encoding/decoding.}
|
12
|
+
spec.description = %q{Rencoder is implementation of Rencoder encoding/decoding.}
|
13
|
+
spec.homepage = 'https://github.com/t3hk0d3/rencoder'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
22
|
+
spec.add_development_dependency 'rspec', '~> 3.1.0'
|
23
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rencoder::Decoder do
|
4
|
+
include_context 'serialization_values'
|
5
|
+
|
6
|
+
subject { Rencoder::Coder.new }
|
7
|
+
|
8
|
+
describe '#decode' do
|
9
|
+
describe 'string' do
|
10
|
+
it 'decode string' do
|
11
|
+
expect(subject.decode(serialized_string)).to eq('Test')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'decode long string' do
|
15
|
+
expect(subject.decode(serialized_long_string)).to eq('a' * 100)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'integer' do
|
20
|
+
it 'decode positive small integer' do
|
21
|
+
expect(subject.decode(serialized_positive_integer)).to eq(10)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'decode negative small integer' do
|
25
|
+
expect(subject.decode(serialized_negative_integer)).to eq(-10)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'decode 8-bit integer' do
|
29
|
+
expect(subject.decode(serialized_8bit_integer)).to eq(100)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'decode 16-bit integer' do
|
33
|
+
expect(subject.decode(serialized_16bit_integer)).to eq(5000)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'decode 32-bit integer' do
|
37
|
+
expect(subject.decode(serialized_32bit_integer)).to eq(50000)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'decode 64-bit integer' do
|
41
|
+
expect(subject.decode(serialized_64bit_integer)).to eq(5000000000)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'decode big ascii integer' do
|
45
|
+
expect(subject.decode(serialized_big_integer)).to eq(50000000000000000000)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'float' do
|
50
|
+
it 'decode 32-bit float' do
|
51
|
+
expect(subject.decode(serialized_32bit_float)).to eq(100.0)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'decode 64-bit float' do
|
55
|
+
expect(subject.decode(serialized_float)).to eq(100.0001)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'boolean' do
|
60
|
+
it 'decode true boolean' do
|
61
|
+
expect(subject.decode(serialized_true)).to eq(true)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'decode false boolean' do
|
65
|
+
expect(subject.decode(serialized_false)).to eq(false)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'nil' do
|
70
|
+
it 'decode nil' do
|
71
|
+
expect(subject.decode(serialized_nil)).to eq(nil)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'array' do
|
76
|
+
it 'decode small array' do
|
77
|
+
expect(subject.decode(serialized_array)).to eq(['Test', 100, 100.0001, nil])
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'decode big array' do
|
81
|
+
expect(subject.decode(serialized_big_array)).to eq(100.times.to_a)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'hash' do
|
86
|
+
it 'decode small hash' do
|
87
|
+
expect(subject.decode(serialized_hash)).to eq({ 'test' => 123, 'hello' => 'world' })
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'decode big hash' do
|
91
|
+
expect(subject.decode(serialized_big_hash)).to eq(Hash[100.times.map { |i| [i, i.chr] }])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rencoder::Encoder do
|
4
|
+
include_context 'serialization_values'
|
5
|
+
|
6
|
+
subject { Rencoder::Coder.new }
|
7
|
+
|
8
|
+
describe '#encode' do
|
9
|
+
it 'encode string' do
|
10
|
+
expect(subject.encode('Test')).to eq(serialized_string)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'encode symbol' do
|
14
|
+
expect(subject.encode(:test)).to eq(serialized_symbol)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'encode integer' do
|
18
|
+
expect(subject.encode(100)).to eq(serialized_integer)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'encode float' do
|
22
|
+
expect(subject.encode(100.0001)).to eq(serialized_float)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'encode boolean' do
|
26
|
+
expect(subject.encode(false)).to eq(serialized_false)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'encode nil' do
|
30
|
+
expect(subject.encode(nil)).to eq(serialized_nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'encode array' do
|
34
|
+
expect(subject.encode(["Test", 100, 100.0001, nil])).to eq(serialized_array)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'encode hash' do
|
38
|
+
expect(subject.encode({ test: 123, hello: 'world' })).to eq(serialized_hash)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'raise exception for non-serializable object' do
|
42
|
+
expect { subject.encode(Object.new) }.to raise_error(Rencoder::Encoder::EncodingError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#encode_string' do
|
47
|
+
it 'encode small strings' do
|
48
|
+
expect(subject.encode_string('Test')).to eq(serialized_string)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'encode big strings' do
|
52
|
+
expect(subject.encode_string('a' * 100)).to eq(serialized_long_string)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#encode_integer' do
|
57
|
+
it 'encode small positive number' do
|
58
|
+
expect(subject.encode_integer(10)).to eq(serialized_positive_integer)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'encode small negative number' do
|
62
|
+
expect(subject.encode_integer(-10)).to eq(serialized_negative_integer)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'encode 8-bit integer' do
|
66
|
+
expect(subject.encode_integer(100)).to eq(serialized_8bit_integer)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'encode 16-bit integer' do
|
70
|
+
expect(subject.encode_integer(5000)).to eq(serialized_16bit_integer)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'encode 32-bit integer' do
|
74
|
+
expect(subject.encode_integer(50000)).to eq(serialized_32bit_integer)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'encode 64-bit integer' do
|
78
|
+
expect(subject.encode_integer(5000000000)).to eq(serialized_64bit_integer)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'encode big integer as ascii' do
|
82
|
+
expect(subject.encode_integer(50000000000000000000)).to eq(serialized_big_integer)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'raise error for super-big integers' do
|
86
|
+
expect do
|
87
|
+
subject.encode_integer(128.times.map { '9' }.join.to_i)
|
88
|
+
end.to raise_error(Rencoder::Encoder::EncodingError)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#encode_float' do
|
93
|
+
it 'encode 64-bit float' do
|
94
|
+
expect(subject.encode_float(100.0001)).to eq(serialized_float)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#encode_boolean' do
|
99
|
+
it 'encode true boolean' do
|
100
|
+
expect(subject.encode_boolean(true)).to eq(serialized_true)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'encode false boolean' do
|
104
|
+
expect(subject.encode_boolean(false)).to eq(serialized_false)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#encode_nil' do
|
109
|
+
it 'encode nil' do
|
110
|
+
expect(subject.encode_nil(nil)).to eq(serialized_nil)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#encode_array' do
|
115
|
+
it 'encode small-sized array' do
|
116
|
+
expect(subject.encode_array(["Test", 100, 100.0001, nil])).to eq(serialized_array)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'encode big-sized array' do
|
120
|
+
expect(subject.encode_array(100.times.to_a)).to eq(serialized_big_array)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#encode_hash' do
|
125
|
+
it 'encode small-sized hash' do
|
126
|
+
expect(subject.encode_hash({ test: 123, hello: 'world' })).to eq(serialized_hash)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'encode big-sized hash' do
|
130
|
+
hash = Hash[100.times.map { |i| [i, i.chr] }]
|
131
|
+
|
132
|
+
expect(subject.encode_hash(hash)).to eq(serialized_big_hash)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'rencoder'
|
5
|
+
|
6
|
+
require 'base64'
|
7
|
+
|
8
|
+
shared_context 'serialization_values' do
|
9
|
+
|
10
|
+
# Values encoded using original Python code
|
11
|
+
|
12
|
+
# Integers
|
13
|
+
# 100
|
14
|
+
let(:serialized_integer) { '>d' }
|
15
|
+
|
16
|
+
# 10
|
17
|
+
let(:serialized_positive_integer) { "\n" }
|
18
|
+
|
19
|
+
# -10
|
20
|
+
let(:serialized_negative_integer) { 'O' }
|
21
|
+
|
22
|
+
# 100
|
23
|
+
let(:serialized_8bit_integer) { serialized_integer }
|
24
|
+
|
25
|
+
# 5000
|
26
|
+
let(:serialized_16bit_integer) { Base64.decode64('PxOI') }
|
27
|
+
|
28
|
+
# 50000
|
29
|
+
let(:serialized_32bit_integer) { Base64.decode64('QAAAw1A=') }
|
30
|
+
|
31
|
+
# 5000000000
|
32
|
+
let(:serialized_64bit_integer) { Base64.decode64('QQAAAAEqBfIA') }
|
33
|
+
|
34
|
+
# 50000000000000000000
|
35
|
+
let(:serialized_big_integer) { "=50000000000000000000\x7f" }
|
36
|
+
|
37
|
+
# Strings
|
38
|
+
# 'Test'
|
39
|
+
let(:serialized_string) { Base64.decode64('hFRlc3Q=') }
|
40
|
+
|
41
|
+
# :test (symbol)
|
42
|
+
let(:serialized_symbol) { Base64.decode64('hHRlc3Q=') }
|
43
|
+
|
44
|
+
# 'a' * 100
|
45
|
+
let(:serialized_long_string) { '100:' + 'a' * 100 }
|
46
|
+
|
47
|
+
# Booleans
|
48
|
+
# true
|
49
|
+
let(:serialized_true) { 67.chr }
|
50
|
+
|
51
|
+
let(:serialized_false) { 68.chr }
|
52
|
+
|
53
|
+
# Floats
|
54
|
+
# 100.0001
|
55
|
+
let(:serialized_float) { Base64.decode64('LEBZAAGjbi6y') }
|
56
|
+
|
57
|
+
# 100.0001 (single-precision)
|
58
|
+
let(:serialized_32bit_float) { Base64.decode64('QkLIAAA=') }
|
59
|
+
|
60
|
+
# NULL
|
61
|
+
let(:serialized_nil) { 69.chr }
|
62
|
+
|
63
|
+
# Array
|
64
|
+
# ["Test", 100, 100.0001, nil]
|
65
|
+
let(:serialized_array) { Base64.decode64('xIRUZXN0PmQsQFkAAaNuLrJF') }
|
66
|
+
|
67
|
+
# big array (100.times.to_a)
|
68
|
+
let(:serialized_big_array) do
|
69
|
+
Base64.decode64('OwABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorPiw+LT4uPi8+MD4xPjI+Mz40PjU+Nj43Pjg+OT46Pjs+PD49Pj4+Pz5APkE+Qj5DPkQ+RT5GPkc+SD5JPko+Sz5MPk0+Tj5PPlA+UT5SPlM+VD5VPlY+Vz5YPlk+Wj5bPlw+XT5ePl8+YD5hPmI+Y38=')
|
70
|
+
end
|
71
|
+
|
72
|
+
# Hash
|
73
|
+
# { test: 123, hello: "world" }
|
74
|
+
let(:serialized_hash) { Base64.decode64('aIR0ZXN0PnuFaGVsbG+Fd29ybGQ=') }
|
75
|
+
|
76
|
+
# big hash (Hash[100.times.map { |i| [i, i.chr] }])
|
77
|
+
let(:serialized_big_hash) do
|
78
|
+
Base64.decode64('PACBAAGBAQKBAgOBAwSBBAWBBQaBBgeBBwiBCAmBCQqBCguBCwyBDA2BDQ6BDg+BDxCBEBGBERKBEhOBExSBFBWBFRaBFheBFxiBGBmBGRqBGhuBGxyBHB2BHR6BHh+BHyCBICGBISKBIiOBIySBJCWBJSaBJieBJyiBKCmBKSqBKiuBKz4sgSw+LYEtPi6BLj4vgS8+MIEwPjGBMT4ygTI+M4EzPjSBND41gTU+NoE2PjeBNz44gTg+OYE5PjqBOj47gTs+PIE8Pj2BPT4+gT4+P4E/PkCBQD5BgUE+QoFCPkOBQz5EgUQ+RYFFPkaBRj5HgUc+SIFIPkmBST5KgUo+S4FLPkyBTD5NgU0+ToFOPk+BTz5QgVA+UYFRPlKBUj5TgVM+VIFUPlWBVT5WgVY+V4FXPliBWD5ZgVk+WoFaPluBWz5cgVw+XYFdPl6BXj5fgV8+YIFgPmGBYT5igWI+Y4Fjfw==')
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rencoder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Igor Yamolov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.1.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.1.0
|
41
|
+
description: Rencoder is implementation of Rencoder encoding/decoding.
|
42
|
+
email:
|
43
|
+
- clouster@yandex.ru
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- ".ruby-version"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- lib/rencoder.rb
|
54
|
+
- lib/rencoder/coder.rb
|
55
|
+
- lib/rencoder/decoder.rb
|
56
|
+
- lib/rencoder/encoder.rb
|
57
|
+
- lib/rencoder/version.rb
|
58
|
+
- rencoder.gemspec
|
59
|
+
- spec/rencoder/decoder_spec.rb
|
60
|
+
- spec/rencoder/encoder_spec.rb
|
61
|
+
- spec/spec_helper.rb
|
62
|
+
homepage: https://github.com/t3hk0d3/rencoder
|
63
|
+
licenses:
|
64
|
+
- MIT
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.2.2
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: Rencoder is pure Ruby implementation of Rencoder serialization format encoding/decoding.
|
86
|
+
test_files:
|
87
|
+
- spec/rencoder/decoder_spec.rb
|
88
|
+
- spec/rencoder/encoder_spec.rb
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
has_rdoc:
|