diff --git a/README.md b/README.md index 3975e1f..372768b 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ puts obj.raw_hash.inspect #### Object `Ruby::Marshal.load(::Class, IO)` and `Ruby::Marshal.load(::Class, ::String)` are provided as convenience methods for unmarshalling straight into a Crystal object. Any class passed to these methods must implement `#initialize(obj : ::Ruby::Marshal::StreamObject)` in order to read the marshalled data. -The `ruby_marshal_properties` macro is provided as a convenience for simple marshalled objects. It will auto-unmarshal for you provided the correct schema for the data. +The `Ruby::Marshal.mapping` macro is provided as a convenience for simple marshalled objects. It will auto-unmarshal for you provided the correct schema for the data. Unlike the other datatypes, `#data` in the case of objects will return a `Ruby::Marshall::Null` object. To use an unmarshalled object, cast to `Ruby::Marshal::Object`. You can then reach the data by means of `#read_raw_attr(::String)` or `#read_attr(::String)`. @@ -284,10 +284,9 @@ obj = Ruby::Marshal.load( User, File.read("marshalled-valid.out") ) puts obj.inspect #=> # -# As a convenience to setting these classes up, use the `ruby_marshal_properties` helper macro +# As a convenience to setting these classes up, use the `Ruby::Marshal.mapping` helper macro class User - property :id, :name - ruby_marshal_properties({ id: ::Int32, name: ::String }) + Ruby::Marshal.mapping({ id: ::Int32, name: ::String }) end ``` diff --git a/spec/data/generate-marshalled-objects.rb b/spec/data/generate-marshalled-objects.rb index 92d489e..e4765f9 100755 --- a/spec/data/generate-marshalled-objects.rb +++ b/spec/data/generate-marshalled-objects.rb @@ -74,7 +74,7 @@ class UserHash < Hash ; end File.open( File.join(File.dirname( __FILE__ ), 'marshalled-string.out'), 'w') { |f| f.write(Marshal.dump("test_string")) } File.open( File.join(File.dirname( __FILE__ ), 'marshalled-symbol.out'), 'w') { |f| f.write(Marshal.dump(:test_symbol)) } File.open( File.join(File.dirname( __FILE__ ), 'marshalled-symbol-array.out'), 'w') { |f| f.write(Marshal.dump([:hello, :hello])) } -File.open( File.join(File.dirname( __FILE__ ), 'marshalled-complex-array.out'), 'w') { |f| f.write(Marshal.dump([:hello, :hello, [:hello, :test, 1, nil],1_000_000, true, false, nil, "string", "string"])) } +File.open( File.join(File.dirname( __FILE__ ), 'marshalled-complex-array.out'), 'w') { |f| f.write(Marshal.dump([:hello, :hello, [:hello, :test, 1, nil],1_000_000, true, false, nil, "string", "string", -1.2])) } File.open( File.join(File.dirname( __FILE__ ), 'marshalled-hash.out'), 'w') { |f| f.write(Marshal.dump({:simple => 'hash'})) } hash_with_default = Hash.new("default_value") hash_with_default['key'] = 1 diff --git a/spec/data/marshalled-complex-array.out b/spec/data/marshalled-complex-array.out index d93c060..9c48405 100644 Binary files a/spec/data/marshalled-complex-array.out and b/spec/data/marshalled-complex-array.out differ diff --git a/spec/ruby_marshal_dump_spec.cr b/spec/ruby_marshal_dump_spec.cr new file mode 100644 index 0000000..385b3d5 --- /dev/null +++ b/spec/ruby_marshal_dump_spec.cr @@ -0,0 +1,250 @@ +require "./spec_helper" + +describe Ruby::Marshal do + + it "#dump true" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-true.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(true) ) } + object = Ruby::Marshal.dump(true) + Ruby::Marshal.load( File.open(f) ).data.should be_true + end + + it "#dump false" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-false.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(false) ) } + object = Ruby::Marshal.dump(false) + Ruby::Marshal.load( File.open(f) ).data.should be_false + end + + it "#dump nil" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-nil.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(nil) ) } + object = Ruby::Marshal.dump(nil) + Ruby::Marshal.load( File.open(f) ).data.should be_nil + end + + it "#dump a class" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-class.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(User) ) } + object = Ruby::Marshal.dump(User) + Ruby::Marshal.load( File.open(f) ).data.should eq("User") + end + + it "#dump a module" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-module.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(TestModule) ) } + object = Ruby::Marshal.dump(TestModule) + Ruby::Marshal.load( File.open(f) ).data.should eq("TestModule") + end + + it "#dump a symbol" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-symbol.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(:test_symbol) ) } + object = Ruby::Marshal.dump(:test_symbol) + Ruby::Marshal.load( File.open(f) ).data.should eq("test_symbol") + end + + it "#dump 0" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-zero.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(0) ) } + object = Ruby::Marshal.dump(0) + Ruby::Marshal.load( File.open(f) ).data.should eq(0) + end + + it "#dump 122" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-zero-byte-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(122) ) } + object = Ruby::Marshal.dump(122) + Ruby::Marshal.load( File.open(f) ).data.should eq(122) + end + + it "#dump -122" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-zero-byte-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-122) ) } + object = Ruby::Marshal.dump(-122) + Ruby::Marshal.load( File.open(f) ).data.should eq(-122) + end + + it "#dump -123" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-one-byte-negative-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-123) ) } + object = Ruby::Marshal.dump(-123) + Ruby::Marshal.load( File.open(f) ).data.should eq(-123) + end + + it "#dump 123" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-one-byte-positive-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(123) ) } + object = Ruby::Marshal.dump(123) + Ruby::Marshal.load( File.open(f) ).data.should eq(123) + end + + it "#dump 255" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-one-byte-positive-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(255) ) } + object = Ruby::Marshal.dump(255) + Ruby::Marshal.load( File.open(f) ).data.should eq(255) + end + + it "#dump -256" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-one-byte-negative-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-256) ) } + object = Ruby::Marshal.dump(-256) + Ruby::Marshal.load( File.open(f) ).data.should eq(-256) + end + + it "#dump 256" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-two-byte-positive-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(256) ) } + object = Ruby::Marshal.dump(256) + Ruby::Marshal.load( File.open(f) ).data.should eq(256) + end + + it "#dump -257" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-two-byte-negative-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-257) ) } + object = Ruby::Marshal.dump(-257) + Ruby::Marshal.load( File.open(f) ).data.should eq(-257) + end + + it "#dump 65_535" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-two-byte-positive-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(65_535) ) } + object = Ruby::Marshal.dump(65_535) + Ruby::Marshal.load( File.open(f) ).data.should eq(65_535) + end + + it "#dump -65_536" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-two-byte-negative-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-65_536) ) } + object = Ruby::Marshal.dump(-65_536) + Ruby::Marshal.load( File.open(f) ).data.should eq(-65_536) + end + + it "#dump 65_536" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-three-byte-positive-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(65_536) ) } + object = Ruby::Marshal.dump(65_536) + Ruby::Marshal.load( File.open(f) ).data.should eq(65_536) + end + + it "#dump 16_777_215" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-three-byte-positive-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(16_777_215) ) } + object = Ruby::Marshal.dump(16_777_215) + Ruby::Marshal.load( File.open(f) ).data.should eq(16_777_215) + end + + it "#dump -65_537" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-three-byte-negative-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-65_537) ) } + object = Ruby::Marshal.dump(-65_537) + Ruby::Marshal.load( File.open(f) ).data.should eq(-65_537) + end + + it "#dump -16_777_216" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-three-byte-negative-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-16_777_216) ) } + object = Ruby::Marshal.dump(-16_777_216) + Ruby::Marshal.load( File.open(f) ).data.should eq(-16_777_216) + end + + it "#dump 16_777_216" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-four-byte-positive-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(16_777_216) ) } + object = Ruby::Marshal.dump(16_777_216) + Ruby::Marshal.load( File.open(f) ).data.should eq(16_777_216) + end + + it "#dump 1_073_741_823" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-four-byte-positive-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(1_073_741_823) ) } + object = Ruby::Marshal.dump(1_073_741_823) + Ruby::Marshal.load( File.open(f) ).data.should eq(1_073_741_823) + end + + it "#dump -16_777_217" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-four-byte-negative-int-upper.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-16_777_217) ) } + object = Ruby::Marshal.dump(-16_777_217) + Ruby::Marshal.load( File.open(f) ).data.should eq(-16_777_217) + end + + it "#dump -1_073_741_824" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-four-byte-negative-int-lower.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-1_073_741_824) ) } + object = Ruby::Marshal.dump(-1_073_741_824) + Ruby::Marshal.load( File.open(f) ).data.should eq(-1_073_741_824) + end + + it "#dump -1.26479" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-float.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(-1.26479) ) } + object = Ruby::Marshal.dump(-1.26479) + Ruby::Marshal.load( File.open(f) ).data.should eq(-1.26479) + end + + it "#dump an array" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-array.out") + a = ["string", 1, -1.2, nil, true, false, [true, nil, :symbol]] + b = ["string", 1, -1.2, nil, true, false, [true, nil, "symbol"]] + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(a) ) } + object = Ruby::Marshal.dump(a) + Ruby::Marshal.load( File.open(f) ).data.should eq(b) + end + + it "#dump a hash" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-hash.out") + a = { "simple" => 1, :hash => -1.2 } + b = { "simple" => 1, "hash" => -1.2 } + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(a) ) } + object = Ruby::Marshal.dump(a) + Ruby::Marshal.load( File.open(f) ).as(Ruby::Marshal::Hash).data.should eq(b) + end + + it "#dump a hash with default value" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-hash-with-default.out") + a = Hash(String, String | Int32).new("Default") + a["simple"] = 1 + #puts a["nonexistent"] + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(a) ) } + #puts `xxd #{f}` + object = Ruby::Marshal.dump(a) + r = Ruby::Marshal.load( File.open(f) ).as(Ruby::Marshal::HashWithDefault).data + r.should eq(a) + r["nada"].should eq("Default") + end + + it "#dump an instance" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-instance-object.out") + object = DumpTestUser.new + object.id = 1 + object.name = "string name" + object.valid = false + #object.opts = { "this" => "hash" } + write = Ruby::Marshal.dump(object) + File.open(f, "w") { |f| f.write(write) } + #result = Ruby::Marshal.load( DumpTestUser, File.open(f) ) + #puts result.inspect + #result.should eq(object) + end + + # TODO - these should dump InstanceObjects + it "#dump an regex" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-regex.out") + a = /[A-Za-z0-9]+/ + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump(a) ) } + object = Ruby::Marshal.dump(a) + result = Ruby::Marshal.load( File.open(f) ) + #puts result.inspect + result.data.should eq(a) + end + + it "#dump \"a string\"" do + f = File.join(File.dirname( __FILE__ ), "tmp", "marshalled-string.out") + File.open(f, "w") { |f| f.write( Ruby::Marshal.dump("a string") ) } + object = Ruby::Marshal.dump("a string") + Ruby::Marshal.load( File.open(f) ).data.should eq("a string") + end + +end diff --git a/spec/ruby-marshal_spec.cr b/spec/ruby_marshal_load_spec.cr similarity index 94% rename from spec/ruby-marshal_spec.cr rename to spec/ruby_marshal_load_spec.cr index ed960bb..bc50ebd 100644 --- a/spec/ruby-marshal_spec.cr +++ b/spec/ruby_marshal_load_spec.cr @@ -193,7 +193,7 @@ describe Ruby::Marshal do #puts `xxd #{SPEC_ROOT}/data/marshalled-complex-array.out` object = Ruby::Marshal.load( File.read( "#{SPEC_ROOT}/data/marshalled-complex-array.out" ) ) object.should be_a(Ruby::Marshal::Array) - object.data.should eq(["hello", "hello", ["hello", "test", 1, nil], 1_000_000, true, false, nil, "string", "string"]) + object.data.should eq(["hello", "hello", ["hello", "test", 1, nil], 1_000_000, true, false, nil, "string", "string", -1.2]) end it "should read a marshalled string" do @@ -209,12 +209,12 @@ describe Ruby::Marshal do object.read_attr("id", true).should eq(1) object.read_attr("name", true).should eq("Test") object.read_attr("valid", true).should eq(true) - data_hash = object.read_attr("data").as(Ruby::Marshal::Hash) - data_hash["some"].data.should eq(true) - data_hash[1].data.should eq("extra") - data_hash.data.each do |(k, v)| - if(k.class == Ruby::Marshal::Hash) - v.data.should eq(0x01) + data_hash = object.read_attr("data").as(Hash) + data_hash["some"].should eq(true) + data_hash[1].should eq("extra") + data_hash.each do |(k, v)| + if(k.class == Hash) + v.should eq(0x01) end end end @@ -238,16 +238,14 @@ describe Ruby::Marshal do it "should read a marshalled hash" do #puts `xxd #{SPEC_ROOT}/data/marshalled-hash.out` object = Ruby::Marshal.load( File.read( "#{SPEC_ROOT}/data/marshalled-hash.out" ) ) - object.as(::Ruby::Marshal::Hash)["simple"].data.should eq("hash") + object.as(::Ruby::Marshal::Hash)["simple"].should eq("hash") end it "should read a marshalled hash with a default" do #puts `xxd #{SPEC_ROOT}/data/marshalled-hash-with-default.out` object = Ruby::Marshal.load( File.read( "#{SPEC_ROOT}/data/marshalled-hash-with-default.out" ) ).as(Ruby::Marshal::Hash) - object["key"].data.should eq(1) - object.default_value.data.should eq("default_value") - raw_hash = object.raw_hash - raw_hash["new_key"].should eq("default_value") + object["key"].should eq(1) + object.default_value.should eq("default_value") end it "should read a marshalled class" do @@ -295,9 +293,8 @@ describe Ruby::Marshal do object.should be_a(Ruby::Marshal::UserClass) object = object.as(Ruby::Marshal::UserClass) object.class_name.data.should eq("UserHash") - object.data.should be_a(Ruby::Marshal::HashWithDefault) - wrapped_object = object.data.as(Ruby::Marshal::HashWithDefault) - wrapped_object["data"].data.should eq(123) + wrapped_object = object.data.as(Hash) + wrapped_object["data"].should eq(123) end end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index ba6206d..34c9393 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -13,32 +13,42 @@ class User @name = marshalled_object.read_raw_attr("name").as(::String) @valid = marshalled_object.read_raw_attr("valid").as(::Bool) @data = ::Hash(::String | ::Int32 | ::Hash(::String, ::Int32), ::Bool | ::String | ::Int32).new - raw_data = marshalled_object.read_attr("data").as(::Ruby::Marshal::Hash) - @data["some"] = raw_data["some"].data.as(::Bool) - @data[1] = raw_data[1].data.as(::String) - raw_data.each do |(k,v )| - if(k.class == Ruby::Marshal::Hash) - @data[{"key" => 1}] = v.data.as(::Int32) - end - end + raw_data = marshalled_object.read_attr("data").as(::Hash) + @data["some"] = raw_data["some"].as(::Bool) + @data[1] = raw_data[1].as(::String) + @data[{"key" => 1}] = raw_data[{"key" => 1}].as(Int32) end end class ExtendedUser - property :id - ruby_marshal_properties({ id: ::Int32 }) + + Ruby::Marshal.mapping({ + id: ::Int32, + }) end struct Customer - property :name, :address, :valid, :age - - def initialize(obj : ::Ruby::Marshal::StreamObject) - obj = obj.as(::Ruby::Marshal::Struct) - @name = obj.read_raw_attr("name").as(::String) - @address = obj.read_raw_attr("address").as(::String) - @valid = obj.read_raw_attr("valid").as(::Bool) - @age = obj.read_raw_attr("age").as(::Int32) - end + Ruby::Marshal.mapping({ + name: ::String, + address: ::String, + valid: ::Bool, + age: ::Int32, + }) +end + +module TestModule + end +class DumpTestUser + + def initialize ; end + + Ruby::Marshal.mapping({ + id: ::Int32, + name: ::String, + valid: ::Bool, + opts: ::Hash(String, String), + }) +end diff --git a/spec/tmp/marshalled-array.out b/spec/tmp/marshalled-array.out new file mode 100644 index 0000000..25b31c3 --- /dev/null +++ b/spec/tmp/marshalled-array.out @@ -0,0 +1 @@ +[ " stringif -1.20TF[T0" symbol \ No newline at end of file diff --git a/spec/tmp/marshalled-class.out b/spec/tmp/marshalled-class.out new file mode 100644 index 0000000..bd37121 --- /dev/null +++ b/spec/tmp/marshalled-class.out @@ -0,0 +1 @@ +c User \ No newline at end of file diff --git a/spec/tmp/marshalled-false.out b/spec/tmp/marshalled-false.out new file mode 100644 index 0000000..4bac0dd --- /dev/null +++ b/spec/tmp/marshalled-false.out @@ -0,0 +1 @@ +F \ No newline at end of file diff --git a/spec/tmp/marshalled-float.out b/spec/tmp/marshalled-float.out new file mode 100644 index 0000000..2c52d77 --- /dev/null +++ b/spec/tmp/marshalled-float.out @@ -0,0 +1 @@ +f -1.26479 \ No newline at end of file diff --git a/spec/tmp/marshalled-four-byte-negative-int-lower.out b/spec/tmp/marshalled-four-byte-negative-int-lower.out new file mode 100644 index 0000000..2045061 Binary files /dev/null and b/spec/tmp/marshalled-four-byte-negative-int-lower.out differ diff --git a/spec/tmp/marshalled-four-byte-negative-int-upper.out b/spec/tmp/marshalled-four-byte-negative-int-upper.out new file mode 100644 index 0000000..029b66b --- /dev/null +++ b/spec/tmp/marshalled-four-byte-negative-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-four-byte-positive-int-lower.out b/spec/tmp/marshalled-four-byte-positive-int-lower.out new file mode 100644 index 0000000..7ae686d Binary files /dev/null and b/spec/tmp/marshalled-four-byte-positive-int-lower.out differ diff --git a/spec/tmp/marshalled-four-byte-positive-int-upper.out b/spec/tmp/marshalled-four-byte-positive-int-upper.out new file mode 100644 index 0000000..05130de --- /dev/null +++ b/spec/tmp/marshalled-four-byte-positive-int-upper.out @@ -0,0 +1 @@ +i? \ No newline at end of file diff --git a/spec/tmp/marshalled-hash-with-default.out b/spec/tmp/marshalled-hash-with-default.out new file mode 100644 index 0000000..d6b84e9 --- /dev/null +++ b/spec/tmp/marshalled-hash-with-default.out @@ -0,0 +1 @@ +}" simplei0 \ No newline at end of file diff --git a/spec/tmp/marshalled-hash.out b/spec/tmp/marshalled-hash.out new file mode 100644 index 0000000..c549eb5 --- /dev/null +++ b/spec/tmp/marshalled-hash.out @@ -0,0 +1 @@ +{" simplei" hashf -1.2 \ No newline at end of file diff --git a/spec/tmp/marshalled-instance-object.out b/spec/tmp/marshalled-instance-object.out new file mode 100644 index 0000000..56a5662 --- /dev/null +++ b/spec/tmp/marshalled-instance-object.out @@ -0,0 +1,3 @@ +o:DumpTestUser "@idi" +@name"string name" @validF" +@opts0 \ No newline at end of file diff --git a/spec/tmp/marshalled-module.out b/spec/tmp/marshalled-module.out new file mode 100644 index 0000000..050764f --- /dev/null +++ b/spec/tmp/marshalled-module.out @@ -0,0 +1 @@ +cTestModule \ No newline at end of file diff --git a/spec/tmp/marshalled-nil.out b/spec/tmp/marshalled-nil.out new file mode 100644 index 0000000..e0dcc82 --- /dev/null +++ b/spec/tmp/marshalled-nil.out @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/spec/tmp/marshalled-one-byte-int-lower.out b/spec/tmp/marshalled-one-byte-int-lower.out new file mode 100644 index 0000000..592b54c --- /dev/null +++ b/spec/tmp/marshalled-one-byte-int-lower.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-one-byte-int-upper.out b/spec/tmp/marshalled-one-byte-int-upper.out new file mode 100644 index 0000000..f6df4c5 --- /dev/null +++ b/spec/tmp/marshalled-one-byte-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-one-byte-int.out b/spec/tmp/marshalled-one-byte-int.out new file mode 100644 index 0000000..f6df4c5 --- /dev/null +++ b/spec/tmp/marshalled-one-byte-int.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-one-byte-negative-int-lower.out b/spec/tmp/marshalled-one-byte-negative-int-lower.out new file mode 100644 index 0000000..c6d33e0 Binary files /dev/null and b/spec/tmp/marshalled-one-byte-negative-int-lower.out differ diff --git a/spec/tmp/marshalled-one-byte-negative-int-upper.out b/spec/tmp/marshalled-one-byte-negative-int-upper.out new file mode 100644 index 0000000..67c589f --- /dev/null +++ b/spec/tmp/marshalled-one-byte-negative-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-one-byte-positive-int-lower.out b/spec/tmp/marshalled-one-byte-positive-int-lower.out new file mode 100644 index 0000000..5aaade4 --- /dev/null +++ b/spec/tmp/marshalled-one-byte-positive-int-lower.out @@ -0,0 +1 @@ +i{ \ No newline at end of file diff --git a/spec/tmp/marshalled-one-byte-positive-int-upper.out b/spec/tmp/marshalled-one-byte-positive-int-upper.out new file mode 100644 index 0000000..1a92e1e --- /dev/null +++ b/spec/tmp/marshalled-one-byte-positive-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-regex.out b/spec/tmp/marshalled-regex.out new file mode 100644 index 0000000..89338a9 --- /dev/null +++ b/spec/tmp/marshalled-regex.out @@ -0,0 +1 @@ +/[A-Za-z0-9]+ \ No newline at end of file diff --git a/spec/tmp/marshalled-string.out b/spec/tmp/marshalled-string.out new file mode 100644 index 0000000..495fdd5 --- /dev/null +++ b/spec/tmp/marshalled-string.out @@ -0,0 +1 @@ +" a string \ No newline at end of file diff --git a/spec/tmp/marshalled-symbol.out b/spec/tmp/marshalled-symbol.out new file mode 100644 index 0000000..fd035bf --- /dev/null +++ b/spec/tmp/marshalled-symbol.out @@ -0,0 +1 @@ +:test_symbol \ No newline at end of file diff --git a/spec/tmp/marshalled-three-byte-negative-int-lower.out b/spec/tmp/marshalled-three-byte-negative-int-lower.out new file mode 100644 index 0000000..738d830 Binary files /dev/null and b/spec/tmp/marshalled-three-byte-negative-int-lower.out differ diff --git a/spec/tmp/marshalled-three-byte-negative-int-upper.out b/spec/tmp/marshalled-three-byte-negative-int-upper.out new file mode 100644 index 0000000..ec55db2 --- /dev/null +++ b/spec/tmp/marshalled-three-byte-negative-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-three-byte-positive-int-lower.out b/spec/tmp/marshalled-three-byte-positive-int-lower.out new file mode 100644 index 0000000..df869d7 Binary files /dev/null and b/spec/tmp/marshalled-three-byte-positive-int-lower.out differ diff --git a/spec/tmp/marshalled-three-byte-positive-int-upper.out b/spec/tmp/marshalled-three-byte-positive-int-upper.out new file mode 100644 index 0000000..2ac6928 --- /dev/null +++ b/spec/tmp/marshalled-three-byte-positive-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-true.out b/spec/tmp/marshalled-true.out new file mode 100644 index 0000000..d7e212e --- /dev/null +++ b/spec/tmp/marshalled-true.out @@ -0,0 +1 @@ +T \ No newline at end of file diff --git a/spec/tmp/marshalled-two-byte-negative-int-lower.out b/spec/tmp/marshalled-two-byte-negative-int-lower.out new file mode 100644 index 0000000..a3d09b6 Binary files /dev/null and b/spec/tmp/marshalled-two-byte-negative-int-lower.out differ diff --git a/spec/tmp/marshalled-two-byte-negative-int-upper.out b/spec/tmp/marshalled-two-byte-negative-int-upper.out new file mode 100644 index 0000000..b45e4c3 --- /dev/null +++ b/spec/tmp/marshalled-two-byte-negative-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-two-byte-positive-int-lower.out b/spec/tmp/marshalled-two-byte-positive-int-lower.out new file mode 100644 index 0000000..5098040 Binary files /dev/null and b/spec/tmp/marshalled-two-byte-positive-int-lower.out differ diff --git a/spec/tmp/marshalled-two-byte-positive-int-upper.out b/spec/tmp/marshalled-two-byte-positive-int-upper.out new file mode 100644 index 0000000..8ec3def --- /dev/null +++ b/spec/tmp/marshalled-two-byte-positive-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-zero-byte-int-lower.out b/spec/tmp/marshalled-zero-byte-int-lower.out new file mode 100644 index 0000000..592b54c --- /dev/null +++ b/spec/tmp/marshalled-zero-byte-int-lower.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-zero-byte-int-upper.out b/spec/tmp/marshalled-zero-byte-int-upper.out new file mode 100644 index 0000000..f6df4c5 --- /dev/null +++ b/spec/tmp/marshalled-zero-byte-int-upper.out @@ -0,0 +1 @@ +i \ No newline at end of file diff --git a/spec/tmp/marshalled-zero.out b/spec/tmp/marshalled-zero.out new file mode 100644 index 0000000..5e245f3 Binary files /dev/null and b/spec/tmp/marshalled-zero.out differ diff --git a/src/ruby-marshal.cr b/src/ruby-marshal.cr index 70538b3..0aacef2 100644 --- a/src/ruby-marshal.cr +++ b/src/ruby-marshal.cr @@ -18,7 +18,7 @@ module Ruby::Marshal end def self.load(klass : ::Class, source : IO) - klass.new( self._load(source.get_to_end) ) + klass.new( self._load(source.gets_to_end.to_slice) ) end # The first two bytes of the stream contain the major and minor version, each as @@ -60,4 +60,16 @@ module Ruby::Marshal return StreamObjectFactory.get(stream) end + def self.dump(obj) : ::Bytes + obj_bytes = obj.ruby_marshal_dump.dump + version_bytes.concat(obj_bytes || Null.new.dump) + end + + def self.version_bytes + bytestream = ::Bytes.new(2) + bytestream[0] = UInt8.new(MAJOR_VERSION) + bytestream[1] = UInt8.new(MINOR_VERSION) + bytestream + end + end diff --git a/src/ruby-marshal/exception.cr b/src/ruby-marshal/exception.cr index 7653dfc..1414f0c 100644 --- a/src/ruby-marshal/exception.cr +++ b/src/ruby-marshal/exception.cr @@ -2,5 +2,6 @@ module Ruby::Marshal class UnsupportedVersion < Exception ; end class InvalidMarshalData < Exception ; end + class UnsupportedMarshalClass < Exception ; end end \ No newline at end of file diff --git a/src/ruby-marshal/hash.cr b/src/ruby-marshal/hash.cr new file mode 100644 index 0000000..b14fc91 --- /dev/null +++ b/src/ruby-marshal/hash.cr @@ -0,0 +1,8 @@ +class Hash(K, V) + + def default_value + return (fetch("nonexistent") rescue nil) + fetch(nil) rescue nil + end + +end \ No newline at end of file diff --git a/src/ruby-marshal/mapping.cr b/src/ruby-marshal/mapping.cr new file mode 100644 index 0000000..08cfc0c --- /dev/null +++ b/src/ruby-marshal/mapping.cr @@ -0,0 +1,40 @@ +require "./exception" + +module Ruby::Marshal + + macro mapping(properties) + + def num_instance_vars + Ruby::Marshal::Integer.get({{ properties.size }}) + end + + def instance_vars + { + {% for prop, klass in properties %} + :"@{{ prop.id }}" => @{{ prop.id }}, + {% end %} + }.ruby_marshal_dump + end + + {% for prop, klass in properties %} + property :{{ prop.id }} + + def read_ruby_marshalled_{{ prop.id }}(obj : ::Ruby::Marshal::Object | ::Ruby::Marshal::Struct) : {{klass}} + {{ klass }}.cast(obj.read_raw_attr("{{ prop.id }}")) + end + {% end %} + + def initialize(obj : ::Ruby::Marshal::StreamObject) + {% for prop, klass in properties %} + if self.is_a?(::Struct) + @{{ prop.id }} = read_ruby_marshalled_{{ prop.id }}(obj.as(::Ruby::Marshal::Struct)).as({{ klass }}) + elsif self.is_a?(::Object) + @{{ prop.id }} = read_ruby_marshalled_{{ prop.id }}(obj.as(::Ruby::Marshal::Object)).as({{ klass }}) + else; raise Ruby::Marshal::UnsupportedMarshalClass.new + end + {% end %} + end + + end + +end diff --git a/src/ruby-marshal/ruby_marshal_dump.cr b/src/ruby-marshal/ruby_marshal_dump.cr new file mode 100644 index 0000000..484b211 --- /dev/null +++ b/src/ruby-marshal/ruby_marshal_dump.cr @@ -0,0 +1,73 @@ +class Object + def ruby_marshal_dump + Ruby::Marshal::Object.from(self) + end +end + +struct Nil + def ruby_marshal_dump + Ruby::Marshal::Null.new + end +end + +struct Bool + def ruby_marshal_dump + self ? Ruby::Marshal::True.new(self) : Ruby::Marshal::False.new(self) + end +end + +class Class + def ruby_marshal_dump + Ruby::Marshal::Class.new(self) + end +end + +struct Symbol + def ruby_marshal_dump + Ruby::Marshal::Symbol.new(self) + end +end + +struct Int + def ruby_marshal_dump + Ruby::Marshal::Integer.get(self) + end +end + +struct Float + def ruby_marshal_dump + Ruby::Marshal::Float.new(self) + end +end + +class String + def ruby_marshal_dump + Ruby::Marshal::String.new(self) + end +end + +class Array + def ruby_marshal_dump + Ruby::Marshal::Array.new(self) + end +end + +class Regex + def ruby_marshal_dump + Ruby::Marshal::Regex.new(self) + end +end + +class Hash + def ruby_marshal_dump + self.default_value ? + Ruby::Marshal::HashWithDefault.new(self) : + Ruby::Marshal::Hash.new(self) + end +end + +struct Struct + def ruby_marshal_dump + Ruby::Marshal::Struct.new(self) + end +end diff --git a/src/ruby-marshal/slice.cr b/src/ruby-marshal/slice.cr new file mode 100644 index 0000000..15e4ec9 --- /dev/null +++ b/src/ruby-marshal/slice.cr @@ -0,0 +1,12 @@ +struct Slice(T) + + # Cocatenate the passed slice onto the end of the slice + def concat(slice : ::Slice) + result = ::Slice(T).new(self.size + slice.size) + self.each_with_index { |b, i| result[i] = b } + slice.each_with_index { |b, i| result[i + self.size] = b } + return result + end + +end + diff --git a/src/ruby-marshal/stream_objects/array.cr b/src/ruby-marshal/stream_objects/array.cr index 8d3e248..0c9575e 100644 --- a/src/ruby-marshal/stream_objects/array.cr +++ b/src/ruby-marshal/stream_objects/array.cr @@ -5,14 +5,14 @@ module Ruby::Marshal class Array < StreamObject getter :data - alias RubyStreamArray = StreamObject | ::Regex | ::Bytes | ::Float64 | ::Bool | ::Int32 | ::String | ::Nil | ::Array(RubyStreamArray) | ::Hash(StreamObject, StreamObject) - @data : RubyStreamArray + @data : ::Array(Hash::RawHashObjects) @num_objects : Int32 + TYPE_BYTE = ::UInt8.new(0x5b) def initialize(stream : Bytes) array_length = Integer.get(stream) @num_objects = array_length.data - @data = ::Array(RubyStreamArray).new(@num_objects) + @data = ::Array(Hash::RawHashObjects).new(@num_objects) super(array_length.size) stream += @size read(stream) @@ -21,16 +21,43 @@ module Ruby::Marshal def read(stream : Bytes) obj_index = 0 - obj_size = 0 while(obj_index < @num_objects) object = StreamObjectFactory.get(stream) - @data.as(::Array(RubyStreamArray)) << object.data + @data << object.data obj_index += 1 stream += object.stream_size @size += object.stream_size end end + def initialize(array : ::Array) + obj_count = Integer.get(array.size) + @num_objects = obj_count.data + @data = ::Array(Hash::RawHashObjects).new(@num_objects) + s = obj_count.size + obj_index = 0 + while(obj_index < @num_objects) + object = array[obj_index].ruby_marshal_dump + @data.as(::Array(Hash::RawHashObjects)) << object.data + obj_index += 1 + s += object.stream_size + end + super(s) + end + + def dump + result = ::Bytes.new(1) + result[0] = TYPE_BYTE + result = result.concat(Integer.get(@num_objects).dump + 1) + obj_index = 0 + while(obj_index < @num_objects) + dumped_obj = @data[obj_index].ruby_marshal_dump.dump || ::Bytes.new(0) + result = result.concat(dumped_obj) + obj_index += 1 + end + result + end + end end diff --git a/src/ruby-marshal/stream_objects/bignum.cr b/src/ruby-marshal/stream_objects/bignum.cr index b3746b3..dfcd351 100644 --- a/src/ruby-marshal/stream_objects/bignum.cr +++ b/src/ruby-marshal/stream_objects/bignum.cr @@ -40,6 +40,12 @@ module Ruby::Marshal @size += @length.data * 2 end + def dump + #output = ::Bytes.new(1) + #output[0] = @type_byte + #bytestream.concat(output) + end + end end diff --git a/src/ruby-marshal/stream_objects/byte_sequence.cr b/src/ruby-marshal/stream_objects/byte_sequence.cr index 2d5411a..9319328 100644 --- a/src/ruby-marshal/stream_objects/byte_sequence.cr +++ b/src/ruby-marshal/stream_objects/byte_sequence.cr @@ -12,8 +12,29 @@ module Ruby::Marshal @data = stream[@length.size, @length.data] end + def initialize(str : ::String) + @length = Integer.get(str.size) + bytes = str.bytes + i = 0 + result = ::Bytes.new(bytes.size) + while(i < bytes.size) + result[i] = bytes[i] + i += 1 + end + @data = result + end + def stream_size - @length.data + @length.data + @length.size + end + + def dump : ::Bytes + result = @length.dump + if result + (result + 1).concat(@data) # strip the "i" type byte + else + ::Bytes.new(0).concat(@data) + end end end diff --git a/src/ruby-marshal/stream_objects/class.cr b/src/ruby-marshal/stream_objects/class.cr index 7f05d3c..0b31ecd 100644 --- a/src/ruby-marshal/stream_objects/class.cr +++ b/src/ruby-marshal/stream_objects/class.cr @@ -19,11 +19,31 @@ module Ruby::Marshal getter :data @data : ::String + @byte_sequence : ByteSequence + TYPE_BYTE = UInt8.new(0x63) def initialize(stream : Bytes) - source = ByteSequence.new(stream) - @data = ::String.new(source.data) - super(source.stream_size) + @byte_sequence = ByteSequence.new(stream) + @data = ::String.new(@byte_sequence.data) + super(@byte_sequence.stream_size) + end + + def initialize(klass : ::Class) + @data = klass.to_s + @byte_sequence = ByteSequence.new(@data) + super(@byte_sequence.stream_size) + end + + def initialize(mod : ::Module) + @data = mod.to_s + @byte_sequence = ByteSequence.new(@data) + super(@byte_sequence.stream_size) + end + + def dump + result = ::Bytes.new(1) + result[0] = TYPE_BYTE + result.concat(@byte_sequence.dump) end end diff --git a/src/ruby-marshal/stream_objects/extended_object.cr b/src/ruby-marshal/stream_objects/extended_object.cr index bdc9ed0..f79b9b1 100644 --- a/src/ruby-marshal/stream_objects/extended_object.cr +++ b/src/ruby-marshal/stream_objects/extended_object.cr @@ -19,6 +19,12 @@ module Ruby::Marshal @extended_by = StreamObjectFactory.get(stream) end + def dump + #output = ::Bytes.new(1) + #output[0] = @type_byte + #bytestream.concat(output) + end + end end diff --git a/src/ruby-marshal/stream_objects/false.cr b/src/ruby-marshal/stream_objects/false.cr index 3f76b20..b07f867 100644 --- a/src/ruby-marshal/stream_objects/false.cr +++ b/src/ruby-marshal/stream_objects/false.cr @@ -5,6 +5,7 @@ module Ruby::Marshal class False < StreamObject @data : Bool + @type_byte = UInt8.new(0x46) getter :data def initialize(stream : Bytes) @@ -12,9 +13,17 @@ module Ruby::Marshal @data = false end - def read(stream : Bytes) - # noop + def initialize(bool : ::Bool) + super(0x00) + @data = false + end + + def dump + output = ::Bytes.new(1) + output[0] = @type_byte + output end + end end diff --git a/src/ruby-marshal/stream_objects/float.cr b/src/ruby-marshal/stream_objects/float.cr index 36f6ee9..a94b28c 100644 --- a/src/ruby-marshal/stream_objects/float.cr +++ b/src/ruby-marshal/stream_objects/float.cr @@ -17,13 +17,27 @@ module Ruby::Marshal class Float < StreamObject @data : ::Float64 + @byte_sequence : ByteSequence getter :data + TYPE_BYTE = UInt8.new(0x66) def initialize(stream : Bytes) - source = ByteSequence.new(stream) - float_io = ::IO::Memory.new(source.data) + @byte_sequence = ByteSequence.new(stream) + float_io = ::IO::Memory.new(@byte_sequence.data) @data = ::Float64.new(float_io.to_s) - super(source.stream_size) + super(@byte_sequence.stream_size) + end + + def initialize(float : ::Float64) + @byte_sequence = ByteSequence.new(float.to_s) + @data = float + super(@byte_sequence.stream_size) + end + + def dump + result = ::Bytes.new(1) + result[0] = UInt8.new(TYPE_BYTE) + result.concat(@byte_sequence.dump) end end diff --git a/src/ruby-marshal/stream_objects/four_byte_negative_int.cr b/src/ruby-marshal/stream_objects/four_byte_negative_int.cr index bdc6f73..a35e227 100644 --- a/src/ruby-marshal/stream_objects/four_byte_negative_int.cr +++ b/src/ruby-marshal/stream_objects/four_byte_negative_int.cr @@ -4,12 +4,10 @@ module Ruby::Marshal class FourByteNegativeInt < Integer + SUB_TYPE_BYTE = UInt8.new(0xfc) + def initialize(stream : Bytes) super(Int32.new(0x04)) - read(stream) - end - - def read(stream : Bytes) stream += 1 data_bytes = Slice(UInt8).new(size) data_bytes.copy_from(stream.to_unsafe, size) @@ -24,6 +22,22 @@ module Ruby::Marshal @data = -(IO::ByteFormat::BigEndian.decode(Int32, padded_slice) + 1) end + def initialize(int : ::Int32) + super(Int32.new(0x02)) + @data = int + end + + def dump + output = ::Bytes.new(2) + output[0] = Integer::TYPE_BYTE + output[1] = SUB_TYPE_BYTE + data_slice = ::Bytes.new(4) + io = ::IO::Memory.new(0x04) + @data.to_io(io, ::IO::ByteFormat::LittleEndian) + io.rewind.read(data_slice) + output.concat(data_slice) + end + end end diff --git a/src/ruby-marshal/stream_objects/four_byte_positive_int.cr b/src/ruby-marshal/stream_objects/four_byte_positive_int.cr index 6e4197c..9445837 100644 --- a/src/ruby-marshal/stream_objects/four_byte_positive_int.cr +++ b/src/ruby-marshal/stream_objects/four_byte_positive_int.cr @@ -4,13 +4,27 @@ module Ruby::Marshal class FourBytePositiveInt < Integer + SUB_TYPE_BYTE = UInt8.new(0x04) + def initialize(stream : Bytes) super(Int32.new(0x04)) - read(stream) + @data = ::IO::ByteFormat::LittleEndian.decode(Int32, stream[1, size]) end - def read(stream : Bytes) - @data = ::IO::ByteFormat::LittleEndian.decode(Int32, stream[1, size]) + def initialize(int : ::Int32) + super(Int32.new(0x04)) + @data = int + end + + def dump + output = ::Bytes.new(2) + output[0] = Integer::TYPE_BYTE # 3 + output[1] = SUB_TYPE_BYTE # 3 + io = ::IO::Memory.new(0x04) + @data.to_io(io, ::IO::ByteFormat::LittleEndian) + data_slice = ::Bytes.new(4) + io.rewind.read(data_slice) + output.concat(data_slice) end end diff --git a/src/ruby-marshal/stream_objects/hash.cr b/src/ruby-marshal/stream_objects/hash.cr index 0117972..3a93bb1 100644 --- a/src/ruby-marshal/stream_objects/hash.cr +++ b/src/ruby-marshal/stream_objects/hash.cr @@ -1,5 +1,6 @@ require "./stream_object" require "./array" +require "../hash" require "./object_pointer" module Ruby::Marshal @@ -14,24 +15,25 @@ module Ruby::Marshal # all the pairs. class Hash < StreamObject - alias RawHashObjects = StreamObject | ::Regex | ::Bytes | ::Bool | ::Int32 | ::String | ::Nil | ::Array(Ruby::Marshal::Array::RubyStreamArray) | ::Hash(Ruby::Marshal::StreamObject, Ruby::Marshal::StreamObject) | ::Float64 | ::Hash(RawHashObjects, RawHashObjects) + alias RawHashObjects = ::Symbol | ::Float64 | ::Regex | ::Bytes | ::Bool | ::Int32 | ::String | ::Nil | ::Array(RawHashObjects) | ::Hash(RawHashObjects, RawHashObjects) - getter :data, :default_value - @data : ::Hash(StreamObject, StreamObject) + getter :data, :default_value, :num_keys + @data : ::Hash(RawHashObjects, RawHashObjects) # RawHashObjects # ::Hash(StreamObject, StreamObject) @num_keys : Integer - @default_value : StreamObject | Null + @default_value : RawHashObjects + TYPE_BYTE = UInt8.new(0x7b) def initialize(stream : Bytes) - @default_value = Null.new + @size = 0 + @default_value = nil @num_keys = Integer.get(stream) - @data = ::Hash(StreamObject, StreamObject).new + @data = ::Hash(RawHashObjects, RawHashObjects).new stream += @num_keys.size super(@num_keys.size) read(stream) Heap.add(self) end - # instantiate the class if it exists and assign to @data def read(stream : Bytes) i = 0 while(i < @num_keys.data) @@ -41,10 +43,25 @@ module Ruby::Marshal instance_var_value = StreamObjectFactory.get(stream) stream += instance_var_value.stream_size @size += instance_var_value.stream_size - @data[instance_var_name] = instance_var_value + @data[instance_var_name.data] = instance_var_value.data i += 1 end - return stream + stream + end + + def initialize(hash : ::Hash(RawHashObjects, RawHashObjects)) + @size = 0 + @default_value = nil + @data = ::Hash(RawHashObjects, RawHashObjects).new + @num_keys = Integer.get(hash.keys.size) + super(@num_keys.size) + hash.each do |key, value| + instance_var_name = key.ruby_marshal_dump + @size += instance_var_name.stream_size + instance_var_value = value.ruby_marshal_dump + @size += instance_var_value.stream_size + @data[instance_var_name.data] = instance_var_value.data + end end def each(&block) @@ -55,10 +72,10 @@ module Ruby::Marshal macro add_hash_accessor(klass) def [](requested_key : {{klass}}) - result = Null.new + result = nil @data.each do |(k, v)| - if(k.data.class == {{klass}}) - if k.data.as({{klass}}) == requested_key + if(k.class == {{klass}}) + if k.as({{klass}}) == requested_key result = v break end @@ -71,18 +88,16 @@ module Ruby::Marshal add_hash_accessor ::String add_hash_accessor ::Int32 - def raw_hash : ::Hash(RawHashObjects, RawHashObjects) - unless @default_value.data.nil? - raw_hash = ::Hash(RawHashObjects, RawHashObjects).new { @default_value.data } - else - raw_hash = ::Hash(RawHashObjects, RawHashObjects).new - end - @data.each do |(k, v)| - key = (k.class == Ruby::Marshal::Hash) ? k.as(Hash).raw_hash : k.data - value = (v.class == Ruby::Marshal::Hash) ? v.as(Hash).raw_hash : v.data - raw_hash[key] = value + def dump + output = ::Bytes.new(1) + output[0] = TYPE_BYTE + output = output.concat(@num_keys.dump + 1) + null = Null.new + @data.each do |(key, value)| + output = output.concat(key.ruby_marshal_dump.dump || null.dump) + output = output.concat(value.ruby_marshal_dump.dump || null.dump) end - raw_hash + output end end diff --git a/src/ruby-marshal/stream_objects/hash_with_default.cr b/src/ruby-marshal/stream_objects/hash_with_default.cr index 7afaa71..7e0a625 100644 --- a/src/ruby-marshal/stream_objects/hash_with_default.cr +++ b/src/ruby-marshal/stream_objects/hash_with_default.cr @@ -7,19 +7,44 @@ module Ruby::Marshal # all the pairs. class HashWithDefault < Hash + TYPE_BYTE = UInt8.new(0x7d) + def initialize(stream : Bytes) - @default_value = Null.new + @num_keys = Integer.get(0) + @default_value = nil super(stream) - data_with_default = ::Hash(StreamObject, StreamObject).new { @default_value } + data_with_default = ::Hash(RawHashObjects, RawHashObjects).new { @default_value.ruby_marshal_dump.data } data_with_default.merge!(@data) @data = data_with_default - @size += @default_value.stream_size + @size += @default_value.ruby_marshal_dump.stream_size end def read(stream : Bytes) stream = super(stream) # Get the default value - @default_value = StreamObjectFactory.get(stream) + @default_value = StreamObjectFactory.get(stream).data + end + + def initialize(hash : ::Hash(RawHashObjects, RawHashObjects)) + @size = 0 + tmp_default = hash.default_value + @default_value = tmp_default.ruby_marshal_dump.data + @data = ::Hash(RawHashObjects, RawHashObjects).new + @num_keys = Integer.get(hash.keys.size) + super(hash) + hash.each do |key, value| + instance_var_name = key.ruby_marshal_dump + @size += instance_var_name.stream_size + instance_var_value = value.ruby_marshal_dump + @size += instance_var_value.stream_size + @data[instance_var_name.data] = instance_var_value.data + end + end + + def dump + output = super() + output[0] = TYPE_BYTE + output.concat(@default_value.ruby_marshal_dump.dump || ::Bytes.new(0)) end end diff --git a/src/ruby-marshal/stream_objects/instance_object.cr b/src/ruby-marshal/stream_objects/instance_object.cr index cb89f2b..a0054cd 100644 --- a/src/ruby-marshal/stream_objects/instance_object.cr +++ b/src/ruby-marshal/stream_objects/instance_object.cr @@ -13,39 +13,40 @@ module Ruby::Marshal getter :data @num_instance_variables : Integer - @instance_variables : ::Hash(::String, StreamObject) + @instance_variables : Hash def initialize(stream : Bytes) super(0x00) @data = StreamObjectFactory.get(stream) - stream += 1 + @data.as(Ruby::Marshal::StreamObject).stream_size - @num_instance_variables = Integer.get(stream) - @instance_variables = ::Hash(::String, StreamObject).new - @size = 1 + @data.as(Ruby::Marshal::StreamObject).stream_size + @num_instance_variables.size - stream += @num_instance_variables.size - read(stream) + stream += @data.stream_size + @instance_variables = Hash.new(stream) + @num_instance_variables = @instance_variables.num_keys + @size = @instance_variables.stream_size + @data.stream_size - 1 Heap.add(self) end - # read instance variables - def read(stream : Bytes) - i = 0 - while(i < @num_instance_variables.data) - instance_var_name = StreamObjectFactory.get(stream) - stream += instance_var_name.stream_size - @size += instance_var_name.stream_size - instance_var_value = StreamObjectFactory.get(stream) - stream += instance_var_value.stream_size - @size += instance_var_value.stream_size - @instance_variables[instance_var_name.data.as(::String)] = instance_var_value - i += 1 - end + def initialize(num_vars : Integer, vars : Hash) + super(0x00) + @num_instance_variables = num_vars + @instance_variables = vars + @data = vars.dump + @size = @num_instance_variables.stream_size + @instance_variables.stream_size - 1 + end + + def self.from(obj : ::Object) + InstanceObject.new(obj.num_instance_vars, obj.instance_vars) end def data @data.data end + def dump + #output = ::Bytes.new(1) + #output[0] = @type_byte + #bytestream.concat(output) + end + end end diff --git a/src/ruby-marshal/stream_objects/integer.cr b/src/ruby-marshal/stream_objects/integer.cr index 38c5cdf..ef8ae07 100644 --- a/src/ruby-marshal/stream_objects/integer.cr +++ b/src/ruby-marshal/stream_objects/integer.cr @@ -7,12 +7,39 @@ module Ruby::Marshal @data : Int32 getter :data + TYPE_BYTE = UInt8.new(0x69) def initialize(size : Int32) super(size) @data = Int32.new(0x00) end + def self.get(int : ::UInt8 |::Int8 | ::UInt16 | ::Int16 | ::UInt32 | ::Int32 | ::UInt64 | ::Int64) : StreamObject + if int == 0 + return ZeroByteInt.new + elsif int >= -122 && int <= 122 + return OneByteInt.new(int) + elsif int <= -123 && int >= -256 + return OneByteNegativeInt.new(int) + elsif int >= 123 && int <= 255 + return OneBytePositiveInt.new(int) + elsif int <= -257 && int >= -65_536 + return TwoByteNegativeInt.new(int) + elsif int >= 256 && int <= 65_535 + return TwoBytePositiveInt.new(int) + elsif int <= -65_537 && int >= -16_777_216 + return ThreeByteNegativeInt.new(int) + elsif int >= 65_536 && int <= 16_777_215 + return ThreeBytePositiveInt.new(int) + elsif int <= -16_777_217 && int >= -1_073_741_824 + return FourByteNegativeInt.new(int) + elsif int >= 16_777_216 && int <= 1_073_741_823 + return FourBytePositiveInt.new(int) + else + return ZeroByteInt.new + end + end + def self.get(stream : Bytes) case stream.first when 0x00 diff --git a/src/ruby-marshal/stream_objects/module.cr b/src/ruby-marshal/stream_objects/module.cr index cb105b0..1d8f67e 100644 --- a/src/ruby-marshal/stream_objects/module.cr +++ b/src/ruby-marshal/stream_objects/module.cr @@ -35,6 +35,12 @@ module Ruby::Marshal @size += @length.data end + def dump + #output = ::Bytes.new(1) + #output[0] = @type_byte + #bytestream.concat(output) + end + end end diff --git a/src/ruby-marshal/stream_objects/null.cr b/src/ruby-marshal/stream_objects/null.cr index 9fbcfed..093b0d9 100644 --- a/src/ruby-marshal/stream_objects/null.cr +++ b/src/ruby-marshal/stream_objects/null.cr @@ -6,6 +6,7 @@ module Ruby::Marshal getter :data @data : ::Nil + @type_byte = UInt8.new(0x30) def initialize(stream : Bytes) super(Int32.new(0x00)) @@ -17,8 +18,10 @@ module Ruby::Marshal @data = nil end - def read(stream : Bytes) - # noop + def dump + output = ::Bytes.new(1) + output[0] = @type_byte + output end end diff --git a/src/ruby-marshal/stream_objects/object.cr b/src/ruby-marshal/stream_objects/object.cr index b1aae49..2481819 100644 --- a/src/ruby-marshal/stream_objects/object.cr +++ b/src/ruby-marshal/stream_objects/object.cr @@ -13,37 +13,33 @@ module Ruby::Marshal # variable names. class Object < StreamObject - getter :data + getter :data, :instance_variables @class_name : Symbol @num_instance_variables : Integer - @instance_variables : ::Hash(::String, StreamObject) + @data : ::Hash(Hash::RawHashObjects, Hash::RawHashObjects) + @instance_variables : Hash + TYPE_BYTE = UInt8.new(0x6f) def initialize(stream : Bytes) super(0x00) @class_name = StreamObjectFactory.get(stream).as(Symbol) - @data = Null.new(stream) stream += @class_name.stream_size - @num_instance_variables = Integer.get(stream) - @instance_variables = ::Hash(::String, StreamObject).new - stream += @num_instance_variables.size - @size = @num_instance_variables.size + @class_name.stream_size - read(stream) + @instance_variables = Hash.new(stream) + @data = @instance_variables.data + @num_instance_variables = @instance_variables.num_keys + @size = @instance_variables.stream_size + @class_name.stream_size - 1 Heap.add(self) end - def read(stream : Bytes) - i = 0 - while(i < @num_instance_variables.data) - instance_var_name = StreamObjectFactory.get(stream) - stream += instance_var_name.stream_size - @size += instance_var_name.stream_size - instance_var_value = StreamObjectFactory.get(stream) - stream += instance_var_value.stream_size - @size += instance_var_value.stream_size - @instance_variables[instance_var_name.data.as(::String)] = instance_var_value - i += 1 - end - return stream + def initialize(@num_instance_variables : Integer, @instance_variables : Hash, @class_name : Symbol) + super(0x00) + @data = @instance_variables.data + @size = @num_instance_variables.stream_size + @instance_variables.stream_size - 1 + Heap.add(self) + end + + def self.from(obj : ::Object) + Object.new(obj.num_instance_vars, obj.instance_vars, Symbol.new(obj.class.to_s)) end def populate_class(klass : ::Object) @@ -51,42 +47,22 @@ module Ruby::Marshal end def read_attr(name : ::String, raw = false) - key = @instance_variables.has_key?(name) ? name : "@#{name}" - attr = @instance_variables.has_key?(key) ? @instance_variables[key] : nil - if(raw && !attr.nil?) - if (attr.is_a?(::Hash)) - return attr.raw_hash - else - return attr.data - end - else - return attr - end + key = @data.has_key?(name) ? name : "@#{name}" + @data.has_key?(key) ? @data[key] : nil end def read_raw_attr(name : ::String) read_attr(name, true) end - end - -end - -macro ruby_marshal_properties(prop_hash) - - {% for prop, klass in prop_hash %} - @{{ prop.id }} : {{ klass }} - - def read_ruby_marshalled_{{ prop.id }}(marshalled_object : ::Ruby::Marshal::Object) : {{klass}} - marshalled_object.read_raw_attr("{{ prop.id }}").as({{ klass }}) + def dump + result = ::Bytes.new(1) + result[0] = TYPE_BYTE + result = result.concat(@class_name.dump) + .concat(@num_instance_variables.dump + 2) + .concat(@instance_variables.dump + 1) end - {% end %} - def initialize(marshalled_object : ::Ruby::Marshal::StreamObject) - marshalled_object = marshalled_object.as(::Ruby::Marshal::Object) - {% for prop, klass in prop_hash %} - @{{ prop.id }} = read_ruby_marshalled_{{ prop.id }}(marshalled_object).as({{ klass }}) - {% end %} end end diff --git a/src/ruby-marshal/stream_objects/object_pointer.cr b/src/ruby-marshal/stream_objects/object_pointer.cr index 3d2f4ad..f8e1b46 100644 --- a/src/ruby-marshal/stream_objects/object_pointer.cr +++ b/src/ruby-marshal/stream_objects/object_pointer.cr @@ -5,20 +5,20 @@ module Ruby::Marshal class ObjectPointer < StreamObject - alias RubyStreamObjects = StreamObject | ::Bytes | ::Regex | ::Bool | ::Int32 | ::String | ::Nil | ::Array(Ruby::Marshal::Array::RubyStreamArray) | ::Hash(Ruby::Marshal::StreamObject, Ruby::Marshal::StreamObject) | ::Float64 - @data : RubyStreamObjects + @data : Hash::RawHashObjects getter :data def initialize(stream : Bytes) - @data = "" pointer_index = Integer.get(stream) super(pointer_index.stream_size) @heap_index = Int32.new(pointer_index.data) - read(stream) + @data = Heap.get_obj(@heap_index).data end - def read(stream : Bytes) - @data = Heap.get_obj(@heap_index).data + def dump + #output = ::Bytes.new(1) + #output[0] = @type_byte + #bytestream.concat(output) end end diff --git a/src/ruby-marshal/stream_objects/one_byte_int.cr b/src/ruby-marshal/stream_objects/one_byte_int.cr index 6f9616a..d83ff8b 100644 --- a/src/ruby-marshal/stream_objects/one_byte_int.cr +++ b/src/ruby-marshal/stream_objects/one_byte_int.cr @@ -6,10 +6,6 @@ module Ruby::Marshal def initialize(stream : Bytes) super(Int32.new(0x01)) - read(stream) - end - - def read(stream : Bytes) data_bytes = stream[0, size] # negative if first bit is 1 if ((data_bytes[0] & (1 << 7)) != 0) @@ -20,13 +16,26 @@ module Ruby::Marshal # subtracting 5 from the value. @data = Int32.new(Int8.new(data_bytes[0] - 5)) end - @data + end + + def initialize(int : ::Int32) + super(Int32.new(0x01)) + @data = int + end + + def dump + output = ::Bytes.new(2) + output[0] = UInt8.new(Integer::TYPE_BYTE) + if @data < 0 + output[1] = UInt8.new((@data - 5)) | (1 << 7) + else + output[1] = UInt8.new(@data + 5) + end + output end def stream_size - # 1 for 8-bit identifier "i" - # 1 for the 1 byte value - return 2 + 2 end end diff --git a/src/ruby-marshal/stream_objects/one_byte_negative_int.cr b/src/ruby-marshal/stream_objects/one_byte_negative_int.cr index e29693a..933c626 100644 --- a/src/ruby-marshal/stream_objects/one_byte_negative_int.cr +++ b/src/ruby-marshal/stream_objects/one_byte_negative_int.cr @@ -4,18 +4,29 @@ module Ruby::Marshal class OneByteNegativeInt < Integer + SUB_TYPE_BYTE = UInt8.new(0xff) + def initialize(stream : Bytes) super(Int32.new(0x01)) - read(stream) - end - - def read(stream : Bytes) data_bytes = stream[1, size] complement_slice = Slice(UInt8).new(size) data_bytes.each_with_index { |byte, index| complement_slice[index] = ~byte } @data = -(Int32.new(complement_slice.join) + 1) end + def initialize(int : ::Int32) + super(Int32.new(0x01)) + @data = int + end + + def dump + output = ::Bytes.new(3) + output[0] = UInt8.new(Integer::TYPE_BYTE) + output[1] = UInt8.new(SUB_TYPE_BYTE) + output[2] = ~UInt8.new(-@data - 1) + output + end + end end diff --git a/src/ruby-marshal/stream_objects/one_byte_positive_int.cr b/src/ruby-marshal/stream_objects/one_byte_positive_int.cr index 0f1d51e..53fa635 100644 --- a/src/ruby-marshal/stream_objects/one_byte_positive_int.cr +++ b/src/ruby-marshal/stream_objects/one_byte_positive_int.cr @@ -4,13 +4,24 @@ module Ruby::Marshal class OneBytePositiveInt < Integer + SUB_TYPE_BYTE = UInt8.new(0x01) + def initialize(stream : Bytes) super(Int32.new(0x01)) - read(stream) + @data = Int32.new(stream[1, size].join) end - def read(stream : Bytes) - @data = Int32.new(stream[1, size].join) + def initialize(int : ::Int32) + super(Int32.new(0x01)) + @data = int + end + + def dump + output = ::Bytes.new(3) + output[0] = UInt8.new(Integer::TYPE_BYTE) + output[1] = UInt8.new(SUB_TYPE_BYTE) + output[2] = UInt8.new(@data) + output end end diff --git a/src/ruby-marshal/stream_objects/regex.cr b/src/ruby-marshal/stream_objects/regex.cr index 981381f..cea414c 100644 --- a/src/ruby-marshal/stream_objects/regex.cr +++ b/src/ruby-marshal/stream_objects/regex.cr @@ -4,7 +4,7 @@ module Ruby::Marshal # “/” represents a regular expression. Following the # type byte is a byte sequence containing the regular - # expression source. Following the type byte is a byte + # expression @byte_sequence. Following the type byte is a byte # containing the regular expression options (case- # insensitive, etc.) as a signed 8-bit value. # @@ -17,13 +17,27 @@ module Ruby::Marshal getter :data @data : ::Regex + @byte_sequence : ByteSequence + TYPE_BYTE = UInt8.new(0x2f) def initialize(stream : Bytes, @data : ::Regex = ::Regex.new("")) - source = ByteSequence.new(stream) + @byte_sequence = ByteSequence.new(stream) # The flags are a lie. Ruby does not marshal the option data :( #flags = ByteSequence.new(stream) - @data = ::Regex.new(::String.new(source.data)) - super(source.stream_size + source.length.size) + @data = ::Regex.new(::String.new(@byte_sequence.data)) + super(@byte_sequence.stream_size) + end + + def initialize(regex : ::Regex) + @byte_sequence = ByteSequence.new(regex.source) + @data = regex + super(@byte_sequence.stream_size) + end + + def dump + result = ::Bytes.new(1) + result[0] = TYPE_BYTE + result.concat(@byte_sequence.dump) end end diff --git a/src/ruby-marshal/stream_objects/string.cr b/src/ruby-marshal/stream_objects/string.cr index a163a90..2d1b181 100644 --- a/src/ruby-marshal/stream_objects/string.cr +++ b/src/ruby-marshal/stream_objects/string.cr @@ -11,14 +11,28 @@ module Ruby::Marshal getter :data @data : ::String + @byte_sequence : ByteSequence + TYPE_BYTE = UInt8.new(0x22) def initialize(stream : Bytes) - source = ByteSequence.new(stream) - @data = ::String.new(source.data) - super(source.stream_size) + @byte_sequence = ByteSequence.new(stream) + @data = ::String.new(@byte_sequence.data) + super(@byte_sequence.stream_size) Heap.add(self) end + def initialize(string : ::String) + @byte_sequence = ByteSequence.new(string) + @data = string + super(@byte_sequence.stream_size) + end + + def dump + result = ::Bytes.new(1) + result[0] = TYPE_BYTE + result.concat(@byte_sequence.dump) + end + end end diff --git a/src/ruby-marshal/stream_objects/struct.cr b/src/ruby-marshal/stream_objects/struct.cr index 0421ba9..d31bc6c 100644 --- a/src/ruby-marshal/stream_objects/struct.cr +++ b/src/ruby-marshal/stream_objects/struct.cr @@ -18,60 +18,54 @@ module Ruby::Marshal # marshaled struct an exception should be raised. class Struct < StreamObject - getter :data + getter :data, :instance_variables @struct_name : Symbol + @data : ::Hash(Hash::RawHashObjects, Hash::RawHashObjects) @num_members : Integer - @members : ::Hash(::String, StreamObject) + @instance_variables : Hash + TYPE_BYTE = UInt8.new(0x53) def initialize(stream : Bytes) super(0x00) - @data = Null.new @struct_name = StreamObjectFactory.get(stream).as(Symbol) stream += @struct_name.stream_size - @num_members = Integer.get(stream) - @members = ::Hash(::String, StreamObject).new - stream += @num_members.size - @size = @num_members.size + @struct_name.stream_size - read(stream) + @instance_variables = Hash.new(stream) + @data = @instance_variables.data + @num_members = @instance_variables.num_keys + @size = @instance_variables.size + @struct_name.stream_size - 1 Heap.add(self) end - def read(stream : Bytes) - i = 0 - while(i < @num_members.data) - instance_var_name = StreamObjectFactory.get(stream) - stream += instance_var_name.stream_size - @size += instance_var_name.stream_size - instance_var_value = StreamObjectFactory.get(stream) - stream += instance_var_value.stream_size - @size += instance_var_value.stream_size - @members[instance_var_name.data.as(::String)] = instance_var_value - i += 1 - end - end - def populate_class(klass : ::Object) klass.new(self) end def read_attr(name : ::String, raw = false) - key = @members.has_key?(name) ? name : "@#{name}" - attr = @members.has_key?(key) ? @members[key] : nil - if(raw && !attr.nil?) - if (attr.is_a?(::Hash)) - return attr.raw_hash - else - return attr.data - end - else - return attr - end + key = @data.has_key?(name) ? name : "@#{name}" + @data.has_key?(key) ? @data[key] : nil end def read_raw_attr(name : ::String) read_attr(name, true) end + def initialize(str : ::Struct) + @size = 0 + super(@size) + @num_members = str.num_instance_vars + @instance_variables = str.instance_vars + @data = @instance_variables.data + @struct_name = Symbol.new(str.class.to_s) + end + + def dump + output = ::Bytes.new(1) + output[0] = TYPE_BYTE + output = output.concat(@struct_name.dump) + .concat(@num_members.dump + 2) + .concat(@instance_variables.dump + 1) + end + end end diff --git a/src/ruby-marshal/stream_objects/symbol.cr b/src/ruby-marshal/stream_objects/symbol.cr index b84cf7e..ef8a1e0 100644 --- a/src/ruby-marshal/stream_objects/symbol.cr +++ b/src/ruby-marshal/stream_objects/symbol.cr @@ -6,23 +6,26 @@ module Ruby::Marshal class Symbol < StreamObject getter :data + @type_byte = UInt8.new(0x3a) + @byte_sequence : ByteSequence def initialize(stream : Bytes) - @data = "" - symbol_length = Integer.get(stream) - @symbol_length = symbol_length.size.as(Int32) - super(symbol_length.data) - stream += @symbol_length - read(stream) + @byte_sequence = ByteSequence.new(stream) + @data = ::String.new(@byte_sequence.data) + super(@byte_sequence.stream_size) Heap.add(self) end - def read(stream : Bytes) - @data = ::String.new(stream[0, size]) + def initialize(sym : ::Symbol | ::String) + @data = sym.to_s + @byte_sequence = ByteSequence.new(@data) + super(@byte_sequence.stream_size) end - def stream_size - 1 + @symbol_length + size + def dump + output = ::Bytes.new(1) + output[0] = @type_byte + output.concat(@byte_sequence.dump) end end diff --git a/src/ruby-marshal/stream_objects/symbol_pointer.cr b/src/ruby-marshal/stream_objects/symbol_pointer.cr index cb5a7fc..aee5bab 100644 --- a/src/ruby-marshal/stream_objects/symbol_pointer.cr +++ b/src/ruby-marshal/stream_objects/symbol_pointer.cr @@ -19,6 +19,12 @@ module Ruby::Marshal @data = Heap.get_sym(@heap_index).data end + def dump + #output = ::Bytes.new(1) + #output[0] = @type_byte + #bytestream.concat(output) + end + end end diff --git a/src/ruby-marshal/stream_objects/three_byte_negative_int.cr b/src/ruby-marshal/stream_objects/three_byte_negative_int.cr index 6967112..ce11ba3 100644 --- a/src/ruby-marshal/stream_objects/three_byte_negative_int.cr +++ b/src/ruby-marshal/stream_objects/three_byte_negative_int.cr @@ -4,12 +4,10 @@ module Ruby::Marshal class ThreeByteNegativeInt < Integer + SUB_TYPE_BYTE = UInt8.new(0xfd) + def initialize(stream : Bytes) super(Int32.new(0x03)) - read(stream) - end - - def read(stream : Bytes) stream += 1 data_bytes = Slice(UInt8).new(size) data_bytes.copy_from(stream.to_unsafe, size) @@ -23,6 +21,22 @@ module Ruby::Marshal @data = -(IO::ByteFormat::BigEndian.decode(Int32, padded_slice) + 1) end + def initialize(int : ::Int32) + super(Int32.new(0x02)) + @data = int + end + + def dump + output = ::Bytes.new(2) + output[0] = Integer::TYPE_BYTE + output[1] = SUB_TYPE_BYTE + data_slice = ::Bytes.new(3) + io = ::IO::Memory.new(0x03) + @data.to_io(io, ::IO::ByteFormat::LittleEndian) + io.rewind.read(data_slice) + output.concat(data_slice) + end + end end diff --git a/src/ruby-marshal/stream_objects/three_byte_positive_int.cr b/src/ruby-marshal/stream_objects/three_byte_positive_int.cr index b80140d..61a839f 100644 --- a/src/ruby-marshal/stream_objects/three_byte_positive_int.cr +++ b/src/ruby-marshal/stream_objects/three_byte_positive_int.cr @@ -4,12 +4,11 @@ module Ruby::Marshal class ThreeBytePositiveInt < Integer + SUB_TYPE_BYTE = UInt8.new(0x03) + def initialize(stream : Bytes) + @data = Int32.new(0x00) super(Int32.new(0x03)) - read(stream) - end - - def read(stream : Bytes) stream += 1 data_bytes = Slice(UInt8).new(size) data_bytes.copy_from(stream.to_unsafe, size) @@ -21,6 +20,22 @@ module Ruby::Marshal @data = ::IO::ByteFormat::BigEndian.decode(Int32, padded_slice) end + def initialize(int : ::Int32) + super(Int32.new(0x03)) + @data = int + end + + def dump + output = ::Bytes.new(2) + output[0] = Integer::TYPE_BYTE # 3 + output[1] = SUB_TYPE_BYTE # 3 + io = ::IO::Memory.new(0x03) + @data.to_io(io, ::IO::ByteFormat::LittleEndian) + data_slice = ::Bytes.new(3) + io.rewind.read(data_slice) + output.concat(data_slice) + end + end end diff --git a/src/ruby-marshal/stream_objects/true.cr b/src/ruby-marshal/stream_objects/true.cr index 1089e46..1f9d2fb 100644 --- a/src/ruby-marshal/stream_objects/true.cr +++ b/src/ruby-marshal/stream_objects/true.cr @@ -5,6 +5,7 @@ module Ruby::Marshal class True < StreamObject @data : Bool + @type_byte = UInt8.new(0x54) getter :data def initialize(stream : Bytes) @@ -12,9 +13,17 @@ module Ruby::Marshal @data = true end - def read(stream : Bytes) - # noop + def initialize(bool : ::Bool) + super(0x00) + @data = true + end + + def dump + output = ::Bytes.new(1) + output[0] = @type_byte + output end + end end diff --git a/src/ruby-marshal/stream_objects/two_byte_negative_int.cr b/src/ruby-marshal/stream_objects/two_byte_negative_int.cr index f960f43..82829ca 100644 --- a/src/ruby-marshal/stream_objects/two_byte_negative_int.cr +++ b/src/ruby-marshal/stream_objects/two_byte_negative_int.cr @@ -4,12 +4,10 @@ module Ruby::Marshal class TwoByteNegativeInt < Integer + SUB_TYPE_BYTE = UInt8.new(0xfe) + def initialize(stream : Bytes) super(Int32.new(0x02)) - read(stream) - end - - def read(stream : Bytes) stream += 1 data_bytes = Slice(UInt8).new(size) data_bytes.copy_from(stream.to_unsafe, size) @@ -22,6 +20,22 @@ module Ruby::Marshal @data = -(IO::ByteFormat::BigEndian.decode(Int32, padded_slice) + 1) end + def initialize(int : ::Int32) + super(Int32.new(0x02)) + @data = int + end + + def dump + output = ::Bytes.new(2) + output[0] = Integer::TYPE_BYTE + output[1] = SUB_TYPE_BYTE + data_slice = ::Bytes.new(2) + io = ::IO::Memory.new(0x02) + @data.to_io(io, ::IO::ByteFormat::LittleEndian) + io.rewind.read(data_slice) + output.concat(data_slice) + end + end end diff --git a/src/ruby-marshal/stream_objects/two_byte_positive_int.cr b/src/ruby-marshal/stream_objects/two_byte_positive_int.cr index 2cb7b62..6799849 100644 --- a/src/ruby-marshal/stream_objects/two_byte_positive_int.cr +++ b/src/ruby-marshal/stream_objects/two_byte_positive_int.cr @@ -4,13 +4,27 @@ module Ruby::Marshal class TwoBytePositiveInt < Integer + SUB_TYPE_BYTE = UInt8.new(0x02) + def initialize(stream : Bytes) super(Int32.new(0x02)) - read(stream) + @data = Int32.new(::IO::ByteFormat::LittleEndian.decode(UInt16, stream[1, size])) end - def read(stream : Bytes) - @data = Int32.new(::IO::ByteFormat::LittleEndian.decode(UInt16, stream[1, size])) + def initialize(int : ::Int32) + super(Int32.new(0x02)) + @data = int + end + + def dump + output = ::Bytes.new(2) + output[0] = UInt8.new(Integer::TYPE_BYTE) + output[1] = UInt8.new(SUB_TYPE_BYTE) + io = ::IO::Memory.new(0x02) + @data.to_io(io, ::IO::ByteFormat::LittleEndian) + data_slice = ::Bytes.new(2) + io.rewind.read(data_slice) + output.concat(data_slice) end end diff --git a/src/ruby-marshal/stream_objects/user_class.cr b/src/ruby-marshal/stream_objects/user_class.cr index bde6306..bcede63 100644 --- a/src/ruby-marshal/stream_objects/user_class.cr +++ b/src/ruby-marshal/stream_objects/user_class.cr @@ -9,20 +9,22 @@ module Ruby::Marshal class UserClass < StreamObject getter :data, :class_name - @data : StreamObject + @data : Hash::RawHashObjects @class_name : Symbol def initialize(stream : Bytes) - @data = Null.new @class_name = StreamObjectFactory.get(stream).as(Symbol) super(@class_name.stream_size) stream += @class_name.stream_size - read(stream) + obj = StreamObjectFactory.get(stream) + @size += obj.stream_size + @data = obj.data end - def read(stream : Bytes) - @data = StreamObjectFactory.get(stream) - @size += @data.stream_size + def dump + #output = ::Bytes.new(1) + #output[0] = @type_byte + #bytestream.concat(output) end end diff --git a/src/ruby-marshal/stream_objects/zero_byte_int.cr b/src/ruby-marshal/stream_objects/zero_byte_int.cr index 71fe6d1..d8b0ff2 100644 --- a/src/ruby-marshal/stream_objects/zero_byte_int.cr +++ b/src/ruby-marshal/stream_objects/zero_byte_int.cr @@ -6,11 +6,17 @@ module Ruby::Marshal def initialize(stream : Bytes) super(Int32.new(0x01)) - read(stream) end - def read(stream : Bytes) - # noop + def initialize + super(Int32.new(0x01)) + end + + def dump + output = ::Bytes.new(2) + output[0] = UInt8.new(Integer::TYPE_BYTE) + output[1] = UInt8.new(0x00) + output end end