--- /dev/null
+module bare ;
+
+import std.algorithm ;
+import std.bitmanip ;
+import std.range ;
+import std.conv ;
+import std.typecons ;
+import std.traits ;
+import std.variant ;
+
+// PRIMITIVE TYPES
+
+// u*, i*, f*
+static foreach ( t ; [ "ubyte" , "ushort" , "uint" , "ulong" , "byte" ,
+ "short" , "int" , "long" , "float" , "double" ] ) {
+ T readb(T)( ref ubyte[] data ) if ( is( T == mixin( t ) ) )
+ in ( data.length >= T.sizeof , "Not enough data." ) {
+ return data.read!( T , Endian.littleEndian ) ;
+ }
+ ubyte[] writeb(T)( T v ) if ( is( T == mixin( t ) ) ) {
+ ubyte[] buffer = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
+ buffer.write!( T , Endian.littleEndian )( v , 0 ) ;
+ return buffer[ 0 .. T.sizeof ] ;
+ }
+}
+
+// bool
+bool readb(T)( ref ubyte[] data ) if ( is( T == bool ) )
+ in ( data.length >= 1 , "Not enough data." ) {
+ scope(exit) data.popFront ;
+ return data[0] > 0 ;
+}
+ubyte[] writeb( bool v ) { return v ? [ ubyte( 1 ) ] : [ ubyte( 0 ) ] ; }
+
+// uint
+ulong rduint( ref ubyte[] data ) {
+ ulong v = 0 ;
+ ulong s = 0 ;
+ while ( true ) {
+ ubyte b = data[0] ; data.popFront ;
+ v |= cast(ulong)( b & 127 ) << s ;
+ s += 7 ;
+ if ( b < 128 ) break ;
+ }
+ return v ;
+}
+ubyte[] wruint( ulong v ) {
+ ubyte[] data = [] ;
+ while ( v > 0 ) {
+ data ~= 0x80 + ( v & 0b01111111 ) ;
+ v >>>= 7 ;
+ }
+ data[ $ - 1 ] -= 0x80 ;
+ return data ;
+}
+
+// int
+long rdint( ref ubyte[] data ) {
+ ulong v = data.rduint ;
+ return ( v >>> 1 ) ^ - ( v & 1 ) ;
+}
+ubyte[] wrint( long v ) {
+ return wruint( cast(ulong)( ( v << 1 ) ^ ( v >> 63 ) ) ) ;
+}
+
+// string
+string readb(T)( ref ubyte[] data ) if ( is( T == string ) ) {
+ ulong l = data.rduint ;
+ assert( data.length >= l , "Not enough data." ) ;
+ scope(exit) data.popFrontExactly( l ) ;
+ return cast(string)( data[0..l] ) ;
+}
+ubyte[] writeb( string v ) { return wruint( v.length ) ~ cast(ubyte[])v ; }
+
+// void
+void readb(T)( ref ubyte[] data ) if ( is( T == void ) ) { return ; }
+// no write as it is useless
+
+// NOTE: use *uint methods for enums
+
+// NOTE: data and data<length> use array aggregate methods
+
+// AGGREGATE TYPES
+
+// optional<type>
+T readb(T)( ref ubyte[] data )
+ if ( __traits( isSame , TemplateOf!T , Nullable ) ) {
+ return data.readb!bool
+ ? mixin( "T( readb!" , TemplateArgsOf!(T)[0] , "( data ) )" )
+ : T() ;
+}
+ubyte[] writeb(T)( Nullable!T v ) {
+ return v.isNull
+ ? [ ubyte( 0 ) ]
+ : [ ubyte( 1 ) ] ~ writeb( v.get ) ;
+}
+
+// [length]type
+T readb(T)( ref ubyte[] data ) if ( __traits( isStaticArray , T ) ) {
+ T result ;
+ foreach ( i ; 0 .. result.length )
+ result[l] = mixin( "readb!" , ElementType!T , "( data )" ) ;
+ return result ;
+}
+ubyte[] writeb(T)( T v ) if ( __traits( isStaticArray , T ) ) {
+ return v.map!( i => writeb( i ) ).array.join() ;
+}
+
+// []type
+T readb(T)( ref ubyte[] data ) if ( isDynamicArray!T && ! is( T == string ) ) {
+ T[] result = [] ;
+ foreach ( i ; 0 .. data.rduint )
+ result ~= mixin( "readb!" , ElementType!T , "( data )" ) ;
+ return result ;
+}
+ubyte[] writeb(T)( T v ) if ( isDynamicArray!T && ! is( T == string ) ) {
+ return wruint( v.length ) ~ v.map!( i => writeb( i ) ).array.join() ;
+}
+
+// map[type A]type B
+T readb(T)( ref ubyte[] data ) if ( isAssociativeArray!T ) {
+ T result ;
+ foreach ( i ; 0 .. data.rduint )
+ result[ mixin( "readb!" , KeyType!T , "( data )" ) ]
+ = mixin( "readb!" , ElementType!T , "( data )" ) ;
+ return result ;
+}
+ubyte[] writeb(T)( T v ) if ( isAssociativeArray!T ) {
+ return wruint( v.keys.length )
+ ~ v.keys.map!( k => writeb( k ) ~ writeb( v[k] ) ).array.join() ;
+}
+
+// (type | type | ...)
+T readb(T)( ref ubyte[] data )
+ if ( __traits( isSame , TemplateOf!T , Algebraic ) ) {
+ ulong t = data.rduint ;
+ static foreach ( i , V ; TemplateArgsOf!T )
+ if ( i == t ) return mixin( "readb!" , V , "( data )" ) ;
+}
+ubyte[] writeb(T)( T v )
+ if ( __traits( isSame , TemplateOf!T , Algebraic ) ) {
+ static foreach ( i , V ; TemplateArgsOf!T )
+ if ( v.type == typeid(V) )
+ return wruint( i ) ~ write( v.get!V ) ;
+ assert( 0 ) ;
+}
+
+// struct
+T readb(T)( ref ubyte[] data )
+ if ( isAggregateType!T
+ && ! __traits( isSame , TemplateOf!T , Nullable )
+ && ! __traits( isSame , TemplateOf!T , Algebraic ) ) {
+ alias names = FieldNameTuple!T ;
+ alias types = Fields!T ;
+ static assert( names.length == types.length ) ;
+ T result ;
+ static foreach ( i ; 0 .. names.length )
+ mixin( "result." , names[i] , " = readb!" , types[i] , "( data ) ;" ) ;
+ return result ;
+}
+ubyte[] writeb(T)( T v )
+ if ( isAggregateType!T
+ && ! __traits( isSame , TemplateOf!T , Nullable )
+ && ! __traits( isSame , TemplateOf!T , Algebraic ) ) {
+ alias names = FieldNameTuple!T ;
+ ubyte[] result ;
+ static foreach ( i ; 0 .. names.length )
+ result ~= writeb( mixin( "v." , names[i] ) ) ;
+ return result ;
+}