rencoder 0.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.
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
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.gem
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rencoder.gemspec
4
+ gemspec
5
+
6
+ gem 'rencode-ruby'
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,12 @@
1
+ module Rencoder
2
+ class Coder
3
+ attr_reader :options
4
+
5
+ def initialize(options = {})
6
+ @options = options
7
+ end
8
+
9
+ include Rencoder::Encoder
10
+ include Rencoder::Decoder
11
+ end
12
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Rencoder
2
+ VERSION = '0.1.0'
3
+ 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
@@ -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: