bencode_ext 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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.2
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.1.2"
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-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", "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"
@@ -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
- NEXT_CHAR;
31
+ ++*str;
32
+ --*len;
39
33
  }
40
34
 
41
35
  while(*len && **str >= '0' && **str <= '9'){
42
36
  ret = ret * 10 + (**str - '0');
43
- NEXT_CHAR;
37
+ ++*str;
38
+ --*len;
44
39
  }
45
40
 
46
41
  return ret * t;
47
42
  }
48
43
 
49
- static VALUE _decode(char** str, long* len, long rlen){
50
- if(!*len)
51
- return Qnil;
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 str){
127
- long rlen, len;
128
- char* ptr;
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(str, rb_cString))
71
+ if(!rb_obj_is_kind_of(encoded, rb_cString))
132
72
  rb_raise(rb_eTypeError, "String expected");
133
73
 
134
- rlen = len = RSTRING_LEN(str);
135
- ptr = RSTRING_PTR(str);
136
- ret = _decode(&ptr, &len, len);
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);
@@ -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
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/ruby -w
2
2
 
3
3
  require 'mkmf'
4
+ $CFLAGS='-g'
5
+ $LDFLAGS='-g'
4
6
  create_makefile('bencode_ext')
@@ -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
- version: 0.1.2
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-01 00:00:00 +02:00
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