Last active
April 7, 2019 21:51
-
-
Save SeijiEmery/20d71ca127b11c7f214e042a7bc8ff69 to your computer and use it in GitHub Desktop.
Full serialization lib in 160 lines of code (D)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
chmod +x serializer.d | |
./serializer.d |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Initial thing: MyComplexDataType([Thing("foo", 10, 12.4), Thing("bar", 4, 22.9), Thing("baz", 19, 14.5)], "fubar") | |
Restored thing: MyComplexDataType([Thing("foo", 10, 12.4), Thing("bar", 4, 22.9), Thing("baz", 19, 14.5)], "fubar") | |
data (82 bytes): [3, 0, 0, 0, 0, 0, 0, 0, 102, 111, 111, 10, 0, 0, 0, 205, 204, 204, 204, 204, 204, 40, 64, 3, 0, 0, 0, 0, 0, 0, 0, 98, 97, 114, 4, 0, 0, 0, 102, 102, 102, 102, 102, 230, 54, 64, 3, 0, 0, 0, 0, 0, 0, 0, 98, 97, 122, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 64, 5, 0, 0, 0, 0, 0, 0, 0, 102, 117, 98, 97, 114] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env rdmd | |
import std.traits: hasIndirections, isBasicType; | |
import std.stdio: writefln; | |
// | |
// Our serialization interface | |
// | |
// Serialize an object / value to a stream of bytes | |
// usage: | |
// auto data = serialize(x); | |
// | |
ubyte[] serialize (T)(T object) { | |
ubyte[] data; | |
serialize!write_bytes(data, object); | |
return data; | |
} | |
// Deserialize a serialized stream of bytes back into an existing object / value | |
// usage: | |
// deserialize(x, data); | |
// | |
auto ref deserialize (T)(T object, ubyte[] data) { | |
serialize!read_bytes(data, object); | |
return object; | |
} | |
// Deserialize a stream of bytes and construct / create a new object or value of type T | |
// usage: | |
// auto x = deserialize!Thing(data); | |
// | |
auto deserialize (T)(ubyte[] data) | |
{ | |
static if (is(T == class)) { | |
T object = new T(); | |
} else { | |
T object; | |
} | |
return deserialize(object, data); | |
} | |
// | |
// Implementation details | |
// | |
// Basic serialization functions: | |
// write a stream of bytes to a stream of bytes | |
// or | |
// read a stream of bytes from a stream of bytes | |
void write_bytes (ref ubyte[] data, const ubyte[] memory) { | |
data ~= memory; | |
} | |
void read_bytes (ref ubyte[] data, ref ubyte[] memory) { | |
memory[0..$] = data[0..memory.length]; | |
data = data[memory.length..$]; | |
} | |
// | |
// serialize() is a generic serialization / deserialization function with the following type signature: | |
// | |
// serialize!(serializer_function)(data, thing-to-serialize-or-deserialize) | |
// | |
// Most basic case: forward to the serialization function | |
void serialize (alias serializer)(ref ubyte[] data, ref ubyte[] memory) { | |
serializer(data, memory); | |
} | |
// Best case: value type without pointers or references | |
// (can cast this directly to a stream of bytes and write that) | |
void serialize (alias serializer, T)(ref ubyte[] data, T value) | |
if (!hasIndirections!T || isBasicType!T) | |
{ | |
auto value_data = cast(ubyte[])((&value)[0..1]); | |
serializer(data, value_data); | |
} | |
// Even better case: array of value types without pointers or references | |
// (can cast this directly to a stream of bytes and write that) | |
void serialize (alias serializer, T)(ref ubyte[] data, T[] values) | |
if (!hasIndirections!T || isBasicType!T || is(T[] == string)) | |
{ | |
serialize!serializer(data, values.length); | |
auto value_data = cast(ubyte[])values; | |
serializer(data, value_data); | |
} | |
// Array of non-value types: iterate over elements and serialize each individually | |
void serialize (alias serializer, T)(ref ubyte[] data, T[] values) | |
if (hasIndirections!T && __traits(compiles, serialize!serializer(data, values[0]))) | |
{ | |
foreach (value; values) { | |
serialize!serializer(data, value); | |
} | |
} | |
// Struct / class with pointers / references: | |
// (can't read/write as byte stream; iterate over fields and serialize each of those in order) | |
void serialize (alias serializer, T)(ref ubyte[] data, T value) | |
if (hasIndirections!T && (is(T == struct) || is(T == class))) | |
{ | |
foreach (ref field; value.tupleof) { | |
serialize!serializer(data, field); | |
} | |
} | |
//Custom serialization method? call that | |
void serialize (alias serializer, T)(ref ubyte[] data, T object) | |
if (__traits(compiles, object.serialize!serializer(data))) | |
{ | |
object.serialize!serializer(data); | |
} | |
// Helper function: lets you call | |
// serialize!serializer(data, thing1, thing2, thing3, ...); | |
// for custom serialization methods | |
// | |
void serialize (alias serializer, Args...)(ref ubyte[] data, Args args) | |
if (args.length >= 2) | |
{ | |
//writefln("serializing multiple things"); | |
foreach (arg; args) { | |
serialize!serializer(data, arg); | |
} | |
} | |
// | |
// Example :) | |
// | |
struct Thing { | |
string x; | |
int y; | |
double z; | |
} | |
struct MyComplexDataType { | |
Thing[] things; | |
string x; | |
} | |
int main () { | |
//ubyte[] data; | |
//auto thing = "fubar"; | |
//auto thing = Thing("foo", 1, 2); | |
//auto thing = [ Thing("foo", 1, 2), Thing("bar", 2, 12) ]; | |
auto thing = MyComplexDataType( | |
[ | |
Thing("foo", 10, 12.4), | |
Thing("bar", 4, 22.9), | |
Thing("baz", 19, 14.5), | |
], | |
"fubar" | |
); | |
alias T = typeof(thing); | |
writefln("Initial thing: %s", thing); | |
auto data = serialize(thing); | |
auto restoredThing = deserialize!T(data); | |
writefln("Restored thing: %s", thing); | |
writefln("data (%s bytes): %s", data.length, data); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment