smarter C encoder and remove all references to List

This commit is contained in:
Tom Preston-Werner 2009-10-24 18:28:44 -07:00
Родитель 63ce966c3a
Коммит 8a20b33539
7 изменённых файлов: 208 добавлений и 106 удалений

Просмотреть файл

@ -25,7 +25,7 @@ Ruby encoder / Ruby decoder
BERT large 4.190000 0.620000 4.810000 ( 4.959149)
BERT complex 19.380000 0.080000 19.460000 ( 20.402862)
Simple C encoder / Ruby decoder
Simple C decoder / Ruby encoder
41503465479e8762916d6997d91639f0d7308a13
BERT tiny 0.030000 0.000000 0.030000 ( 0.033826)
@ -39,3 +39,10 @@ Smarter Ruby decoder
BERT small 0.810000 0.010000 0.820000 ( 0.831905)
BERT large 4.340000 0.600000 4.940000 ( 5.064875)
BERT complex 18.460000 0.070000 18.530000 ( 19.096184)
Smarter C decoder
BERT tiny 0.030000 0.000000 0.030000 ( 0.035685)
BERT small 0.350000 0.010000 0.360000 ( 0.358929)
BERT large 2.410000 0.560000 2.970000 ( 3.056593)
BERT complex 7.910000 0.040000 7.950000 ( 8.236641)

Просмотреть файл

@ -17,12 +17,19 @@
static VALUE mBERT;
static VALUE cDecode;
static VALUE cTuple;
void Init_decode();
VALUE method_decode(VALUE klass, VALUE rString);
VALUE read_any_raw(unsigned char **pData);
// printers
void p(VALUE val) {
rb_funcall(rb_mKernel, rb_intern("p"), 1, val);
}
// checkers
void check_int(int num) {
@ -67,7 +74,105 @@ unsigned int read_4(unsigned char **pData) {
return val;
}
// tuples, lists
// tuples
VALUE read_tuple(unsigned char **pData, int arity);
VALUE read_dict_pair(unsigned char **pData) {
if(read_1(pData) != ERL_SMALL_TUPLE) {
rb_raise(rb_eStandardError, "Invalid dict pair, not a small tuple");
}
int arity = read_1(pData);
if(arity != 2) {
rb_raise(rb_eStandardError, "Invalid dict pair, not a 2-tuple");
}
return read_tuple(pData, arity);
}
VALUE read_dict(unsigned char **pData) {
int type = read_1(pData);
if(!(type == ERL_LIST || type == ERL_NIL)) {
rb_raise(rb_eStandardError, "Invalid dict spec, not an erlang list");
}
int length = 0;
if(type == ERL_LIST) {
length = read_4(pData);
}
VALUE cHash = rb_const_get(rb_cObject, rb_intern("Hash"));
VALUE hash = rb_funcall(cHash, rb_intern("new"), 0);
int i;
for(i = 0; i < length; ++i) {
VALUE pair = read_dict_pair(pData);
VALUE first = rb_ary_entry(pair, 0);
VALUE last = rb_ary_entry(pair, 1);
rb_funcall(hash, rb_intern("store"), 2, first, last);
}
if(type == ERL_LIST) {
read_1(pData);
}
return hash;
}
VALUE read_complex_type(unsigned char **pData, int arity) {
VALUE type = read_any_raw(pData);
ID id = SYM2ID(type);
if(id == rb_intern("nil")) {
return Qnil;
} else if(id == rb_intern("true")) {
return Qtrue;
} else if(id == rb_intern("false")) {
return Qfalse;
} else if(id == rb_intern("time")) {
VALUE megasecs = read_any_raw(pData);
VALUE msecs = rb_funcall(megasecs, rb_intern("*"), 1, INT2NUM(1000000));
VALUE secs = read_any_raw(pData);
VALUE microsecs = read_any_raw(pData);
VALUE stamp = rb_funcall(msecs, rb_intern("+"), 1, secs);
return rb_funcall(rb_cTime, rb_intern("at"), 2, stamp, microsecs);
} else if(id == rb_intern("regex")) {
VALUE source = read_any_raw(pData);
VALUE opts = read_any_raw(pData);
int flags = 0;
if(rb_ary_includes(opts, ID2SYM(rb_intern("caseless"))))
flags = flags | 1;
if(rb_ary_includes(opts, ID2SYM(rb_intern("extended"))))
flags = flags | 2;
if(rb_ary_includes(opts, ID2SYM(rb_intern("multiline"))))
flags = flags | 4;
return rb_funcall(rb_cRegexp, rb_intern("new"), 2, source, INT2NUM(flags));
} else if(id == rb_intern("dict")) {
return read_dict(pData);
} else {
return Qnil;
}
}
VALUE read_tuple(unsigned char **pData, int arity) {
if(arity > 0) {
VALUE tag = read_any_raw(pData);
if(SYM2ID(tag) == rb_intern("bert")) {
return read_complex_type(pData, arity);
} else {
VALUE tuple = rb_funcall(cTuple, rb_intern("new"), 1, INT2NUM(arity));
rb_ary_store(tuple, 0, tag);
int i;
for(i = 1; i < arity; ++i) {
rb_ary_store(tuple, i, read_any_raw(pData));
}
return tuple;
}
} else {
return rb_funcall(cTuple, rb_intern("new"), 0);
}
}
VALUE read_small_tuple(unsigned char **pData) {
if(read_1(pData) != ERL_SMALL_TUPLE) {
@ -75,15 +180,7 @@ VALUE read_small_tuple(unsigned char **pData) {
}
int arity = read_1(pData);
VALUE array = rb_ary_new2(arity);
int i;
for(i = 0; i < arity; ++i) {
rb_ary_store(array, i, read_any_raw(pData));
}
return array;
return read_tuple(pData, arity);
}
VALUE read_large_tuple(unsigned char **pData) {
@ -92,17 +189,11 @@ VALUE read_large_tuple(unsigned char **pData) {
}
int arity = read_4(pData);
VALUE array = rb_ary_new2(arity);
int i;
for(i = 0; i < arity; ++i) {
rb_ary_store(array, i, read_any_raw(pData));
}
return array;
return read_tuple(pData, arity);
}
// lists
VALUE read_list(unsigned char **pData) {
if(read_1(pData) != ERL_LIST) {
rb_raise(rb_eStandardError, "Invalid Type, not an erlang list");
@ -110,8 +201,7 @@ VALUE read_list(unsigned char **pData) {
int size = read_4(pData);
VALUE list_class = rb_const_get(mBERT, rb_intern("List"));
VALUE array = rb_funcall(list_class, rb_intern("new"), 1, INT2NUM(size));
VALUE array = rb_ary_new2(size);
int i;
for(i = 0; i < size; ++i) {
@ -154,8 +244,7 @@ VALUE read_string(unsigned char **pData) {
unsigned char buf[length + 1];
read_string_raw(buf, pData, length);
VALUE list_class = rb_const_get(mBERT, rb_intern("List"));
VALUE array = rb_funcall(list_class, rb_intern("new"), 1, INT2NUM(length));
VALUE array = rb_ary_new2(length);
int i = 0;
for(i; i < length; ++i) {
@ -279,8 +368,7 @@ VALUE read_nil(unsigned char **pData) {
rb_raise(rb_eStandardError, "Invalid Type, not a nil list");
}
VALUE list_class = rb_const_get(mBERT, rb_intern("List"));
return rb_funcall(list_class, rb_intern("new"), 0);
return rb_ary_new2(0);
}
// read_any_raw
@ -347,6 +435,7 @@ VALUE method_impl(VALUE klass) {
void Init_decode() {
mBERT = rb_const_get(rb_cObject, rb_intern("BERT"));
cDecode = rb_define_class_under(mBERT, "Decode", rb_cObject);
cTuple = rb_const_get(mBERT, rb_intern("Tuple"));
rb_define_singleton_method(cDecode, "decode", method_decode, 1);
rb_define_singleton_method(cDecode, "impl", method_impl, 0);
}

Просмотреть файл

@ -13,12 +13,6 @@ module BERT
"<<" + bytes.join(',') + ">>"
end
class List < Array
def inspect
"l#{super}"
end
end
class Tuple < Array
def inspect
"t#{super}"

Просмотреть файл

@ -24,8 +24,8 @@ module BERT
when Symbol then write_symbol(obj)
when Fixnum, Bignum then write_fixnum(obj)
when Float then write_float(obj)
when BERT::List then write_list(obj)
when Array then write_tuple(obj)
when Tuple then write_tuple(obj)
when Array then write_list(obj)
when String then write_binary(obj)
else
fail(obj)

Просмотреть файл

@ -16,27 +16,27 @@ module BERT
def self.convert(item)
case item
when Hash
pairs = List[]
item.each_pair { |k, v| pairs << [convert(k), convert(v)] }
[:bert, :dict, pairs]
pairs = []
item.each_pair { |k, v| pairs << t[convert(k), convert(v)] }
t[:bert, :dict, pairs]
when Tuple
item.map { |x| convert(x) }
Tuple.new(item.map { |x| convert(x) })
when Array
List.new(item.map { |x| convert(x) })
item.map { |x| convert(x) }
when nil
[:bert, :nil]
t[:bert, :nil]
when TrueClass
[:bert, :true]
t[:bert, :true]
when FalseClass
[:bert, :false]
t[:bert, :false]
when Time
[:bert, :time, item.to_i / 1_000_000, item.to_i % 1_000_000, item.usec]
t[:bert, :time, item.to_i / 1_000_000, item.to_i % 1_000_000, item.usec]
when Regexp
options = List[]
options = []
options << :caseless if item.options & Regexp::IGNORECASE > 0
options << :extended if item.options & Regexp::EXTENDED > 0
options << :multiline if item.options & Regexp::MULTILINE > 0
[:bert, :regex, item.source, options]
t[:bert, :regex, item.source, options]
else
item
end

Просмотреть файл

@ -1,64 +1,76 @@
require 'test_helper'
class DecoderTest < Test::Unit::TestCase
context "BERT Decoder complex type converter" do
# should "convert nil" do
# before = t[:bert, :nil]
# after = nil
# assert_equal after, BERT::Decoder.convert(before)
# end
#
# should "convert nested nil" do
# before = [t[:bert, :nil], [t[:bert, :nil]]]
# after = [nil, [nil]]
# assert_equal after, BERT::Decoder.convert(before)
# end
#
# should "convert hashes" do
# before = t[:bert, :dict, [[:foo, 'bar']]]
# after = {:foo => 'bar'}
# assert_equal after, BERT::Decoder.convert(before)
# end
#
# should "convert empty hashes" do
# before = t[:bert, :dict, []]
# after = {}
# assert_equal after, BERT::Decoder.convert(before)
# end
#
# should "convert nested hashes" do
# before = t[:bert, :dict, [[:foo, t[:bert, :dict, [[:baz, 'bar']]]]]]
# after = {:foo => {:baz => 'bar'}}
# assert_equal after, BERT::Decoder.convert(before)
# end
BERT_NIL = [131,104,2,100,0,4,98,101,114,116,100,0,3,110,105,108].pack('c*')
BERT_TRUE = [131,104,2,100,0,4,98,101,114,116,100,0,4,116,114,117,101].pack('c*')
BERT_FALSE = [131,104,2,100,0,4,98,101,114,116,100,0,5,102,97,108,115,101].pack('c*')
should "convert true" do
# {bert, true}
bert = [131,104,2,100,0,4,98,101,114,116,100,0,4,116,114,117,101].pack('c*')
assert_equal true, BERT::Decoder.decode(bert)
context "BERT Decoder complex type converter" do
should "convert nil" do
assert_equal nil, BERT::Decoder.decode(BERT_NIL)
end
# should "convert false" do
# before = t[:bert, :false]
# after = false
# assert_equal after, BERT::Decoder.convert(before)
# end
#
# should "convert times" do
# before = t[:bert, :time, 1254, 976067, 0]
# after = Time.at(1254976067)
# assert_equal after, BERT::Decoder.convert(before)
# end
#
# should "convert regexen" do
# before = t[:bert, :regex, '^c(a)t$', [:caseless, :extended]]
# after = /^c(a)t$/ix
# assert_equal after, BERT::Decoder.convert(before)
# end
#
# should "leave other stuff alone" do
# before = [1, 2.0, [:foo, 'bar']]
# assert_equal before, BERT::Decoder.convert(before)
# end
should "convert nested nil" do
bert = [131,108,0,0,0,2,104,2,100,0,4,98,101,114,116,100,0,3,110,105,
108,108,0,0,0,1,104,2,100,0,4,98,101,114,116,100,0,3,110,105,
108,106,106].pack('c*')
assert_equal [nil, [nil]], BERT::Decoder.decode(bert)
end
should "convert hashes" do
bert = [131,104,3,100,0,4,98,101,114,116,100,0,4,100,105,99,116,108,
0,0,0,1,104,2,100,0,3,102,111,111,109,0,0,0,3,98,97,114,
106].pack('c*')
after = {:foo => 'bar'}
assert_equal after, BERT::Decoder.decode(bert)
end
should "convert empty hashes" do
bert = [131,104,3,100,0,4,98,101,114,116,100,0,4,100,105,99,116,
106].pack('c*')
after = {}
assert_equal after, BERT::Decoder.decode(bert)
end
should "convert nested hashes" do
bert = [131,104,3,100,0,4,98,101,114,116,100,0,4,100,105,99,116,108,0,
0,0,1,104,2,100,0,3,102,111,111,104,3,100,0,4,98,101,114,116,
100,0,4,100,105,99,116,108,0,0,0,1,104,2,100,0,3,98,97,122,109,
0,0,0,3,98,97,114,106,106].pack('c*')
after = {:foo => {:baz => 'bar'}}
assert_equal after, BERT::Decoder.decode(bert)
end
should "convert true" do
assert_equal true, BERT::Decoder.decode(BERT_TRUE)
end
should "convert false" do
assert_equal false, BERT::Decoder.decode(BERT_FALSE)
end
should "convert times" do
bert = [131,104,5,100,0,4,98,101,114,116,100,0,4,116,105,109,101,98,0,
0,4,230,98,0,14,228,195,97,0].pack('c*')
after = Time.at(1254976067)
assert_equal after, BERT::Decoder.decode(bert)
end
should "convert regexen" do
bert = [131,104,4,100,0,4,98,101,114,116,100,0,5,114,101,103,101,120,
109,0,0,0,7,94,99,40,97,41,116,36,108,0,0,0,2,100,0,8,99,97,
115,101,108,101,115,115,100,0,8,101,120,116,101,110,100,101,
100,106].pack('c*')
after = /^c(a)t$/ix
assert_equal after, BERT::Decoder.decode(bert)
end
should "leave other stuff alone" do
bert = [131,108,0,0,0,3,97,1,99,50,46,48,48,48,48,48,48,48,48,48,48,48,
48,48,48,48,48,48,48,48,48,101,43,48,48,0,0,0,0,0,108,0,0,0,2,
100,0,3,102,111,111,109,0,0,0,3,98,97,114,106,106].pack('c*')
after = [1, 2.0, [:foo, 'bar']]
assert_equal after, BERT::Decoder.decode(bert)
end
end
end

Просмотреть файл

@ -27,7 +27,7 @@ class EncoderTest < Test::Unit::TestCase
should "convert hash to tuple with array of tuples" do
arr = BERT::Encoder.convert({:foo => 'bar'})
assert arr.is_a?(Array)
assert arr[2].is_a?(BERT::List)
assert arr[2].is_a?(Array)
assert arr[2][0].is_a?(Array)
end
@ -38,13 +38,13 @@ class EncoderTest < Test::Unit::TestCase
should "convert array to erl list" do
list = BERT::Encoder.convert([1, 2])
assert list.is_a?(BERT::List)
assert list.is_a?(Array)
end
should "convert an array in a tuple" do
arrtup = BERT::Encoder.convert(t[:foo, [1, 2]])
assert arrtup.is_a?(Array)
assert arrtup[1].is_a?(BERT::List)
assert arrtup[1].is_a?(Array)
end
should "convert true" do
@ -74,9 +74,9 @@ class EncoderTest < Test::Unit::TestCase
should "properly convert types" do
ruby = t[:user, {:name => 'TPW'}, [/cat/i, 9.9], nil, true, false, :true, :false]
cruby = BERT::Encoder.convert(ruby)
assert cruby.instance_of?(Array)
assert cruby.instance_of?(BERT::Tuple)
assert cruby[0].instance_of?(Symbol)
assert cruby[1].instance_of?(Array)
assert cruby[1].instance_of?(BERT::Tuple)
end
should "leave other stuff alone" do