rencoder 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: