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