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 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