use v6; constant u8 = uint8; constant u16 = uint16; constant u32 = uint32; constant u64 = uint64; constant i8 = int8; constant i16 = int16; constant i32 = int32; constant i64 = int64; constant f32 = num32; constant f64 = num64; # byte # uint # int # num # str my SetHash $attr-is-no-serialize; BEGIN { $attr-is-no-serialize = SetHash.new; } multi sub trait_mod:(Attribute $a, :$no-serialize) { $attr-is-no-serialize{$a.WHICH.Str} = True; } sub attr-is-no-serialize(Attribute $a --> Bool) { $attr-is-no-serialize{$a.WHICH.Str} } sub MAIN() { class Foo { has u64 $.bar = 0x11_22_33_44_55_66_77_88; has Int $.baz is no-serialize = 1; has Str $.qux = 'ABCD'; has f32 $float is no-serialize = 1.0e0; # don't know how to turn this into bytes has u8 @.array = 0xAA, 0xBB, 0xCC, 0xDD; } constant Write-File = False; if Write-File { my buf8 $buf .= new; write-type($buf, Foo.new); write-type($buf, Foo.new(qux => 'WXYZ')); my Foo $custom .= new( :bar(0xFF), :qux('QUX'), :array(0x44, 0x33, 0x22, 0x11) ); write-type($buf, $custom); 'my-file.bin'.IO.spurt($buf, :bin); return; } # read file my buf8 $buf = 'my-file.bin'.IO.slurp(:bin); my u64 $offset = 0; my Foo $foo = read-type($buf, Foo, $offset); my Foo $bar = read-type($buf, Foo, $offset); my Foo $baz = read-type($buf, Foo, $offset); say $foo; say $bar; say $baz; } sub write-type(buf8 $b, Any:D $x) { my Any $ty = $x.WHAT; for $ty.^attributes -> $attr { next if attr-is-no-serialize($attr); given $attr.type { when u64 { write-u64($b, $attr.get_value($x)); } when u32 { write-u32($b, $attr.get_value($x)); } when u16 { write-u16($b, $attr.get_value($x)); } when u8 { write-u8($b, $attr.get_value($x)); } when i64 { write-i64($b, $attr.get_value($x)); } when i32 { write-i32($b, $attr.get_value($x)); } when i16 { write-i16($b, $attr.get_value($x)); } when i8 { write-i8($b, $attr.get_value($x)); } when Int { write-Int($b, $attr.get_value($x)); } when Str { write-Str($b, $attr.get_value($x)); } # when Array | array { when Positional { my $array_of = $attr.type.of; my $array = $attr.get_value($x); write-array($b, $array_of, $array); } default { fail 'write-type: unsupported type: ' ~ $_.^name; } } } } sub read-type(buf8 $b, Any:U $ty, u64 $offset is rw --> Any) { my Any $result = $ty.CREATE; for $ty.^attributes -> $attr { given $attr.type { next if attr-is-no-serialize($attr); when u64 { $attr.set_value($result, read-u64($b, $offset)); } when u32 { $attr.set_value($result, read-u32($b, $offset)); } when u16 { $attr.set_value($result, read-u16($b, $offset)); } when u8 { $attr.set_value($result, read-u8($b, $offset)); } when i64 { $attr.set_value($result, read-i64($b, $offset)); } when i32 { $attr.set_value($result, read-i32($b, $offset)); } when i16 { $attr.set_value($result, read-i16($b, $offset)); } when i8 { $attr.set_value($result, read-i8($b, $offset)); } when Int { $attr.set_value($result, read-Int($b, $offset)); } when Str { $attr.set_value($result, read-Str($b, $offset)); } # when Array | array { when Positional { my $array_of = $attr.type.of; $attr.set_value($result, read-array($b, $array_of, $offset)); } default { fail 'read-type: unsupported type: ' ~ $_.^name; } } } $result } sub write-u64(buf8 $b, u64 $x) { $b.push: ($x +& 0xFF_00_00_00_00_00_00_00) +> 0d56; $b.push: ($x +& 0x00_FF_00_00_00_00_00_00) +> 0d48; $b.push: ($x +& 0x00_00_FF_00_00_00_00_00) +> 0d40; $b.push: ($x +& 0x00_00_00_FF_00_00_00_00) +> 0d32; $b.push: ($x +& 0x00_00_00_00_FF_00_00_00) +> 0d24; $b.push: ($x +& 0x00_00_00_00_00_FF_00_00) +> 0d16; $b.push: ($x +& 0x00_00_00_00_00_00_FF_00) +> 0d08; $b.push: ($x +& 0x00_00_00_00_00_00_00_FF) +> 0d00; } sub write-u32(buf8 $b, u32 $x) { $b.push: ($x +& 0xFF_00_00_00) +> 0d24; $b.push: ($x +& 0x00_FF_00_00) +> 0d16; $b.push: ($x +& 0x00_00_FF_00) +> 0d08; $b.push: ($x +& 0x00_00_00_FF) +> 0d00; } sub write-u16(buf8 $b, u16 $x) { $b.push: ($x +& 0xFF_00) +> 0d08; $b.push: ($x +& 0x00_FF) +> 0d00; } sub write-u8(buf8 $b, u8 $x) { $b.push: $x; } sub write-i64(buf8 $b, i64 $x) { write-u64($b, $x); } sub write-i32(buf8 $b, i32 $x) { write-u32($b, $x); } sub write-i16(buf8 $b, i16 $x) { write-u16($b, $x); } sub write-i8(buf8 $b, i8 $x) { write-u8($b, $x); } sub write-Int(buf8 $b, Int $x) { my u64 $y = $x +& 0xFFFF_FFFF_FFFF_FFFF; write-u64($b, $y); } sub write-Str(buf8 $b, Str $s) { my utf8 $utf8 = $s.encode('UTF-8'); write-u32($b, $utf8.bytes); $b.push($utf8); } # sub write-f64(buf8 $b, f64 $x) { # my u64 $y = $x; # how do we reinterpret f64 to u64? # write-u64($b, $y); # } # sub write-f32(buf8 $b, f32 $x) { # my u32 $y = $x; # how do we reinterpret f32 to u32? # write-u32($b, $y); # } sub write-array(buf8 $b, Any:U $ty, @array) { write-u32($b, @array.elems); given $ty { when u64 { for @array ->$e { write-u64($b, $e); } } when u32 { for @array ->$e { write-u32($b, $e); } } when u16 { for @array ->$e { write-u16($b, $e); } } when u8 { for @array ->$e { write-u8($b, $e); } } when i64 { for @array ->$e { write-i64($b, $e); } } when i32 { for @array ->$e { write-i32($b, $e); } } when i16 { for @array ->$e { write-i16($b, $e); } } when i8 { for @array ->$e { write-i8($b, $e); } } when Int { for @array ->$e { write-Int($b, $e); } } when Str { for @array ->$e { write-Str($b, $e); } } default { fail 'write-array: unsupported type: ' ~ $ty.^name; } } } sub read-u64(buf8 $b, u64 $offset is rw --> u64) { my u64 $result = 0 +| $b[$offset++] +< 0d56 +| $b[$offset++] +< 0d48 +| $b[$offset++] +< 0d40 +| $b[$offset++] +< 0d32 +| $b[$offset++] +< 0d24 +| $b[$offset++] +< 0d16 +| $b[$offset++] +< 0d08 +| $b[$offset++] +< 0d00 ; $result } sub read-u32(buf8 $b, u64 $offset is rw --> u32) { my u32 $result = 0 +| $b[$offset++] +< 0d24 +| $b[$offset++] +< 0d16 +| $b[$offset++] +< 0d08 +| $b[$offset++] +< 0d00 ; $result } sub read-u16(buf8 $b, u64 $offset is rw --> u16) { my u16 $result = 0 +| $b[$offset++] +< 0d08 +| $b[$offset++] +< 0d00 ; $result } sub read-u8(buf8 $b, u64 $offset is rw --> u8) { my u8 $result = $b[$offset++]; $result } sub read-i64(buf8 $b, u64 $offset is rw --> i64) { read-u64($b, $offset) } sub read-i32(buf8 $b, u64 $offset is rw --> i32) { read-u32($b, $offset) } sub read-i16(buf8 $b, u64 $offset is rw --> i16) { read-u16($b, $offset) } sub read-i8(buf8 $b, u64 $offset is rw --> i8) { read-u8($b, $offset) } # @Todo: read-f64, read-f32 sub read-Int(buf8 $b, u64 $offset is rw --> Int) { my Int $result = read-u64($b, $offset); $result } sub read-Str(buf8 $b, u64 $offset is rw --> Str) { my $byte_len = read-u32($b, $offset); my Str $result = $b.subbuf($offset, $byte_len).decode('UTF-8'); $offset += $byte_len; $result } sub read-array(buf8 $b, Any:U $ty, u64 $offset is rw) { my @result; my $elems = read-u32($b, $offset); given $ty { when u64 { for 1 .. $elems { @result.push: read-u64($b, $offset); } } when u32 { for 1 .. $elems { @result.push: read-u32($b, $offset); } } when u16 { for 1 .. $elems { @result.push: read-u16($b, $offset); } } when u8 { for 1 .. $elems { @result.push: read-u8($b, $offset); } } when i64 { for 1 .. $elems { @result.push: read-i64($b, $offset); } } when i32 { for 1 .. $elems { @result.push: read-i32($b, $offset); } } when i16 { for 1 .. $elems { @result.push: read-i16($b, $offset); } } when i8 { for 1 .. $elems { @result.push: read-i8($b, $offset); } } when Int { for 1 .. $elems { @result.push: read-Int($b, $offset); } } when Str { for 1 .. $elems { @result.push: read-Str($b, $offset); } } default { fail 'read-array: unsupported type: ' ~ $ty.^name; } } @result } =finish