slurp
changeset 0:95785e4bcc1b
moved from darcs repo
| author | John Leuner <jewel@subvert-the-dominant-paradigm.net> |
|---|---|
| date | Fri Apr 25 02:54:05 2008 +0530 (2008-04-25) |
| parents | |
| children | 465aac549a11 |
| files | Emakefile README erlang/slurp.hrl erlang/slurp_decode.erl erlang/slurp_encode.erl javascript/slurp.js lisp/slurp.asd lisp/slurp.lisp |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Emakefile Fri Apr 25 02:54:05 2008 +0530 1.3 @@ -0,0 +1,2 @@ 1.4 +['erlang/*']. 1.5 +{'*',[debug_info]}.
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/README Fri Apr 25 02:54:05 2008 +0530 2.3 @@ -0,0 +1,1 @@ 2.4 +Slurp is an object serialization library, currently implemented for Common Lisp, Erlang and Javascript.
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/erlang/slurp.hrl Fri Apr 25 02:54:05 2008 +0530 3.3 @@ -0,0 +1,24 @@ 3.4 +-define(Byte, 1). 3.5 +-define(Unsigned_integer, 2). 3.6 +-define(Unicode_string, 4). 3.7 +-define(Cons, 7). 3.8 +-define(KeyValue, 8). 3.9 +-define(Nil, 9). 3.10 +-define(Object, 10). 3.11 +-define(Array_Mask, 16). 3.12 + 3.13 +-define(Byte_array, ?Array_Mask bor ?Byte). 3.14 +-define(Utf_8_array, ?Array_Mask bor ?Utf_8). 3.15 +-define(KeyValue_array, ?Array_Mask bor ?KeyValue). 3.16 +-define(Object_array, ?Array_Mask bor ?Object). 3.17 + 3.18 + 3.19 + 3.20 +-define(MarshalDirective, 0). 3.21 +-define(MarshalDirective_LoadStateDescriptor, 2). 3.22 + 3.23 +-define(Has_Named_Slots, 1). 3.24 +-define(StateDescriptorBase, 64). 3.25 + 3.26 +-record(state_descriptor, {code, shape, package_string, name, slots}). 3.27 +
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/erlang/slurp_decode.erl Fri Apr 25 02:54:05 2008 +0530 4.3 @@ -0,0 +1,117 @@ 4.4 +-module(slurp_decode). 4.5 +-include("slurp.hrl"). 4.6 +-export([ read_object/0 ]). 4.7 + 4.8 +read_object( ) -> ClassCode = read_unsigned_integer(), 4.9 + %log:log("handling type ~p", [ClassCode]), 4.10 + if (ClassCode == 0) -> 4.11 + "load meta", 4.12 + MarshalCode = read_unsigned_integer(), 4.13 + case MarshalCode of 4.14 + ?MarshalDirective_LoadStateDescriptor -> StateDescriptor = read_state_descriptor(), 4.15 + put(state_descriptors, dict:store( StateDescriptor#state_descriptor.code, 4.16 + StateDescriptor, get(state_descriptors))), 4.17 + read_object() 4.18 + end; 4.19 + true -> read_datum(ClassCode) 4.20 + end. 4.21 + 4.22 +read_state_descriptor() -> 4.23 + Code = read_unsigned_integer(), 4.24 + Shape = read_unsigned_integer(), 4.25 + PackageString = read_unicode_string(), 4.26 + Name = list_to_atom(read_unicode_string()), 4.27 + SlotCount = read_unsigned_integer(), 4.28 + Slots = case SlotCount of 4.29 + 0 -> []; 4.30 + _Else -> lists:map( fun(Item) -> read_unicode_string() end, lists:seq(1, SlotCount)) 4.31 + end, 4.32 + %log:log("read state descriptor ~p", [Name]), 4.33 + #state_descriptor { code = Code, 4.34 + shape = Shape, 4.35 + package_string = PackageString, 4.36 + name = Name, 4.37 + slots = Slots }. 4.38 + 4.39 +read_datum(Type) -> 4.40 + case Type of 4.41 + ?Byte_array -> read_byte_array(); 4.42 + ?Unsigned_integer -> read_unsigned_integer(); 4.43 + ?Unicode_string -> read_unicode_string(); 4.44 +% ?Utf_8_array -> read_utf8_array(); 4.45 +% ?Utf_8 -> read_utf8(); 4.46 + ?Cons -> read_cons( ); 4.47 + ?KeyValue_array -> read_key_value_array( ); 4.48 + ?KeyValue -> read_key_value( ); 4.49 + ?Object -> read_object(); 4.50 + ?Object_array -> read_object_array(); 4.51 + ?Nil -> []; 4.52 + _Else -> if Type >= ?StateDescriptorBase -> read_object_state( dict:fetch(Type, get(state_descriptors))); 4.53 + true -> throw("unknown type") %abort 4.54 + end 4.55 + end. 4.56 + 4.57 +read_object_state( StateDescriptor ) -> 4.58 + Tuple_items = [ StateDescriptor#state_descriptor.name | lists:map( fun(SlotName) -> 4.59 + read_object( ) end, StateDescriptor#state_descriptor.slots)], 4.60 + Record = list_to_tuple(Tuple_items), 4.61 + Record. 4.62 + 4.63 +read_datums(_, 0) -> []; 4.64 +read_datums( Type, Count ) -> Obj = read_datum( Type), 4.65 + [Obj] ++ read_datums( Type, Count - 1 ). 4.66 + 4.67 + 4.68 +read_unicode_string() -> 4.69 + Length = read_unsigned_integer(), 4.70 + read_datums(?Unsigned_integer, Length). 4.71 + 4.72 +read_byte_array() -> 4.73 + Length = read_unsigned_integer(), 4.74 + %log:log("read byte array ~p", [Length]), 4.75 + list_to_binary(read_bytes(Length)). 4.76 + 4.77 +read_bytes(0) -> []; 4.78 +read_bytes(Count) -> [apply(get(reader_function), []) | read_bytes(Count - 1)]. 4.79 + 4.80 + 4.81 +read_object_array() -> 4.82 + Dimensions = read_cons(), 4.83 + if length(Dimensions) == 1 -> 4.84 + list_to_tuple(read_datums(?Object, element(1, Dimensions))); 4.85 + true -> 4.86 + throw("unhandled object array with dimension size != 1" ++ Dimensions) 4.87 + end. 4.88 + 4.89 +read_string() -> 4.90 + Length = read_unsigned_integer(), 4.91 + read_datums(?Unsigned_integer, Length). 4.92 + 4.93 +read_symbol_array() -> 4.94 + Length = read_unsigned_integer(), 4.95 + list_to_atom(read_datums(?Unsigned_integer, Length)). 4.96 + 4.97 +read_cons( ) -> 4.98 + A = read_object( ), 4.99 + B = read_object( ), 4.100 + [A | B]. 4.101 + 4.102 +read_key_value( ) -> 4.103 + A = read_object( ), 4.104 + B = read_object( ), 4.105 + { A, B }. 4.106 + 4.107 +read_key_value_array( ) -> 4.108 + Length = read_unsigned_integer(), 4.109 + Datums = read_datums( ?KeyValue, Length), 4.110 + dict:from_list(Datums). 4.111 + 4.112 +read_unsigned_integer() -> 4.113 + read_unsigned_integer(1). 4.114 + 4.115 +read_unsigned_integer( Multiplier) -> Byte = apply(get(reader_function), []), 4.116 + %io:format("byte is ~p~n", [Byte]), 4.117 + <<HighBit:1, LowBits:7>> = Byte, 4.118 + if HighBit == 1 -> (Multiplier * LowBits) + read_unsigned_integer( Multiplier * 128); 4.119 + true-> (Multiplier * LowBits) 4.120 + end.
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/erlang/slurp_encode.erl Fri Apr 25 02:54:05 2008 +0530 5.3 @@ -0,0 +1,126 @@ 5.4 +-module(slurp_encode). 5.5 +-include("slurp.hrl"). 5.6 +-export([write_object/1]). 5.7 + 5.8 + 5.9 +map_outbound_object(Object) -> 5.10 + RecordDescriptions = get(record_descriptions), 5.11 + case Object of 5.12 + Object when (is_tuple(Object) and size(Object) >= 1) and (element(1, Object) == slurp_string) -> 5.13 + write_integer(?Unicode_string), 5.14 + write_unicode_string(element(2,Object)), 5.15 + { mapped, Object }; 5.16 + Object when (is_list(Object) and (length(Object) > 0)) -> 5.17 + IsAString = lists:all( fun(X) -> is_integer(X) end, Object), 5.18 + if IsAString -> 5.19 + write_integer(?Unicode_string), 5.20 + write_unicode_string(Object), 5.21 + { mapped, Object }; 5.22 + true -> { unmapped, Object } 5.23 + end; 5.24 + Object when (is_tuple(Object) and (size(Object) >= 1) and is_atom(element(1,Object))) -> 5.25 + RecordName = list_to_atom(replaceDashWithUnderscore(atom_to_list(element(1, Object)))), 5.26 + %log:log("finding ~p in ~p", [RecordName, RecordDescriptions]), 5.27 + IsARecord = dict:find(RecordName, RecordDescriptions), 5.28 +% io:format("isarecord ~p ~p~n", [RecordName, IsARecord]), 5.29 + case IsARecord of 5.30 + { ok, RecordFieldNames } -> { write_record, RecordName, RecordFieldNames, Object}; 5.31 + error -> { unmapped, Object } 5.32 + end; 5.33 + _Else -> %yio:format("else ~p ~p ~p ~p ~n",[is_tuple(Object), size(Object), element(1,Object), Object]), 5.34 + { unmapped, Object } 5.35 + end. 5.36 + 5.37 +replaceDashWithUnderscore(String) -> 5.38 + lists:map( fun(Char) -> case Char of 5.39 + $- -> $_; 5.40 + _ -> Char 5.41 + end 5.42 + end, 5.43 + String). 5.44 + 5.45 +replaceUnderscoreWithDash(String) -> 5.46 + lists:map( fun(Char) -> case Char of 5.47 + $_ -> $-; 5.48 + _ -> Char 5.49 + end 5.50 + end, 5.51 + String). 5.52 + 5.53 +write_object(Object) -> 5.54 + %log:log("writing object ~p~n", [ Object ]), 5.55 + case map_outbound_object(Object) of 5.56 + {unmapped, MappedObject} -> 5.57 + case MappedObject of 5.58 + [] -> write_integer( ?Nil); 5.59 + MappedObject when is_binary(MappedObject) -> write_integer( ?Byte_array), 5.60 + write_byte_array( MappedObject); 5.61 + MappedObject when is_integer(MappedObject) -> write_integer( ?Unsigned_integer), 5.62 + write_integer( MappedObject); 5.63 + MappedObject when is_list(MappedObject) -> write_integer( ?Cons), 5.64 + write_cons( MappedObject); 5.65 + MappedObject when is_tuple(MappedObject) -> write_integer( ?Object_array), 5.66 + write_object_array( MappedObject); 5.67 + MappedObject when is_atom(MappedObject) -> write_integer( ?Unicode_string), 5.68 + write_unicode_string( atom_to_list(MappedObject)); 5.69 + _Else -> log:log("cannot encode ~p", [MappedObject]), 5.70 + throw("unhandled " ++ MappedObject) 5.71 + end; 5.72 + {mapped, _ } -> Object; 5.73 + {write_record, RecordName, RecordFieldNames, Record} -> %io:format("writing record~n",[]), 5.74 + write_record(RecordName, RecordFieldNames, Record) 5.75 + end. 5.76 + 5.77 +write_record(RecordName, RecordFieldNames, Record) -> 5.78 + write_integer(?MarshalDirective), 5.79 + write_integer(?MarshalDirective_LoadStateDescriptor), 5.80 + DescriptorCode = getNextStateDescriptorCode(), 5.81 + write_integer(DescriptorCode), 5.82 + write_integer(?Has_Named_Slots), 5.83 + write_unicode_string("furax"), 5.84 + write_unicode_string(replaceUnderscoreWithDash(atom_to_list(RecordName))), 5.85 + write_integer( length(RecordFieldNames) ), 5.86 + lists:foreach( fun(F) -> 5.87 + write_unicode_string(replaceUnderscoreWithDash(atom_to_list(F))) end, RecordFieldNames), 5.88 + write_integer(DescriptorCode), 5.89 + lists:foreach( fun write_object/1, tl(tuple_to_list(Record))). 5.90 + 5.91 + 5.92 +write_symbol( String_as_atom) -> 5.93 + CharList = atom_to_list(String_as_atom), 5.94 + write_integer( length(CharList)), 5.95 + lists:foreach( fun write_integer/1, CharList). 5.96 + 5.97 +write_unicode_string( String) -> 5.98 + %io:format("wus ~p~n", [String]), 5.99 + write_integer( length(String)), 5.100 + lists:foreach( fun write_integer/1, String). 5.101 + 5.102 + 5.103 +write_byte_array( Binary) -> 5.104 + write_integer( size(Binary)), 5.105 + write_bytes( Binary). 5.106 + 5.107 +write_bytes( <<>>) -> ok; 5.108 +write_bytes( <<Byte:8,Rest/binary>>) -> apply(get(writer_function), [<<Byte>>]), 5.109 + write_bytes( Rest). 5.110 + 5.111 +write_byte( Byte) -> 5.112 + apply(get(writer_function), [Byte]). 5.113 + 5.114 +write_object_array( Tuple) -> 5.115 + write_cons( [ size(Tuple) ] ), 5.116 + lists:foreach(fun write_object/1, tuple_to_list(Tuple)). 5.117 + 5.118 +write_integer( Integer) when Integer >= 128 -> apply(get(writer_function), [ <<((Integer rem 128) + 128):8>> ]), 5.119 + write_integer( Integer div 128); 5.120 +write_integer( Integer) -> apply(get(writer_function), [ <<Integer:8>> ]). 5.121 + 5.122 +write_cons( [A|B]) -> 5.123 + write_object( A), 5.124 + write_object( B). 5.125 + 5.126 +getNextStateDescriptorCode() -> 5.127 + Code = get(next_state_descriptor), 5.128 + put(next_state_descriptor, Code + 1), 5.129 + Code.
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/javascript/slurp.js Fri Apr 25 02:54:05 2008 +0530 6.3 @@ -0,0 +1,316 @@ 6.4 +var slurp_state_descriptor_map = new Object(); 6.5 + 6.6 +function slurp_error(message) { 6.7 + this.message = message; 6.8 + console.warn("slurp error %s", message); 6.9 +} 6.10 + 6.11 +function slurp_Nil() {} 6.12 +function slurp_Cons(A, B) { this.A = A; this.B = B;} 6.13 + 6.14 +slurp_Cons.prototype['SLURP_TYPE'] = "SLURP_CONS"; 6.15 + 6.16 +var reader_function = null; 6.17 + 6.18 +function string_to_array(str) { 6.19 + var array = new Array(); 6.20 + var len = str.length; 6.21 + for(var c = 0; c < len; c++) { 6.22 + array[c] = str.charCodeAt(c); 6.23 + } 6.24 + return array; 6.25 +} 6.26 + 6.27 +function slurp_decode(encoded_string) { 6.28 + if (encoded_string == "") { 6.29 + return null; 6.30 + } 6.31 + var array = string_to_array(encoded_string); 6.32 + return slurp_decode_array ( array ); 6.33 +} 6.34 + 6.35 +function slurp_decode_array(byteArray) { 6.36 + var read_index = -1; 6.37 + 6.38 + reader_function = function () { 6.39 + read_index++; 6.40 + var byte = byteArray[read_index]; 6.41 +// console.log("b %s", byte); 6.42 + return byte; 6.43 + }; 6.44 + var obj = slurp_read_object(); 6.45 + return obj; 6.46 +} 6.47 + 6.48 +function slurp_read_object() { 6.49 + var classCode = 0; 6.50 + while( (classCode = slurp_read_unsigned_int()) == 0) { 6.51 + var marshalDirective = slurp_read_unsigned_int(); 6.52 + switch (marshalDirective) { 6.53 + case slurp_marshal_directive_load_state_descriptor: slurp_read_state_descriptor(); break; 6.54 + default: return new slurp_error("can't handle marshal dir"); 6.55 + } 6.56 + } 6.57 +// console.log("reading datum class code %s" , classCode); 6.58 + return slurp_read_datum(classCode); 6.59 +} 6.60 + 6.61 +var slurp_marshal_directive = 0; 6.62 +var slurp_marshal_directive_load_state_descriptor = 2; 6.63 + 6.64 +var slurp_array_mask = 16; 6.65 + 6.66 +var slurp_byte = 1; 6.67 +var slurp_unsigned_integer = 2; 6.68 +var slurp_unicode_string = 4; 6.69 +var slurp_cons = 7; 6.70 +var slurp_nil = 9; 6.71 +var slurp_object = 10; 6.72 + 6.73 +var slurp_byte_array = slurp_byte + slurp_array_mask; 6.74 +var slurp_object_array = slurp_object + slurp_array_mask; 6.75 + 6.76 +function slurp_read_unsigned_int() { 6.77 + var readValue, answer = 0, shifter = 1; 6.78 + var finished = 0; 6.79 + // var count = 5; 6.80 + while(finished != 1) { 6.81 + readValue = reader_function(); 6.82 + // writeError("read val is " + readValue, true); 6.83 + if (readValue < 128) { 6.84 + finished = 1; 6.85 + } 6.86 + if(finished != 1) { 6.87 + readValue = readValue - 128; 6.88 + } 6.89 + answer = answer + (readValue * shifter); 6.90 + shifter = shifter * 128; 6.91 + // count--; 6.92 + // if (count == 0) return "FAILED"; 6.93 + } 6.94 + return answer; 6.95 +} 6.96 + 6.97 +function slurp_read_datum(classCode) { 6.98 + switch(classCode) { 6.99 + case slurp_unsigned_integer: 6.100 + return slurp_read_unsigned_int(); 6.101 + case slurp_unicode_string: 6.102 + return slurp_read_unicode_string(); 6.103 + case slurp_cons: 6.104 + return slurp_read_cons(); 6.105 + case slurp_nil: 6.106 + return null; 6.107 + case slurp_byte_array: 6.108 + return slurp_read_byte_array(); 6.109 + case slurp_object_array: 6.110 + return slurp_read_object_array(); 6.111 + default: 6.112 + if (classCode >= 64) { 6.113 + return slurp_read_state_object(classCode); 6.114 + } 6.115 + } 6.116 + return new slurp_error("read invalid class code " + classCode); 6.117 +} 6.118 + 6.119 +function slurp_read_byte_array() { 6.120 + var length = slurp_read_unsigned_int(); 6.121 + var array = new Array(); 6.122 + for(var i = 0; i < length; i++) { 6.123 + array[i] = reader_function(); 6.124 + } 6.125 + return array; 6.126 +} 6.127 + 6.128 +function slurp_read_object_array() { 6.129 + var dimensions = slurp_read_cons(); 6.130 + dimensions = slurp_cons_to_list(dimensions); 6.131 + if (dimensions.length != 1) { 6.132 + var SlurpError = new Error("bad dimensions read in read_object_array"); 6.133 + throw SlurpError; 6.134 + } 6.135 + var array = new Array(); 6.136 +// console.log("reading array length %s", dimensions[0]); 6.137 + for(var i = 0; i < dimensions[0]; i++) { 6.138 + array[i] = slurp_read_object(); 6.139 +// console.log("at %s read %s", i, Dumper(array[i])); 6.140 + } 6.141 + return array; 6.142 +} 6.143 + 6.144 +function slurp_read_cons() { 6.145 + var A = slurp_read_object(); 6.146 + var B = slurp_read_object(); 6.147 +// Dumper.popup(B); 6.148 +// console.log("tail is null %s %s", B, B == null); 6.149 + return new slurp_Cons(A, B); 6.150 +} 6.151 + 6.152 +function slurp_cons_to_list(cons) { 6.153 + var acc = new Array(); 6.154 + walk_cons_helper(cons, acc); 6.155 + return acc.reverse(); 6.156 +} 6.157 + 6.158 +function walk_cons_helper(cons, acc) { 6.159 + if( cons instanceof slurp_Cons ) { 6.160 + // writeError("A is " + cons.A + "<br/>",true); 6.161 + acc.push(cons.A); 6.162 + walk_cons_helper( cons.B, acc); 6.163 + } 6.164 +} 6.165 + 6.166 +function slurp_read_unicode_string() { 6.167 + var length = slurp_read_unsigned_int(); 6.168 + var result = ""; 6.169 + for(var i = 0; i < length; i++) { 6.170 + result = result + String.fromCharCode( slurp_read_unsigned_int()); 6.171 + } 6.172 +// console.log("read unicode %s", result); 6.173 + return result; 6.174 +} 6.175 + 6.176 + 6.177 +function slurp_read_state_descriptor() { 6.178 + var code = slurp_read_unsigned_int(); 6.179 + var shape = slurp_read_unsigned_int(); 6.180 + var package_string = slurp_read_unicode_string(); 6.181 + var name = slurp_read_unicode_string(); 6.182 + var slot_count = slurp_read_unsigned_int(); 6.183 + var slots = new Array(); 6.184 + for(var i = 0; i < slot_count; i++) { 6.185 + slots[i] = slurp_read_unicode_string(); 6.186 + } 6.187 + var sd = new state_descriptor(code, shape, package_string, name, slot_count, slots); 6.188 + slurp_state_descriptor_map[ '' + code ] = sd; 6.189 +// console.log("read state desc %s", Dumper.popup(sd)); 6.190 + return sd; 6.191 +} 6.192 + 6.193 +function state_descriptor(code, shape, package_string, name, slot_count, slots) { 6.194 + this.code = code; 6.195 + this.shape = shape; 6.196 + this.package_string = package_string; 6.197 + this.name = name; 6.198 + this.slot_count = slot_count; 6.199 + this.slots = slots; 6.200 + return this; 6.201 +} 6.202 + 6.203 +function slurp_read_state_object(classCode) { 6.204 + var sd = slurp_state_descriptor_map[ classCode ]; 6.205 + if (sd == null) 6.206 + return new slurp_error("missing state descriptor for class " + classCode); 6.207 + var obj = new Object(); 6.208 + for(var i = 0; i < sd.slot_count; i++) 6.209 + { 6.210 + obj[ sd.slots['' + i ] ] = slurp_read_object(); 6.211 + } 6.212 + obj["SLURP_TYPE"] = sd.name; 6.213 + // console.log("loaded %s object", sd.name); 6.214 + return obj; 6.215 +} 6.216 + 6.217 +var last_class_code = 64; 6.218 + 6.219 +var writer_function = null; 6.220 + 6.221 +function slurp_encode(obj) { 6.222 + var outputArray = new Array(); 6.223 + writer_function = function (newByte) { 6.224 + outputArray.push(newByte); 6.225 + } 6.226 + slurp_write_object(obj); 6.227 + 6.228 +return String.fromCharCode.apply( null, outputArray); 6.229 +} 6.230 + 6.231 +function slurp_write_object(obj) { 6.232 + if (typeof(obj) == "number") { 6.233 + slurp_write_unsigned_int(slurp_unsigned_integer); 6.234 + slurp_write_unsigned_int(obj); 6.235 + } 6.236 + else if(typeof(obj) == "string") { 6.237 + slurp_write_unsigned_int(slurp_unicode_string); 6.238 + slurp_write_unicode_string(obj); 6.239 + } 6.240 + else if ((obj == null) || (obj == undefined)) { 6.241 +// console.log("writing %s", obj); 6.242 +// debugger; 6.243 + slurp_write_unsigned_int(slurp_nil); 6.244 + } 6.245 + else { 6.246 + var type = obj['SLURP_TYPE']; 6.247 + if (type == 'undefined') { 6.248 + console.log('type of ' + obj + ' is undefined'); 6.249 + } 6.250 + else if (type == "SLURP_CONS") { 6.251 + slurp_write_unsigned_int(slurp_cons); 6.252 + slurp_write_object( obj.A ); 6.253 + slurp_write_object( obj.B ); 6.254 + } else { 6.255 + var slots = new Array(); 6.256 + for (var property in obj) { 6.257 + if (!((property == 'SLURP_TYPE') || (property == 'SLURP_PACKAGE'))) { 6.258 + slots.push(property); 6.259 + } 6.260 + } 6.261 + var sd = new state_descriptor(last_class_code++, 1, obj['SLURP_PACKAGE'], type, slots.length, slots); 6.262 + 6.263 + slurp_write_unsigned_int( slurp_marshal_directive ); 6.264 + slurp_write_unsigned_int( slurp_marshal_directive_load_state_descriptor ); 6.265 + 6.266 + slurp_write_state_descriptor(sd); 6.267 + slurp_write_state_object(sd, obj); 6.268 + } 6.269 + } 6.270 +} 6.271 + 6.272 +function slurp_write_state_descriptor(sd) { 6.273 + with(sd) { 6.274 + slurp_write_unsigned_int(code); 6.275 + slurp_write_unsigned_int(shape); 6.276 + slurp_write_unicode_string(package_string); 6.277 + slurp_write_unicode_string(name); 6.278 + slurp_write_unsigned_int(slot_count); 6.279 + 6.280 + for (var idx = 0; idx < slot_count; idx++) { 6.281 + slurp_write_unicode_string(slots[idx]); 6.282 + } 6.283 + } 6.284 +} 6.285 + 6.286 +function slurp_write_state_object(sd, obj) { 6.287 +// console.log("writing object %s", obj); 6.288 +// Dumper.popup(obj); 6.289 + slurp_write_unsigned_int(sd.code); 6.290 + for (var idx = 0; idx < sd.slot_count; idx++) { 6.291 + var slot = sd.slots[idx]; 6.292 + // console.log("writing slot %s with %s", slot, obj[slot]); 6.293 + slurp_write_object( obj[slot] ); 6.294 + } 6.295 +} 6.296 + 6.297 +//or use Math.floor instead of parseInt 6.298 + 6.299 +function slurp_write_unsigned_int(num) { 6.300 + var carry = parseInt(num / 128); 6.301 + var writeValue = (num % 128); 6.302 + while (carry != 0) { 6.303 + writer_function (writeValue + 128); 6.304 + writeValue = carry % 128; 6.305 + carry = parseInt(carry / 128); 6.306 + } 6.307 + writer_function(writeValue); 6.308 +} 6.309 + 6.310 +function slurp_write_unicode_string(str) { 6.311 +// console.log("writing %s", str); 6.312 + var len = str.length; 6.313 + slurp_write_unsigned_int(len); 6.314 + for (var c = 0; c < len; c++) { 6.315 + slurp_write_unsigned_int( str.charCodeAt(c)); 6.316 + } 6.317 +} 6.318 + 6.319 +
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/lisp/slurp.asd Fri Apr 25 02:54:05 2008 +0530 7.3 @@ -0,0 +1,8 @@ 7.4 +(defsystem slurp 7.5 + :description "SLURP" 7.6 + :version "0.01" 7.7 + :author "John Leuner" 7.8 + :licence "MIT License" 7.9 + :components ((:file "slurp" )) 7.10 + :depends-on (flexi-streams)) 7.11 +
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/lisp/slurp.lisp Fri Apr 25 02:54:05 2008 +0530 8.3 @@ -0,0 +1,454 @@ 8.4 +(defpackage #:slurp 8.5 + (:use #:cl :flexi-streams) 8.6 + (:export :writer :reader :decode :encode :make-slurp-writer :make-slurp-reader) 8.7 +) 8.8 +(defpackage #:test-slurp 8.9 + (:use #:cl :slurp)) 8.10 + 8.11 +(in-package :slurp) 8.12 + 8.13 +(declaim (optimize (speed 0) (safety 3) (debug 3))) 8.14 + 8.15 +(defconstant +byte+ 1) 8.16 +(defconstant +unsigned-integer+ 2) 8.17 +(defconstant +signed-integer+ 3) 8.18 +(defconstant +unicode-string+ 4) 8.19 +(defconstant +cons+ 7) 8.20 +(defconstant +key-value+ 8) 8.21 +(defconstant +nil+ 9) 8.22 +(defconstant +object+ 10) ; used for object array 8.23 + 8.24 +(defconstant +array-mask+ 16) 8.25 + 8.26 +(defconstant +byte-array+ (logior +byte+ +array-mask+)) 8.27 +(defconstant +key-value-array+ (logior +key-value+ +array-mask+)) 8.28 +(defconstant +object-array+ (logior +object+ +array-mask+)) 8.29 + 8.30 +(defconstant +state-descriptor-base+ 64) ; all state descriptors have a code greater than or equal to this 8.31 + 8.32 +(defconstant +marshal-directive+ 0) 8.33 + 8.34 +(defconstant +marshal-directive-load-relative-offset+ 1) ; directive to load an object that has already been loaded previously 8.35 +(defconstant +marshal-directive-load-metastate+ 2) ; directive to load a metastate description from the stream 8.36 +(defconstant +marshal-directive-skip-bytes+ 3) ; directive to load an array of bytes and ignore them 8.37 +(defconstant +marshal-directive-ignore-object+ 3) ; directive to load an object from the stream, but ignore it 8.38 + 8.39 +(defconstant HAS-NAMED-SLOTS 1) 8.40 +(defconstant HAS-TYPED-NAMED-SLOTS 2) 8.41 +(defconstant HAS-INDEXED-PART 4) 8.42 +(defconstant HAS-VERSION-NUMBER-ARRAY 8) 8.43 +(defconstant IS_STATELESS 16) 8.44 +(defconstant IS_NAMED 32) 8.45 +(defconstant IS_TYPED 64) 8.46 + 8.47 +(defclass state-descriptor () ((code :initarg :code :reader code) 8.48 + (shape :initarg :shape :reader shape) 8.49 + (package-string :initarg :package-string :reader package-string) 8.50 + (name :initarg :name :reader name) 8.51 + (named-slots :initarg :named-slots :reader named-slots))) 8.52 + 8.53 +(defgeneric has-named-slots (state-descriptor)) 8.54 +(defmethod has-named-slots ((state-descriptor state-descriptor)) 8.55 + (not (null (named-slots state-descriptor)))) 8.56 + 8.57 +(defclass placeholder () ((base-object :initarg :base-object :accessor base-object))) 8.58 + 8.59 +(defclass writer () 8.60 + ((lisp-stream :initarg :stream :accessor lisp-stream) 8.61 + (save-stack :initform (make-array 10 :adjustable t :fill-pointer 0) :accessor save-stack) 8.62 + (state-descriptors :initform (make-hash-table) :accessor state-descriptors))) 8.63 + 8.64 +(defclass reader () 8.65 + ((lisp-stream :initarg :stream :accessor lisp-stream) 8.66 + (load-stack :initform (make-array 10 :adjustable t :fill-pointer 0) :accessor load-stack) 8.67 + (state-descriptors :initform (make-hash-table) :accessor state-descriptors))) 8.68 + 8.69 +(defclass input-stream () ()) 8.70 + 8.71 +(defgeneric slurp-type (object)) 8.72 +(defgeneric read-datum (stream type-indicator)) 8.73 +(defgeneric write-datum (stream type-indicator object)) 8.74 + 8.75 +(defgeneric make-slurp-input-stream (underlying)) 8.76 +(defgeneric make-slurp-output-stream (underlying)) 8.77 + 8.78 +(defun make-slurp-reader (stream) 8.79 + (make-instance 'reader :stream stream)) 8.80 + 8.81 +(defun make-slurp-writer (stream) 8.82 + (make-instance 'writer :stream stream)) 8.83 + 8.84 +(defun decode (buffer) 8.85 + (let* ((reader (make-slurp-reader (flexi-streams:make-in-memory-input-stream buffer)))) 8.86 + (read-object reader))) 8.87 + 8.88 +(defun encode (object) 8.89 + (let* ((writer (make-slurp-writer (flexi-streams:make-in-memory-output-stream)))) 8.90 + (write-object writer object) 8.91 + (flexi-streams:get-output-stream-sequence (lisp-stream writer)))) 8.92 + 8.93 +(defvar *writer*) 8.94 +(defvar *save-stack*) 8.95 + 8.96 +(defvar *reader*) 8.97 +(defvar *load-stack*) 8.98 +(defvar *state-descriptors*) 8.99 + 8.100 +(defgeneric discard-placeholders (placeholder-or-object recursion-set)) 8.101 +; reading and writing lisp objects from a slurp stream 8.102 +(defgeneric read-object (reader)) 8.103 +(defgeneric write-object (writer object)) 8.104 + 8.105 +(defmethod read-object ((reader reader)) 8.106 + (let* ((stream (lisp-stream reader)) 8.107 + (*reader* reader) 8.108 + (*load-stack* (load-stack reader)) 8.109 + (*state-descriptors* (state-descriptors reader)) 8.110 + (placeholder (do ((class-code (read-datum stream +unsigned-integer+) 8.111 + (read-datum stream +unsigned-integer+))) 8.112 + ((not (eq class-code 0)) (read-datum stream class-code)) 8.113 + (let ((marshal-directive (read-datum stream +unsigned-integer+))) 8.114 + (cond ((eq marshal-directive +marshal-directive-load-metastate+) 8.115 + (let ((state-descriptor (read-state-descriptor stream))) 8.116 + ;(break state-descriptor) 8.117 + (setf (gethash (code state-descriptor) *state-descriptors*) state-descriptor) 8.118 + )) 8.119 + (t (error (format nil "unknown marshal directive ~A ~A" marshal-directive +marshal-directive-load-metastate+))) 8.120 + ) 8.121 + )))) 8.122 + (discard-placeholders placeholder (make-hash-table)))) 8.123 + 8.124 +(defmethod read-object :around ((reader reader)) 8.125 + (let ((foo (call-next-method reader))) 8.126 + ; (format t "ro ~A~%" foo) 8.127 + foo)) 8.128 + 8.129 + (defmethod write-object ((writer writer) object) 8.130 + (let ((stream (lisp-stream writer)) 8.131 + (*writer* writer) 8.132 + (*state-descriptors* (state-descriptors writer))) 8.133 + (if (null object) 8.134 + (write-datum stream +unsigned-integer+ +nil+) 8.135 + (typecase object 8.136 + (cons (progn (write-datum stream +unsigned-integer+ +cons+) 8.137 + (write-datum stream +cons+ object) 8.138 + )) 8.139 + (integer (progn (write-datum stream +unsigned-integer+ +unsigned-integer+) 8.140 + (write-datum stream +unsigned-integer+ object))) 8.141 + (symbol (progn (write-datum stream +unsigned-integer+ +unicode-string+) 8.142 + (write-datum stream +unicode-string+ (string object)))) 8.143 + (string (progn (write-datum stream +unsigned-integer+ +unicode-string+) 8.144 + (write-datum stream +unicode-string+ object))) 8.145 + (hash-table (progn (write-datum stream +unsigned-integer+ +key-value-array+) 8.146 + (write-datum stream +unsigned-integer+ (hash-table-count object)) 8.147 + (loop for k being the hash-keys in object using (hash-value v) 8.148 + do (write-datum writer +key-value+ (cons k v))))) 8.149 + ((vector (unsigned-byte 8)) (progn (write-datum stream +unsigned-integer+ +byte-array+) 8.150 + (write-datum stream +unsigned-integer+ (length object)) 8.151 + (write-sequence object stream))) 8.152 + ((vector t) 8.153 + (progn 8.154 + (write-datum stream +unsigned-integer+ +object-array+) 8.155 + (write-datum stream +object-array+ object))) 8.156 + ((simple-array t) 8.157 + (progn 8.158 + (write-datum stream +unsigned-integer+ +object-array+) 8.159 + (write-datum stream +object-array+ object))) 8.160 + (t (let ((state-descriptor (find-state-descriptor-by-class (class-of object)))) 8.161 + (when (null state-descriptor) 8.162 +; (format t "could not find sd for obj ~A of class ~A~%" object (class-of object)) 8.163 + (setf state-descriptor (make-state-descriptor (class-of object))) 8.164 + (write-state-descriptor stream state-descriptor)) 8.165 + (write-datum stream +unsigned-integer+ (code state-descriptor)) 8.166 + (write-datum stream state-descriptor object) 8.167 + )))))) 8.168 + 8.169 +(defmethod discard-placeholders ((placeholder placeholder) recursion-set) 8.170 + (let ((lookup (gethash placeholder recursion-set))) 8.171 + (if lookup 8.172 + (base-object placeholder) 8.173 + (progn 8.174 + (setf (gethash placeholder recursion-set) placeholder) 8.175 + (discard-placeholders (base-object placeholder) recursion-set))))) 8.176 + 8.177 +(defmethod discard-placeholders ((object t) recursion-set) 8.178 + (let ((lookup (gethash object recursion-set))) 8.179 + (if lookup 8.180 + object 8.181 + (let* ((class (class-of object)) 8.182 + (slots (sb-pcl:class-slots class))) 8.183 + (setf (gethash object recursion-set) object) 8.184 + (loop for s in slots do (let ((name (sb-pcl:slot-definition-name s))) 8.185 + (setf (slot-value object name) 8.186 + (discard-placeholders (slot-value object name) recursion-set)))) 8.187 + object)))) 8.188 + 8.189 +(defun make-state-descriptor (class) 8.190 + (let* ((code (+ 1 (hash-table-count *state-descriptors*) +state-descriptor-base+)) 8.191 + (slots (sb-pcl:class-slots class)) 8.192 + (state-descriptor (make-instance 'state-descriptor 8.193 + :code code 8.194 + :shape (logior (if slots 8.195 + HAS-NAMED-SLOTS 8.196 + 0)) 8.197 + :package-string (package-name (symbol-package (class-name class))) 8.198 + :name (symbol-name (class-name class)) 8.199 + :named-slots (loop for slot in slots collect (string (sb-pcl:slot-definition-name slot)))))) 8.200 + (setf (gethash class *state-descriptors*) state-descriptor) 8.201 +; (break "state descriptors ~A" *state-descriptors*) 8.202 + state-descriptor)) 8.203 + 8.204 +(defun find-state-descriptor-by-code (code) 8.205 + (let ((lookup (gethash code *state-descriptors*))) 8.206 + (if (not lookup) 8.207 + (error (format nil "missing state-descriptor for code ~A" code) ) 8.208 + lookup))) 8.209 + 8.210 +(defun find-state-descriptor-by-class (class) 8.211 + (let ((lookup (gethash class *state-descriptors*))) 8.212 + lookup)) 8.213 + 8.214 +(defun write-state-descriptor (stream state-descriptor) 8.215 +; (format t "writing state descriptor ~A~%" state-descriptor) 8.216 + (write-datum stream +unsigned-integer+ +marshal-directive+) 8.217 + (write-datum stream +unsigned-integer+ +marshal-directive-load-metastate+) 8.218 + (write-datum stream +unsigned-integer+ (code state-descriptor)) 8.219 + (write-datum stream +unsigned-integer+ (shape state-descriptor)) 8.220 + (write-datum stream +unicode-string+ (package-string state-descriptor)) 8.221 + (write-datum stream +unicode-string+ (name state-descriptor)) 8.222 + (write-datum stream +unsigned-integer+ (list-length (named-slots state-descriptor))) 8.223 + (loop for slot in (named-slots state-descriptor) do (write-datum stream +unicode-string+ slot))) 8.224 + 8.225 +(defun read-state-descriptor (stream) 8.226 + (let* ((code (read-datum stream +unsigned-integer+)) 8.227 + (shape (read-datum stream +unsigned-integer+)) 8.228 + (package-string (read-datum stream +unicode-string+)) 8.229 + (name (read-datum stream +unicode-string+)) 8.230 + (slot-count (read-datum stream +unsigned-integer+)) 8.231 + (slots (loop for s from 1 to slot-count collect (read-datum stream +unicode-string+)))) 8.232 + (make-instance 'state-descriptor :code code :shape shape :package-string package-string :name name :named-slots slots))) 8.233 + 8.234 +; mapping lisp objects to slurp types 8.235 + 8.236 +(defmethod slurp-type ((object integer)) 8.237 + +unsigned-integer+) 8.238 + 8.239 +(defmethod slurp-type ((object cons)) 8.240 + +cons+) 8.241 + 8.242 +(defmethod slurp-type ((object string)) 8.243 + +unicode-string+) 8.244 + 8.245 +; reading and writing slurp data 8.246 +(defgeneric modify-decoded-slot (object slot value)) 8.247 +(defmethod modify-decoded-slot (object slot value) 8.248 + value) 8.249 + 8.250 + 8.251 +(defmethod read-datum (stream (state-descriptor-code integer)) 8.252 + (assert (>= state-descriptor-code +state-descriptor-base+)) 8.253 + (let* ((state-descriptor (find-state-descriptor-by-code state-descriptor-code)) 8.254 + (package (find-package (intern (string-upcase (package-string state-descriptor)) :keyword))) 8.255 + (class-name (intern (name state-descriptor) package)) 8.256 + (newobject (make-instance class-name)) ; make an instance 8.257 + (placeholder (make-instance 'placeholder :base-object newobject))) ; make a placeholder 8.258 +; (break placeholder newobject state-descriptor) 8.259 + (when (has-named-slots state-descriptor) ; read the slots 8.260 + (loop for slot in (named-slots state-descriptor) do 8.261 + (let ((slot-name (intern (string-upcase slot) package)) 8.262 + (value (read-object *reader*))) 8.263 + (handler-case 8.264 + (setf (slot-value newobject slot-name) (modify-decoded-slot newobject slot-name value)) 8.265 + (simple-error (condition) 8.266 + (format t "error ~A setting slot ~A in class ~A with value ~A~%" condition slot-name class-name value)))))) 8.267 + placeholder)) 8.268 + 8.269 +(defmethod write-datum (stream (state-descriptor state-descriptor) object) 8.270 +; (break state-descriptor) 8.271 + (when (has-named-slots state-descriptor) 8.272 + ; (format t "writing state descriptor slots ~A ~%" (name state-descriptor)) 8.273 + (loop for slot in (named-slots state-descriptor) do 8.274 + (write-object *writer* 8.275 + (handler-case (slot-value object (intern slot (find-package (intern (package-string state-descriptor))))) 8.276 + (unbound-slot () nil)))))) 8.277 + 8.278 +(defmethod read-datum (stream (data-type (eql +nil+))) nil) 8.279 + 8.280 +(defmethod read-datum (stream (data-type (eql +byte+))) 8.281 + (read-byte stream)) 8.282 + 8.283 +(defmethod write-datum (stream (data-type (eql +byte+)) obj) 8.284 + (write-byte obj stream)) 8.285 + 8.286 +(defmethod write-datum (stream (data-type (eql +cons+)) obj) 8.287 + (write-object *writer* (first obj)) 8.288 + (write-object *writer* (rest obj))) 8.289 + 8.290 +(defmethod write-datum (stream (data-type (eql +key-value+)) obj) 8.291 + (write-object *writer* (first obj)) 8.292 + (write-object *writer* (rest obj))) 8.293 + 8.294 +(defmethod write-datum (stream (data-type (eql +object-array+)) (obj vector)) 8.295 + (write-datum *writer* +cons+ (list (length obj))) 8.296 + (let ((length (length obj))) 8.297 + (loop for index from 0 below length 8.298 + do (write-object *writer* (elt obj index))))) 8.299 + 8.300 +(defmethod write-datum (stream (data-type (eql +object-array+)) obj) 8.301 + (write-datum *writer* +cons+ (array-dimensions obj)) 8.302 + (let ((length (array-total-size obj))) 8.303 + (loop for index from 0 below length 8.304 + do (write-object *writer* (row-major-aref obj index))))) 8.305 + 8.306 +(defmethod read-datum (stream (data-type (eql +cons+))) 8.307 + (let ((A (read-object *reader*)) 8.308 + (B (read-object *reader*))) 8.309 + (cons A B))) 8.310 + 8.311 +(defmethod read-datum (stream (data-type (eql +object-array+))) 8.312 + (let* ((dimensions (read-datum stream +cons+)) 8.313 + (buffer (make-array dimensions :element-type t)) 8.314 + (length (array-total-size buffer))) 8.315 + (loop for index from 0 below length do 8.316 + (setf (row-major-aref buffer index) (read-object *reader*))) 8.317 + buffer)) 8.318 + 8.319 +(defmethod read-datum (stream (data-type (eql +byte-array+))) 8.320 + (let* ((length (read-datum stream +unsigned-integer+)) 8.321 + (buffer (make-array length :element-type '(unsigned-byte 8)))) 8.322 + (loop for index from 0 to (- length 1) do 8.323 + (setf (aref buffer index) (read-byte stream))) 8.324 + buffer)) 8.325 + 8.326 +(defmethod read-datum (stream (data-type (eql +unicode-string+))) 8.327 + (let* ((length (read-datum stream +unsigned-integer+)) 8.328 + (str (make-array length :element-type 'character))) 8.329 + (loop for index from 0 to (- (length str) 1) do 8.330 + (setf (char str index) 8.331 + (code-char (read-datum stream +unsigned-integer+)) 8.332 + )) 8.333 + (string str))) 8.334 + 8.335 +(defmethod read-datum (stream (data-type (eql +key-value-array+))) 8.336 + (let* ((count (read-datum stream +unsigned-integer+)) 8.337 + (ht (make-hash-table :size count))) 8.338 + (dotimes (counter count) 8.339 + (let ((key (read-object *reader*)) 8.340 + (value (read-object *reader*))) 8.341 + (setf (gethash (if (stringp key) 8.342 + (intern key) 8.343 + key) 8.344 + ht) value))) ; hack, we turn string keys into symbols 8.345 + ht)) 8.346 + 8.347 +(defmethod write-datum (stream (data-type (eql +unicode-string+)) (str string)) 8.348 + (write-datum stream +unsigned-integer+ (length str)) 8.349 + (loop for char across str do (write-datum stream +unsigned-integer+ (char-code char)))) 8.350 + 8.351 +(defmethod write-datum (stream (data-type (eql +unsigned-integer+)) (num integer)) 8.352 + (assert (>= num 0)) 8.353 + (do ((carry (truncate num 128)) 8.354 + (writeValue (rem num 128))) 8.355 + ((equal carry 0) (write-byte writeValue stream)) 8.356 + (write-byte (+ writeValue 128) stream) 8.357 +; (format t "write ~A carry ~A writing ~A~%" writeValue carry (+ writeValue 128)) 8.358 + (setf writeValue (rem carry 128)) 8.359 + (setf carry (truncate carry 128)))) 8.360 + 8.361 +(defmethod read-datum (stream (data-type (eql +unsigned-integer+))) 8.362 + (do ((answer 0) 8.363 + (shifter 1) 8.364 + (atLast nil) 8.365 + (readValue)) 8.366 + (atLast (progn 8.367 +; (format t "done ~A~%" answer) 8.368 + answer)) 8.369 + (setf readValue (let ((val (read-byte stream))) 8.370 + (if (typep val 'character) 8.371 + (char-code val) 8.372 + val))) 8.373 + (setf atLast (< readValue 128)) ; (equal (rem readValue 128) 0)) 8.374 + (if (not atLast) 8.375 + (setf readValue (- readValue 128))) 8.376 + (setf answer (+ answer (* readValue shifter))) 8.377 + (setf shifter (* shifter 128)))) 8.378 + 8.379 +; tests 8.380 + 8.381 +(defclass testA () 8.382 + ((a) 8.383 + (b) 8.384 + (c) 8.385 + (d))) 8.386 + 8.387 +(defclass testB () 8.388 + ((|apple|) 8.389 + (|bean|) 8.390 + (|cattle|) 8.391 + (|dog|))) 8.392 + 8.393 +(defun check-equal (A B) 8.394 + (if (not (equal A B)) 8.395 + (break "~A is does not equal ~A" A B))) 8.396 + 8.397 +(defun testArray () 8.398 + (let* ((array (make-array '(19 19) :initial-element nil)) 8.399 + (bytes (slurp::encode array))) 8.400 + bytes 8.401 + ; 8.402 + ; 8.403 + ; (check-equal (decode bytes) array)) 8.404 + )) 8.405 + 8.406 + 8.407 +(defun test () 8.408 + (simple-test) 8.409 + (with-open-file (stream "slurp-test.slurp" :direction :output :if-exists :overwrite :if-does-not-exist :create) 8.410 + (let ((objA (make-instance 'testA))) 8.411 + (setf (slot-value objA 'a) 22) 8.412 + (setf (slot-value objA 'b) "mohab") 8.413 + (setf (slot-value objA 'c) '(1 2 3)) 8.414 + (setf (slot-value objA 'd) (make-array 3 :element-type '(unsigned-byte 8) 8.415 + :initial-contents '(1 99 254))) 8.416 + (write-sequence (encode objA) stream) 8.417 + ) 8.418 + (with-open-file (file "/home/jewel/dev/furax/test-packet.slurp" :direction :input) 8.419 + (let ((s (make-string (file-length file)))) 8.420 + (read-sequence s file) 8.421 + (let ((objB (decode s))) 8.422 + (check-equal (first objB) 22)))))) 8.423 + 8.424 +(defun simple-test () 8.425 + (let ((objA (make-instance 'testA)) 8.426 + (objB (make-instance 'testB))) 8.427 + 8.428 + (setf (slot-value objA 'a) 22) 8.429 + (setf (slot-value objA 'b) "mohab") 8.430 + (setf (slot-value objA 'c) '(1 2 3)) 8.431 + (setf (slot-value objA 'd) (make-array 3 :initial-contents '("a" "b" "c"))) 8.432 + 8.433 + (setf (slot-value objB '|apple|) objA) 8.434 + (setf (slot-value objB '|bean|) 2) 8.435 + (setf (slot-value objB '|cattle|) 'grimace) 8.436 + (setf (slot-value objB '|dog|) :bumper) 8.437 + 8.438 + (let* ((encA (encode objA)) 8.439 + (encB (encode objB)) 8.440 + (decA (decode encA)) 8.441 + (decB (decode encB))) 8.442 + (check-equal (slot-value objA 'a) (slot-value decA 'a)) 8.443 + (check-equal (slot-value objA 'b) (slot-value decA 'b)) 8.444 + (check-equal (slot-value objA 'c) (slot-value decA 'c)) 8.445 + (check-equal (length (slot-value objA 'd)) 8.446 + (length (slot-value decA 'd))) 8.447 + (check-equal (class-of (slot-value decB '|apple|)) (class-of objA)) 8.448 + (check-equal (slot-value objB '|bean|) (slot-value decB '|bean|)) 8.449 + (check-equal (string (slot-value objB '|cattle|)) (slot-value decB '|cattle|)) 8.450 + (check-equal (string (slot-value objB '|dog|)) (slot-value decB '|dog|))))) 8.451 + 8.452 + 8.453 + 8.454 + 8.455 + 8.456 + 8.457 +
