rencode-ruby 1.0.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 +7 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +39 -0
- data/LICENSE.txt +21 -0
- data/README.md +22 -0
- data/app.gemspec +15 -0
- data/lib/rencode.rb +105 -0
- data/lib/rencode/constants.rb +47 -0
- data/lib/rencode/decoder.rb +162 -0
- data/lib/rencode/encoder.rb +57 -0
- data/test/rencode_test.rb +154 -0
- data/test/test_helper.rb +10 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8e4c4b66f1318baa3a826c3b91341e007b045d9d
|
4
|
+
data.tar.gz: 41eeecc0d02e829840468a7804c9295a8fa59454
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 991793a070c073d1264d86022262743b6ed3e791fb7e4760f32d5f9052a3696aabf8d4c92c62188ff7a3a75c4cbe79cae74c1aa0fc1f14b21fb4f67c251ef25c
|
7
|
+
data.tar.gz: baa80ff4cf7dd37f5a5bd70189968fa5fae6dfcf85cea391e34eb3541b7e76e2b32c1c02999505060553077778eb0a3a784776dda5cbc7932ca0347d4cfd01e8
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rencode (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
activesupport (4.0.5)
|
10
|
+
i18n (~> 0.6, >= 0.6.9)
|
11
|
+
minitest (~> 4.2)
|
12
|
+
multi_json (~> 1.3)
|
13
|
+
thread_safe (~> 0.1)
|
14
|
+
tzinfo (~> 0.3.37)
|
15
|
+
ansi (1.4.3)
|
16
|
+
i18n (0.6.9)
|
17
|
+
minitest (4.7.5)
|
18
|
+
multi_json (1.10.0)
|
19
|
+
rake (10.3.1)
|
20
|
+
shoulda (3.5.0)
|
21
|
+
shoulda-context (~> 1.0, >= 1.0.1)
|
22
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
23
|
+
shoulda-context (1.2.1)
|
24
|
+
shoulda-matchers (2.6.1)
|
25
|
+
activesupport (>= 3.0.0)
|
26
|
+
thread_safe (0.3.3)
|
27
|
+
turn (0.9.7)
|
28
|
+
ansi
|
29
|
+
minitest (~> 4)
|
30
|
+
tzinfo (0.3.39)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
rake
|
37
|
+
rencode!
|
38
|
+
shoulda
|
39
|
+
turn
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
"Copyright (C) 2014 Mikael Wikman
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation files
|
5
|
+
(the "Software"), to deal in the Software without restriction,
|
6
|
+
including without limitation the rights to use, copy, modify, merge,
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
and to permit persons to whom the Software is furnished to do so,
|
9
|
+
subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
The Software is provided "AS IS", without warranty of any kind,
|
15
|
+
express or implied, including but not limited to the warranties of
|
16
|
+
merchantability, fitness for a particular purpose and
|
17
|
+
noninfringement. In no event shall the authors or copyright holders
|
18
|
+
be liable for any claim, damages or other liability, whether in an
|
19
|
+
action of contract, tort or otherwise, arising from, out of or in
|
20
|
+
connection with the Software or the use or other dealings in the
|
21
|
+
Software."
|
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# REncode -- Web safe object serialization
|
2
|
+
|
3
|
+
REncode was based on the BitTorrent BEncode module by Petru Paler, et al.
|
4
|
+
Licensed by Bram Cohen, 2001-2002, modified by Connelly Barnes 2006-2007.
|
5
|
+
|
6
|
+
This module for Ruby was written by Mikael Wikman, 2014, and aims to be
|
7
|
+
complient with the Connelly Barnes version 1.0.2
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
This implementation creates byte arrays, and expects byte arrays as argument. If you have a string, you could simply use str.bytes, or if you want to convert to string do arr.pack("C\*")
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
require 'rencode'
|
15
|
+
|
16
|
+
REncode.dump({ 'awesome' => 'It works!' })
|
17
|
+
# => [CHR_DICT, '7'.ord, ':'.ord, 'awesome'.bytes, '9'.ord, ':'.ord, 'It works!'.bytes, CHR_TERM].flatten
|
18
|
+
|
19
|
+
REncode.parse([CHR_INT, '123'.bytes, CHR_TERM].flatten)
|
20
|
+
# => 123
|
21
|
+
|
22
|
+
```
|
data/app.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.authors = ['Mikael Wikman']
|
5
|
+
gem.email = ['mikael@wikman.me']
|
6
|
+
gem.description = %q{This implementation aims to be complient with the Connelly Barnes implementation of rencode, version 1.0.2}
|
7
|
+
gem.summary = %q{Web safe object serialization}
|
8
|
+
gem.files = `git ls-files`.split($\)
|
9
|
+
gem.homepage = 'https://github.com/mikaelwikman/rencode'
|
10
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
gem.test_files = gem.files.grep(%r{^(test|features)/})
|
12
|
+
gem.name = "rencode-ruby"
|
13
|
+
gem.require_paths = ["lib"]
|
14
|
+
gem.version = '1.0.0'
|
15
|
+
end
|
data/lib/rencode.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# REncode -- Web safe object serialization
|
2
|
+
#
|
3
|
+
# REncode was based on the BitTorrent BEncode module by Petru Paler, et al.
|
4
|
+
# Licensed by Bram Cohen, 2001-2002, modified by Connelly Barnes 2006-2007.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# This module for Ruby was written by Mikael Wikman, 2014, and aims to be
|
8
|
+
# complient with the Connelly Barnes version 1.0.2
|
9
|
+
#
|
10
|
+
# "Copyright (C) 2014 Mikael Wikman
|
11
|
+
#
|
12
|
+
# Permission is hereby granted, free of charge, to any person
|
13
|
+
# obtaining a copy of this software and associated documentation files
|
14
|
+
# (the "Software"), to deal in the Software without restriction,
|
15
|
+
# including without limitation the rights to use, copy, modify, merge,
|
16
|
+
# publish, distribute, sublicense, and/or sell copies of the Software,
|
17
|
+
# and to permit persons to whom the Software is furnished to do so,
|
18
|
+
# subject to the following conditions:
|
19
|
+
#
|
20
|
+
# The above copyright notice and this permission notice shall be
|
21
|
+
# included in all copies or substantial portions of the Software.
|
22
|
+
#
|
23
|
+
# The Software is provided "AS IS", without warranty of any kind,
|
24
|
+
# express or implied, including but not limited to the warranties of
|
25
|
+
# merchantability, fitness for a particular purpose and
|
26
|
+
# noninfringement. In no event shall the authors or copyright holders
|
27
|
+
# be liable for any claim, damages or other liability, whether in an
|
28
|
+
# action of contract, tort or otherwise, arising from, out of or in
|
29
|
+
# connection with the Software or the use or other dealings in the
|
30
|
+
# Software."
|
31
|
+
#
|
32
|
+
|
33
|
+
require 'rencode/decoder'
|
34
|
+
require 'rencode/encoder'
|
35
|
+
|
36
|
+
class REncode
|
37
|
+
|
38
|
+
def self.parse data
|
39
|
+
REncode::Decoder.new(data).parse
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.dump obj
|
43
|
+
REncode::Encoder.new.dump(obj)
|
44
|
+
end
|
45
|
+
|
46
|
+
# def self.test
|
47
|
+
# f1 = 25.5
|
48
|
+
# f2 = 29.3
|
49
|
+
# f3 = -0.6
|
50
|
+
# L = [
|
51
|
+
# [
|
52
|
+
# ['a',10**20],
|
53
|
+
# [-100000..100000],
|
54
|
+
# 'b'*31,
|
55
|
+
# 'b'*62,
|
56
|
+
# 'b'*64,
|
57
|
+
# 2**30,
|
58
|
+
# 2**33,
|
59
|
+
# 2**62,
|
60
|
+
# 2**64,
|
61
|
+
# 2**30,
|
62
|
+
# 2**33,
|
63
|
+
# 2**62,
|
64
|
+
# 2**64,
|
65
|
+
# false,
|
66
|
+
# false,
|
67
|
+
# true,
|
68
|
+
# -1,
|
69
|
+
# 2,
|
70
|
+
# 0
|
71
|
+
# ],
|
72
|
+
# ]
|
73
|
+
# test1 = {
|
74
|
+
# 'a':15,
|
75
|
+
# 'bb':25.5,
|
76
|
+
# 'ccc':29.3,
|
77
|
+
# '':[ -0.6, [], false, true,'']
|
78
|
+
# },
|
79
|
+
# raise "Mismatch!" unless parse(dump(test1)) == test1
|
80
|
+
# assert loads(dumps(L)) == L
|
81
|
+
# d = dict(zip(range(-100000,100000),range(-100000,100000)))
|
82
|
+
# d.update({'a':20, 20:40, 40:41, f1:f2, f2:f3, f3:False, False:True, True:False})
|
83
|
+
# L = (d, {}, {5:6}, {7:7,True:8}, {9:10, 22:39, 49:50, 44: ''})
|
84
|
+
# assert loads(dumps(L)) == L
|
85
|
+
# L = ('', 'a'*10, 'a'*100, 'a'*1000, 'a'*10000, 'a'*100000, 'a'*1000000, 'a'*10000000)
|
86
|
+
# assert loads(dumps(L)) == L
|
87
|
+
# L = tuple([dict(zip(range(n),range(n))) for n in range(100)]) + ('b',)
|
88
|
+
# assert loads(dumps(L)) == L
|
89
|
+
# L = tuple([dict(zip(range(n),range(-n,0))) for n in range(100)]) + ('b',)
|
90
|
+
# assert loads(dumps(L)) == L
|
91
|
+
# L = tuple([tuple(range(n)) for n in range(100)]) + ('b',)
|
92
|
+
# assert loads(dumps(L)) == L
|
93
|
+
# L = tuple(['a'*n for n in range(1000)]) + ('b',)
|
94
|
+
# assert loads(dumps(L)) == L
|
95
|
+
# L = tuple(['a'*n for n in range(1000)]) + (None,True,None)
|
96
|
+
# assert loads(dumps(L)) == L
|
97
|
+
# assert loads(dumps(None)) == None
|
98
|
+
# assert loads(dumps({None:None})) == {None:None}
|
99
|
+
# assert 1e-10<abs(loads(dumps(1.1))-1.1)<1e-6
|
100
|
+
# assert 1e-10<abs(loads(dumps(1.1,32))-1.1)<1e-6
|
101
|
+
# assert abs(loads(dumps(1.1,64))-1.1)<1e-12
|
102
|
+
# assert loads(dumps(u"Hello World!!"))
|
103
|
+
# end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module REncodeConstants
|
2
|
+
# Default number of bits for serialized floats, either 32 or 64 (also a parameter for dumps()).
|
3
|
+
DEFAULT_FLOAT_BITS = 32
|
4
|
+
|
5
|
+
# Maximum length of integer when written as base 10 string.
|
6
|
+
# Ruby has no max integer length
|
7
|
+
MAX_INT_LENGTH = 64
|
8
|
+
|
9
|
+
# The bencode 'typecodes' such as i, d, etc have been extended and
|
10
|
+
# relocated on the base-256 character set.
|
11
|
+
CHR_LIST = 59
|
12
|
+
CHR_DICT = 60
|
13
|
+
CHR_INT = 61
|
14
|
+
CHR_INT1 = 62
|
15
|
+
CHR_INT2 = 63
|
16
|
+
CHR_INT4 = 64
|
17
|
+
CHR_INT8 = 65
|
18
|
+
CHR_FLOAT32 = 66
|
19
|
+
CHR_FLOAT64 = 44
|
20
|
+
CHR_TRUE = 67
|
21
|
+
CHR_FALSE = 68
|
22
|
+
CHR_NONE = 69
|
23
|
+
CHR_TERM = 127
|
24
|
+
|
25
|
+
# Dictionaries with length embedded in typecode.
|
26
|
+
DICT_FIXED_START = 102
|
27
|
+
DICT_FIXED_COUNT = 25
|
28
|
+
|
29
|
+
# Positive integers with value embedded in typecode.
|
30
|
+
INT_POS_FIXED_START = 0
|
31
|
+
INT_POS_FIXED_COUNT = 44
|
32
|
+
|
33
|
+
# Negative integers with value embedded in typecode.
|
34
|
+
INT_NEG_FIXED_START = 70
|
35
|
+
INT_NEG_FIXED_COUNT = 32
|
36
|
+
|
37
|
+
# Strings with length embedded in typecode.
|
38
|
+
STR_FIXED_START = 128
|
39
|
+
STR_FIXED_COUNT = 64
|
40
|
+
|
41
|
+
# Lists with length embedded in typecode.
|
42
|
+
LIST_FIXED_START = STR_FIXED_START+STR_FIXED_COUNT
|
43
|
+
LIST_FIXED_COUNT = 64
|
44
|
+
|
45
|
+
VERSION = '1.0.2'
|
46
|
+
ALL = ['dumps', 'loads']
|
47
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'rencode/constants'
|
2
|
+
|
3
|
+
## a little monkey-patching to add enumerator next-N, where N is a number
|
4
|
+
class Enumerator
|
5
|
+
def nextn num
|
6
|
+
[].tap {|a| num.times {|i| a << send('next') } }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class REncode
|
11
|
+
|
12
|
+
class Decoder
|
13
|
+
include REncodeConstants
|
14
|
+
|
15
|
+
##
|
16
|
+
# Prepare the mapping table
|
17
|
+
#
|
18
|
+
|
19
|
+
|
20
|
+
@@mapping_table = Array.new(255)
|
21
|
+
|
22
|
+
(48..57).each do |i| # '0' to '9'
|
23
|
+
@@mapping_table[i] = Proc.new do
|
24
|
+
length_str = i.chr
|
25
|
+
while (chr=@data.next) != ':'.ord
|
26
|
+
length_str << chr.chr
|
27
|
+
end
|
28
|
+
|
29
|
+
if length_str[0] == '0' and length_str.length > 1
|
30
|
+
raise "Non-zero string length supplied with initial zero"
|
31
|
+
end
|
32
|
+
|
33
|
+
n = length_str.to_i
|
34
|
+
data = @data.nextn(n)
|
35
|
+
data.pack('C*')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
STR_FIXED_COUNT.times do |i|
|
40
|
+
@@mapping_table[STR_FIXED_START+i] = Proc.new { @data.nextn(i).pack('C*') } # only ascii for now
|
41
|
+
end
|
42
|
+
|
43
|
+
LIST_FIXED_COUNT.times do |i|
|
44
|
+
@@mapping_table[LIST_FIXED_START+i] = Proc.new { Array.new(i) { _parse } }
|
45
|
+
end
|
46
|
+
|
47
|
+
INT_POS_FIXED_COUNT.times do |i|
|
48
|
+
@@mapping_table[INT_POS_FIXED_START+i] = Proc.new { i }
|
49
|
+
end
|
50
|
+
|
51
|
+
INT_NEG_FIXED_COUNT.times do |i|
|
52
|
+
@@mapping_table[INT_NEG_FIXED_START+i] = Proc.new { -1-i }
|
53
|
+
end
|
54
|
+
|
55
|
+
DICT_FIXED_COUNT.times do |i|
|
56
|
+
@@mapping_table[DICT_FIXED_START+i] = Proc.new { {}.tap {|r| i.times {|j| r[_parse] = _parse } } }
|
57
|
+
end
|
58
|
+
|
59
|
+
@@mapping_table[CHR_LIST] = Proc.new do
|
60
|
+
r = []
|
61
|
+
|
62
|
+
while @data.peek != CHR_TERM
|
63
|
+
r << _parse
|
64
|
+
end
|
65
|
+
|
66
|
+
@data.next
|
67
|
+
r
|
68
|
+
end
|
69
|
+
|
70
|
+
@@mapping_table[CHR_DICT] = Proc.new do
|
71
|
+
r = {}
|
72
|
+
|
73
|
+
while @data.peek != CHR_TERM
|
74
|
+
r[_parse] = _parse
|
75
|
+
end
|
76
|
+
|
77
|
+
@data.next
|
78
|
+
r
|
79
|
+
end
|
80
|
+
|
81
|
+
@@mapping_table[CHR_INT] = Proc.new do
|
82
|
+
num_str = ""
|
83
|
+
|
84
|
+
while (chr=@data.next) != CHR_TERM
|
85
|
+
num_str << chr.chr
|
86
|
+
end
|
87
|
+
|
88
|
+
n = num_str.to_i
|
89
|
+
|
90
|
+
if num_str[0] == '-'
|
91
|
+
if num_str[1] == '0'
|
92
|
+
raise "a zero after minus?!"
|
93
|
+
end
|
94
|
+
elsif num_str[0] == '0' and num_str.length > 1
|
95
|
+
raise "A non-zero number started by zero"
|
96
|
+
end
|
97
|
+
|
98
|
+
n
|
99
|
+
end
|
100
|
+
|
101
|
+
@@mapping_table[CHR_INT1] = Proc.new do
|
102
|
+
# return (struct.unpack('!b', x[f:f+1])[0], f+1)
|
103
|
+
@data.next # python "!b" means 8bit integer, big endian, but what does endianess matter since it's a single byte
|
104
|
+
end
|
105
|
+
|
106
|
+
@@mapping_table[CHR_INT2] = Proc.new do
|
107
|
+
# return (struct.unpack('!h', x[f:f+2])[0], f+2)
|
108
|
+
@data.nextn(2).pack('C*').unpack('s>')[0]
|
109
|
+
end
|
110
|
+
|
111
|
+
@@mapping_table[CHR_INT4] = Proc.new do
|
112
|
+
# return (struct.unpack('!l', x[f:f+4])[0], f+4)
|
113
|
+
@data.nextn(4).pack('C*').unpack('l>')[0]
|
114
|
+
end
|
115
|
+
|
116
|
+
@@mapping_table[CHR_INT8] = Proc.new do
|
117
|
+
#return (struct.unpack('!q', x[f:f+8])[0], f+8)
|
118
|
+
@data.nextn(8).pack('C*').unpack('q>')[0]
|
119
|
+
end
|
120
|
+
|
121
|
+
@@mapping_table[CHR_FLOAT32] = Proc.new do
|
122
|
+
#n = struct.unpack('!f', x[f:f+4])[0]
|
123
|
+
@data.nextn(4).pack('C*').unpack('g')[0]
|
124
|
+
end
|
125
|
+
|
126
|
+
@@mapping_table[CHR_FLOAT64] = Proc.new do
|
127
|
+
#n = struct.unpack('!d', x[f:f+8])[0]
|
128
|
+
@data.nextn(8).pack('C*').unpack('G')[0]
|
129
|
+
end
|
130
|
+
|
131
|
+
@@mapping_table[CHR_TRUE] = Proc.new { true}
|
132
|
+
@@mapping_table[CHR_FALSE] = Proc.new { false}
|
133
|
+
@@mapping_table[CHR_NONE] = Proc.new { nil}
|
134
|
+
|
135
|
+
#
|
136
|
+
# End mapping table
|
137
|
+
##
|
138
|
+
|
139
|
+
def initialize data, decode_utf8=false
|
140
|
+
if data.kind_of?(Array)
|
141
|
+
@data = data.each
|
142
|
+
elsif data.kind_of?(String)
|
143
|
+
@data = data.each_byte
|
144
|
+
else
|
145
|
+
raise "Bad input argument for data #{data.class}"
|
146
|
+
end
|
147
|
+
@decode_utf8 = decode_utf8
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse
|
151
|
+
_parse
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def _parse
|
157
|
+
c = @data.next.ord
|
158
|
+
instance_eval &@@mapping_table[c]
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rencode/constants'
|
2
|
+
|
3
|
+
class REncode
|
4
|
+
|
5
|
+
class Encoder
|
6
|
+
include REncodeConstants
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@out = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def dump(obj)
|
13
|
+
@out = []
|
14
|
+
d(obj)
|
15
|
+
@out
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# NOTE: Performance-wise, this is a rather lousy implementation. It uses none of the
|
21
|
+
# size-optimizations offered by the protocol. Also, doesn't use variable-length numbers,
|
22
|
+
# so we're limited to maximum 64bit.
|
23
|
+
|
24
|
+
def d o
|
25
|
+
case o.class.name
|
26
|
+
when 'Hash'
|
27
|
+
@out << CHR_DICT
|
28
|
+
o.each { |k,v| d(k) ; d(v) }
|
29
|
+
@out << CHR_TERM
|
30
|
+
when 'Fixnum'
|
31
|
+
raise "implementation doesn't support such large numbers" if o > 2**31 || o < -1*2**31
|
32
|
+
@out << CHR_INT8
|
33
|
+
@out.concat [o].pack('q>').bytes
|
34
|
+
when 'Float'
|
35
|
+
@out << CHR_FLOAT64
|
36
|
+
@out.concat [o].pack('G').bytes
|
37
|
+
when 'TrueClass'
|
38
|
+
@out << CHR_TRUE
|
39
|
+
when 'FalseClass'
|
40
|
+
@out << CHR_FALSE
|
41
|
+
when 'NilClass'
|
42
|
+
@out << CHR_NONE
|
43
|
+
when 'String'
|
44
|
+
bytes = o.bytes
|
45
|
+
@out.concat bytes.length.to_s.bytes
|
46
|
+
@out << ':'.ord
|
47
|
+
@out.concat bytes
|
48
|
+
when 'Array'
|
49
|
+
@out << CHR_LIST
|
50
|
+
o.each { |oo| d(oo) }
|
51
|
+
@out << CHR_TERM
|
52
|
+
else
|
53
|
+
raise "Cannot serialize type #{o.class}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'rencode'
|
3
|
+
require 'rencode/constants'
|
4
|
+
|
5
|
+
class REncodeTest < TestCase
|
6
|
+
include REncodeConstants
|
7
|
+
|
8
|
+
context 'rencode' do
|
9
|
+
|
10
|
+
setup do
|
11
|
+
@it = REncode
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'encode' do
|
15
|
+
should 'handle dictionaries' do
|
16
|
+
source = { 'a' => 'b' }
|
17
|
+
expected = [CHR_DICT, '1'.ord, ':'.ord, 'a'.ord, '1'.ord, ':'.ord, 'b'.ord, CHR_TERM]
|
18
|
+
|
19
|
+
assert_equal expected, @it.dump(source)
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'handle numbers' do
|
23
|
+
assert_equal [CHR_INT8, 0, 0, 0, 0, 0, 0, 0, 0], @it.dump(0)
|
24
|
+
assert_equal [CHR_INT8, 0, 0, 0, 0, 0, 0, 0, 1], @it.dump(1)
|
25
|
+
assert_equal [CHR_INT8, 255, 255, 255, 255, 255, 255, 255, 255], @it.dump(-1)
|
26
|
+
assert_equal [CHR_INT8, 0,0,0,0,0,0,1,0], @it.dump(256)
|
27
|
+
@it.dump 2**31
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'fail on too big number' do
|
31
|
+
assert_raises RuntimeError do
|
32
|
+
@it.dump 2**32
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
should 'handle floats' do
|
37
|
+
assert_equal [CHR_FLOAT64, 63,240,0,0,0,0,0,0], @it.dump(1.0)
|
38
|
+
end
|
39
|
+
|
40
|
+
should 'handle boolean' do
|
41
|
+
assert_equal [CHR_TRUE], @it.dump(true)
|
42
|
+
assert_equal [CHR_FALSE], @it.dump(false)
|
43
|
+
end
|
44
|
+
|
45
|
+
should 'handle nil' do
|
46
|
+
assert_equal [CHR_NONE], @it.dump(nil)
|
47
|
+
end
|
48
|
+
|
49
|
+
should 'handle string' do
|
50
|
+
assert_equal ['2'.ord, ':'.ord, 'm'.ord, 'e'.ord], @it.dump('me')
|
51
|
+
end
|
52
|
+
|
53
|
+
should 'handle array' do
|
54
|
+
assert_equal [CHR_LIST, CHR_TRUE, CHR_TRUE, CHR_TERM], @it.dump([true,true])
|
55
|
+
end
|
56
|
+
|
57
|
+
should 'fail on unrecognized type' do
|
58
|
+
assert_raises RuntimeError do
|
59
|
+
@it.dump(Object.new)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'decode' do
|
65
|
+
should 'handle dictionaries' do
|
66
|
+
source = [CHR_DICT, '1'.ord, ':'.ord, 'a'.ord, '1'.ord, ':'.ord, 'b'.ord, CHR_TERM]
|
67
|
+
expected = { 'a' => 'b' }
|
68
|
+
|
69
|
+
assert_equal expected, @it.parse(source)
|
70
|
+
end
|
71
|
+
|
72
|
+
should 'handle fixed size dictionaries' do
|
73
|
+
source = [DICT_FIXED_START+1, '1'.ord, ':'.ord, 'a'.ord, '1'.ord, ':'.ord, 'b'.ord]
|
74
|
+
expected = { 'a' => 'b' }
|
75
|
+
assert_equal expected, @it.parse(source)
|
76
|
+
|
77
|
+
assert_equal({}, @it.parse([DICT_FIXED_START+0]))
|
78
|
+
end
|
79
|
+
|
80
|
+
should 'handle int' do
|
81
|
+
assert_equal 432678, @it.parse([CHR_INT, '432678'.bytes, CHR_TERM].flatten)
|
82
|
+
end
|
83
|
+
|
84
|
+
should 'handle int1' do
|
85
|
+
assert_equal 45, @it.parse([CHR_INT1, 45])
|
86
|
+
end
|
87
|
+
|
88
|
+
should 'handle int2' do
|
89
|
+
assert_equal 272, @it.parse([CHR_INT2, 1, 16])
|
90
|
+
end
|
91
|
+
|
92
|
+
should 'handle int4' do
|
93
|
+
assert_equal 272, @it.parse([CHR_INT4, 0, 0, 1, 16])
|
94
|
+
end
|
95
|
+
|
96
|
+
should 'handle int8' do
|
97
|
+
assert_equal 0, @it.parse([CHR_INT8, 0, 0, 0, 0, 0, 0, 0, 0])
|
98
|
+
assert_equal 1, @it.parse([CHR_INT8, 0, 0, 0, 0, 0, 0, 0, 1])
|
99
|
+
assert_equal -1, @it.parse([CHR_INT8, 255, 255, 255, 255, 255, 255, 255, 255])
|
100
|
+
assert_equal 256, @it.parse([CHR_INT8, 0,0,0,0,0,0,1,0])
|
101
|
+
end
|
102
|
+
|
103
|
+
should 'handle fixed address int' do
|
104
|
+
assert_equal 0, @it.parse([INT_POS_FIXED_START+0])
|
105
|
+
assert_equal 5, @it.parse([INT_POS_FIXED_START+5])
|
106
|
+
assert_equal INT_POS_FIXED_COUNT-1, @it.parse([INT_POS_FIXED_START+INT_POS_FIXED_COUNT-1])
|
107
|
+
end
|
108
|
+
|
109
|
+
should 'handle fixed address negative int' do
|
110
|
+
assert_equal -1, @it.parse([INT_NEG_FIXED_START+0])
|
111
|
+
assert_equal -7, @it.parse([INT_NEG_FIXED_START+6])
|
112
|
+
assert_equal -1-INT_NEG_FIXED_COUNT+1, @it.parse([INT_NEG_FIXED_START+INT_NEG_FIXED_COUNT-1])
|
113
|
+
end
|
114
|
+
|
115
|
+
should 'handle floats' do
|
116
|
+
assert_equal 1.0, @it.parse([CHR_FLOAT64, 63,240,0,0,0,0,0,0])
|
117
|
+
assert_equal 1.0, @it.parse([CHR_FLOAT32, 63,128,0,0])
|
118
|
+
end
|
119
|
+
|
120
|
+
should 'handle boolean' do
|
121
|
+
assert_equal true, @it.parse([CHR_TRUE])
|
122
|
+
assert_equal false, @it.parse([CHR_FALSE])
|
123
|
+
end
|
124
|
+
|
125
|
+
should 'handle nil' do
|
126
|
+
assert_equal nil, @it.parse([CHR_NONE])
|
127
|
+
end
|
128
|
+
|
129
|
+
should 'handle string' do
|
130
|
+
assert_equal 'me', @it.parse(['2'.ord, ':'.ord, 'm'.ord, 'e'.ord])
|
131
|
+
end
|
132
|
+
|
133
|
+
should 'handle fixed-size strings' do
|
134
|
+
assert_equal '', @it.parse([STR_FIXED_START+0])
|
135
|
+
assert_equal 'a', @it.parse([STR_FIXED_START+1, 'a'.ord])
|
136
|
+
|
137
|
+
str= 'a'*(STR_FIXED_COUNT-1)
|
138
|
+
assert_equal str, @it.parse([STR_FIXED_START+STR_FIXED_COUNT-1].concat(str.bytes))
|
139
|
+
end
|
140
|
+
|
141
|
+
should 'handle array' do
|
142
|
+
assert_equal [true, true], @it.parse([CHR_LIST, CHR_TRUE, CHR_TRUE, CHR_TERM])
|
143
|
+
end
|
144
|
+
|
145
|
+
should 'handle fixed-size arrays' do
|
146
|
+
assert_equal [], @it.parse([LIST_FIXED_START+0])
|
147
|
+
assert_equal [true], @it.parse([LIST_FIXED_START+1, CHR_TRUE])
|
148
|
+
|
149
|
+
arr = Array.new(LIST_FIXED_COUNT-1, true)
|
150
|
+
assert_equal arr, @it.parse([LIST_FIXED_START+LIST_FIXED_COUNT-1].concat(Array.new(LIST_FIXED_COUNT-1, CHR_TRUE)))
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rencode-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mikael Wikman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: This implementation aims to be complient with the Connelly Barnes implementation
|
14
|
+
of rencode, version 1.0.2
|
15
|
+
email:
|
16
|
+
- mikael@wikman.me
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- Gemfile
|
22
|
+
- Gemfile.lock
|
23
|
+
- LICENSE.txt
|
24
|
+
- README.md
|
25
|
+
- app.gemspec
|
26
|
+
- lib/rencode.rb
|
27
|
+
- lib/rencode/constants.rb
|
28
|
+
- lib/rencode/decoder.rb
|
29
|
+
- lib/rencode/encoder.rb
|
30
|
+
- test/rencode_test.rb
|
31
|
+
- test/test_helper.rb
|
32
|
+
homepage: https://github.com/mikaelwikman/rencode
|
33
|
+
licenses: []
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.2.2
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Web safe object serialization
|
55
|
+
test_files:
|
56
|
+
- test/rencode_test.rb
|
57
|
+
- test/test_helper.rb
|