bencode_ext 0.1.2 → 0.2.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.
- data/Rakefile +0 -1
- data/VERSION +1 -1
- data/bencode_ext.gemspec +3 -3
- data/ext/bencode_ext/bencode.c +145 -80
- data/ext/bencode_ext/bencode.h +3 -1
- data/ext/bencode_ext/extconf.rb +2 -0
- data/test/test_bencode_ext.rb +13 -0
- metadata +3 -4
data/Rakefile
CHANGED
@@ -14,7 +14,6 @@ Jeweler::Tasks.new do |gem|
|
|
14
14
|
gem.required_ruby_version = '~>1.9.2'
|
15
15
|
gem.add_dependency 'rake-compiler', '~>0.7.5'
|
16
16
|
gem.add_development_dependency "jeweler", "~> 1.5.2"
|
17
|
-
gem.extensions << "ext/bencode_ext/extconf.rb"
|
18
17
|
end
|
19
18
|
Jeweler::RubygemsDotOrgTasks.new
|
20
19
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/bencode_ext.gemspec
CHANGED
@@ -5,14 +5,14 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bencode_ext}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["naquad"]
|
12
|
-
s.date = %q{2011-01-
|
12
|
+
s.date = %q{2011-01-14}
|
13
13
|
s.description = %q{BEncodeExt is implementation of Bencode reader/writer (BitTorent encoding) in C.}
|
14
14
|
s.email = %q{naquad@gmail.com}
|
15
|
-
s.extensions = ["ext/bencode_ext/extconf.rb"
|
15
|
+
s.extensions = ["ext/bencode_ext/extconf.rb"]
|
16
16
|
s.extra_rdoc_files = [
|
17
17
|
"LICENSE.txt",
|
18
18
|
"README.rdoc"
|
data/ext/bencode_ext/bencode.c
CHANGED
@@ -23,88 +23,28 @@
|
|
23
23
|
|
24
24
|
#include "bencode.h"
|
25
25
|
|
26
|
-
#define NEXT_CHAR ++*str; --*len;
|
27
|
-
#define END_CHECK_SKIP(t) {if(!*len)\
|
28
|
-
rb_raise(DecodeError, "Unpexpected " #t " end!");\
|
29
|
-
if(**str != 'e')\
|
30
|
-
rb_raise(DecodeError, "Mailformed " #t " at %d byte: %c", rlen - *len, **str);\
|
31
|
-
NEXT_CHAR;}
|
32
|
-
|
33
26
|
static long parse_num(char** str, long* len){
|
34
27
|
long t = 1, ret = 0;
|
35
28
|
|
36
29
|
if(**str == '-'){
|
37
30
|
t = -1;
|
38
|
-
|
31
|
+
++*str;
|
32
|
+
--*len;
|
39
33
|
}
|
40
34
|
|
41
35
|
while(*len && **str >= '0' && **str <= '9'){
|
42
36
|
ret = ret * 10 + (**str - '0');
|
43
|
-
|
37
|
+
++*str;
|
38
|
+
--*len;
|
44
39
|
}
|
45
40
|
|
46
41
|
return ret * t;
|
47
42
|
}
|
48
43
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
switch(**str){
|
54
|
-
case 'l':{
|
55
|
-
VALUE ret = rb_ary_new();
|
56
|
-
NEXT_CHAR;
|
57
|
-
|
58
|
-
while(**str != 'e' && *len)
|
59
|
-
rb_ary_push(ret, _decode(str, len, rlen));
|
60
|
-
|
61
|
-
END_CHECK_SKIP(list);
|
62
|
-
return ret;
|
63
|
-
}
|
64
|
-
case 'd':{
|
65
|
-
VALUE ret = rb_hash_new();
|
66
|
-
NEXT_CHAR;
|
67
|
-
|
68
|
-
while(**str != 'e' && *len){
|
69
|
-
long t = *len;
|
70
|
-
VALUE k = _decode(str, len, rlen);
|
71
|
-
|
72
|
-
if(TYPE(k) != T_STRING)
|
73
|
-
rb_raise(DecodeError, "Dictionary key is not a string at %d!", rlen - t);
|
74
|
-
|
75
|
-
rb_hash_aset(ret, k, _decode(str, len, rlen));
|
76
|
-
}
|
77
|
-
|
78
|
-
END_CHECK_SKIP(dictionary);
|
79
|
-
return ret;
|
80
|
-
}
|
81
|
-
case 'i':{
|
82
|
-
long t;
|
83
|
-
NEXT_CHAR;
|
84
|
-
t = parse_num(str, len);
|
85
|
-
END_CHECK_SKIP(integer);
|
86
|
-
return LONG2FIX(t);
|
87
|
-
}
|
88
|
-
case '0'...'9':{
|
89
|
-
VALUE ret;
|
90
|
-
long slen = parse_num(str, len);
|
91
|
-
|
92
|
-
if(slen < 0 || (*len && **str != ':'))
|
93
|
-
rb_raise(DecodeError, "Invalid string length specification at %d: %c", rlen - *len, **str);
|
94
|
-
|
95
|
-
if(!*len || *len < slen + 1)
|
96
|
-
rb_raise(DecodeError, "Unexpected string end!");
|
97
|
-
|
98
|
-
ret = rb_str_new(++*str, slen);
|
99
|
-
*str += slen;
|
100
|
-
*len -= slen + 1;
|
101
|
-
return ret;
|
102
|
-
}
|
103
|
-
default:
|
104
|
-
rb_raise(DecodeError, "Unknown element type at %d byte: %c", rlen - *len, **str);
|
105
|
-
return Qnil;
|
106
|
-
}
|
107
|
-
}
|
44
|
+
#define NEXT_CHAR ++str; --len;
|
45
|
+
#define ELEMENT_SCALAR 0
|
46
|
+
#define ELEMENT_STRUCT 1
|
47
|
+
#define ELEMENT_END 2
|
108
48
|
|
109
49
|
/*
|
110
50
|
* Document-method: BEncode.decode
|
@@ -118,25 +58,104 @@ static VALUE _decode(char** str, long* len, long rlen){
|
|
118
58
|
*
|
119
59
|
* Examples:
|
120
60
|
*
|
121
|
-
* 'i1e' => 1
|
122
|
-
* 'i-1e' => -1
|
123
|
-
* '6:string' => 'string'
|
61
|
+
* BEncode.decode('i1e') => 1
|
62
|
+
* BEncode.decode('i-1e') => -1
|
63
|
+
* BEncode.decode('6:string') => 'string'
|
124
64
|
*/
|
125
65
|
|
126
|
-
static VALUE decode(VALUE self, VALUE
|
127
|
-
long rlen
|
128
|
-
char*
|
129
|
-
VALUE ret;
|
66
|
+
static VALUE decode(VALUE self, VALUE encoded){
|
67
|
+
long len, rlen;
|
68
|
+
char* str;
|
69
|
+
VALUE ret, container_stack, current_container, key, crt;
|
130
70
|
|
131
|
-
if(!rb_obj_is_kind_of(
|
71
|
+
if(!rb_obj_is_kind_of(encoded, rb_cString))
|
132
72
|
rb_raise(rb_eTypeError, "String expected");
|
133
73
|
|
134
|
-
|
135
|
-
|
136
|
-
|
74
|
+
len = rlen = RSTRING_LEN(encoded);
|
75
|
+
if(!len)
|
76
|
+
return Qnil;
|
77
|
+
|
78
|
+
str = RSTRING_PTR(encoded);
|
79
|
+
container_stack = rb_ary_new();
|
80
|
+
current_container = ret = key = Qnil;
|
81
|
+
|
82
|
+
while(len){
|
83
|
+
int state = ELEMENT_SCALAR;
|
84
|
+
switch(*str){
|
85
|
+
case 'l':
|
86
|
+
case 'd':
|
87
|
+
crt = *str == 'l' ? rb_ary_new() : rb_hash_new();
|
88
|
+
NEXT_CHAR;
|
89
|
+
if(NIL_P(current_container)){
|
90
|
+
ret = current_container = crt;
|
91
|
+
continue;
|
92
|
+
}
|
93
|
+
state = ELEMENT_STRUCT;
|
94
|
+
break;
|
95
|
+
case 'i':
|
96
|
+
NEXT_CHAR;
|
97
|
+
crt = LONG2FIX(parse_num(&str, &len));
|
98
|
+
|
99
|
+
if(!len)
|
100
|
+
rb_raise(DecodeError, "Unpexpected integer end!");
|
101
|
+
if(*str != 'e')
|
102
|
+
rb_raise(DecodeError, "Mailformed integer at %d byte: %c", rlen - len, *str);
|
103
|
+
|
104
|
+
NEXT_CHAR;
|
105
|
+
break;
|
106
|
+
case '0'...'9':{
|
107
|
+
long slen = parse_num(&str, &len);
|
108
|
+
|
109
|
+
if(slen < 0 || (len && *str != ':'))
|
110
|
+
rb_raise(DecodeError, "Invalid string length specification at %d: %c", rlen - len, *str);
|
111
|
+
|
112
|
+
if(!len || len < slen + 1)
|
113
|
+
rb_raise(DecodeError, "Unexpected string end!");
|
114
|
+
|
115
|
+
crt = rb_str_new(++str, slen);
|
116
|
+
str += slen;
|
117
|
+
len -= slen + 1;
|
118
|
+
break;
|
119
|
+
}
|
120
|
+
case 'e':
|
121
|
+
state = ELEMENT_END;
|
122
|
+
NEXT_CHAR;
|
123
|
+
break;
|
124
|
+
default:
|
125
|
+
rb_raise(DecodeError, "Unknown element type at %d: %c!", rlen - len, *str);
|
126
|
+
}
|
127
|
+
|
128
|
+
if(state == ELEMENT_END){
|
129
|
+
if(NIL_P(current_container))
|
130
|
+
rb_raise(DecodeError, "Unexpected container end at %d!", rlen - len);
|
131
|
+
current_container = rb_ary_pop(container_stack);
|
132
|
+
}else if(NIL_P(current_container)){
|
133
|
+
if(NIL_P(ret))
|
134
|
+
ret = crt;
|
135
|
+
break;
|
136
|
+
}else{
|
137
|
+
if(TYPE(current_container) == T_ARRAY){
|
138
|
+
rb_ary_push(current_container, crt);
|
139
|
+
}else if(NIL_P(key)){
|
140
|
+
if(TYPE(crt) != T_STRING)
|
141
|
+
rb_raise(DecodeError, "Dictionary key must be a string (at %d)!", rlen - len);
|
142
|
+
key = crt;
|
143
|
+
}else{
|
144
|
+
rb_hash_aset(current_container, key, crt);
|
145
|
+
key = Qnil;
|
146
|
+
}
|
147
|
+
|
148
|
+
if(state == ELEMENT_STRUCT){
|
149
|
+
rb_ary_push(container_stack, current_container);
|
150
|
+
if(max_depth > -1 && max_depth < RARRAY_LEN(container_stack) + 1)
|
151
|
+
rb_raise(DecodeError, "Structure is too deep!");
|
152
|
+
current_container = crt;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
137
156
|
|
138
157
|
if(len)
|
139
|
-
rb_raise(DecodeError, "String has garbage on the end (starts at %d).", rlen - len);
|
158
|
+
rb_raise(DecodeError, "String has garbage on the end (starts at %d: %s).", rlen - len);
|
140
159
|
|
141
160
|
return ret;
|
142
161
|
}
|
@@ -257,9 +276,53 @@ static VALUE mod_encode(VALUE self, VALUE x){
|
|
257
276
|
return encode(x);
|
258
277
|
}
|
259
278
|
|
279
|
+
/*
|
280
|
+
* Document-method: max_depth
|
281
|
+
* call-seq:
|
282
|
+
* BEncode.max_depth
|
283
|
+
*
|
284
|
+
* Get maximum depth of parsed structure.
|
285
|
+
*/
|
286
|
+
|
287
|
+
static VALUE get_max_depth(VALUE self){
|
288
|
+
return LONG2FIX(max_depth);
|
289
|
+
}
|
290
|
+
|
291
|
+
/*
|
292
|
+
* Document-method: max_depth=
|
293
|
+
* call-seq:
|
294
|
+
* BEncode.max_depth = _integer_
|
295
|
+
*
|
296
|
+
* Sets maximum depth of parsed structure.
|
297
|
+
* Expects integer greater or equal to 0.
|
298
|
+
* By default this value is 5000.
|
299
|
+
* Assigning nil will disable depth check.
|
300
|
+
*/
|
301
|
+
|
302
|
+
static VALUE set_max_depth(VALUE self, VALUE depth){
|
303
|
+
long t;
|
304
|
+
|
305
|
+
if(NIL_P(depth)){
|
306
|
+
max_depth = -1;
|
307
|
+
return depth;
|
308
|
+
}
|
309
|
+
|
310
|
+
if(!rb_obj_is_kind_of(depth, rb_cInteger))
|
311
|
+
rb_raise(rb_eArgError, "Integer expected!");
|
312
|
+
|
313
|
+
t = NUM2LONG(depth);
|
314
|
+
if(t < 0)
|
315
|
+
rb_raise(rb_eArgError, "Depth must be between 0 and 5000000");
|
316
|
+
|
317
|
+
max_depth = t;
|
318
|
+
return depth;
|
319
|
+
}
|
320
|
+
|
260
321
|
void Init_bencode_ext(){
|
261
322
|
readId = rb_intern("read");
|
323
|
+
max_depth = 5000;
|
262
324
|
BEncode = rb_define_module("BEncode");
|
325
|
+
|
263
326
|
/*
|
264
327
|
* Document-class: BEncode::DecodeError
|
265
328
|
* Exception for indicating decoding errors.
|
@@ -275,6 +338,8 @@ void Init_bencode_ext(){
|
|
275
338
|
rb_define_singleton_method(BEncode, "decode", decode, 1);
|
276
339
|
rb_define_singleton_method(BEncode, "encode", mod_encode, 1);
|
277
340
|
rb_define_singleton_method(BEncode, "decode_file", decode_file, 1);
|
341
|
+
rb_define_singleton_method(BEncode, "max_depth", get_max_depth, 0);
|
342
|
+
rb_define_singleton_method(BEncode, "max_depth=", set_max_depth, 1);
|
278
343
|
|
279
344
|
rb_define_method(BEncode, "bencode", encode, 0);
|
280
345
|
rb_define_method(rb_cString, "bdecode", str_bdecode, 0);
|
data/ext/bencode_ext/bencode.h
CHANGED
@@ -9,7 +9,6 @@ static VALUE EncodeError;
|
|
9
9
|
static VALUE readId;
|
10
10
|
|
11
11
|
static long parse_num(char**, long*);
|
12
|
-
static VALUE _decode(char**, long*, long);
|
13
12
|
static VALUE decode(VALUE, VALUE);
|
14
13
|
static VALUE encode(VALUE);
|
15
14
|
static int hash_traverse(VALUE, VALUE, VALUE);
|
@@ -17,6 +16,9 @@ static VALUE str_bdecode(VALUE);
|
|
17
16
|
static VALUE mod_encode(VALUE, VALUE);
|
18
17
|
static VALUE _decode_file(VALUE);
|
19
18
|
static VALUE decode_file(VALUE, VALUE);
|
19
|
+
static VALUE get_max_depth(VALUE);
|
20
|
+
static VALUE set_max_depth(VALUE, VALUE);
|
21
|
+
static long max_depth;
|
20
22
|
void Init_bencode_ext();
|
21
23
|
|
22
24
|
#endif
|
data/ext/bencode_ext/extconf.rb
CHANGED
data/test/test_bencode_ext.rb
CHANGED
@@ -9,6 +9,7 @@ class TestBencodeExt < Test::Unit::TestCase
|
|
9
9
|
assert_equal('li1ei2ee', [1, 2].bencode)
|
10
10
|
assert_equal('d3:keyi10ee', {:key => 10}.bencode)
|
11
11
|
assert_equal('ld1:ki1eed1:ki2eed1:kd1:v3:123eee', [{:k => 1}, {:k => 2}, {:k => {:v => '123'}}].bencode)
|
12
|
+
assert_equal('llli1eei1eei1ee', [[[1],1],1].bencode)
|
12
13
|
|
13
14
|
assert_raises(BEncode::EncodeError) { STDERR.bencode }
|
14
15
|
end
|
@@ -20,9 +21,21 @@ class TestBencodeExt < Test::Unit::TestCase
|
|
20
21
|
assert_equal([1, 2], 'li1ei2ee'.bdecode)
|
21
22
|
assert_equal({'key' => 10}, 'd3:keyi10ee'.bdecode)
|
22
23
|
assert_equal([{'k' => 1}, {'k' => 2}, {'k' => {'v' => '123'}}], 'ld1:ki1eed1:ki2eed1:kd1:v3:123eee'.bdecode)
|
24
|
+
assert_equal([[[1],1],1], 'llli1eei1eei1ee'.bdecode)
|
23
25
|
|
24
26
|
assert_raises(BEncode::DecodeError) {'33:unpexpected_end'.bdecode }
|
25
27
|
assert_raises(BEncode::DecodeError) { 'i1x'.bdecode }
|
28
|
+
assert_raises(ArgumentError) { BEncode.max_depth = 1.1 }
|
29
|
+
assert_raises(ArgumentError) { BEncode.max_depth = -5 }
|
30
|
+
assert_raises(BEncode::DecodeError) do
|
31
|
+
BEncode.max_depth = 1
|
32
|
+
'lli1eee'.bdecode
|
33
|
+
end
|
34
|
+
assert_nothing_raised(BEncode::DecodeError) do
|
35
|
+
BEncode.max_depth = 0
|
36
|
+
'i1e'.bdecode
|
37
|
+
end
|
38
|
+
|
26
39
|
assert_nil(''.bdecode)
|
27
40
|
end
|
28
41
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 1
|
8
7
|
- 2
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- naquad
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-14 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -53,7 +53,6 @@ executables: []
|
|
53
53
|
|
54
54
|
extensions:
|
55
55
|
- ext/bencode_ext/extconf.rb
|
56
|
-
- ext/bencode_ext/extconf.rb
|
57
56
|
extra_rdoc_files:
|
58
57
|
- LICENSE.txt
|
59
58
|
- README.rdoc
|