Initial commit
authorMira Ayre <mi@boxin.space>
Tue, 7 Jul 2020 20:02:04 +0000 (21:02 +0100)
committerMira Ayre <mi@boxin.space>
Fri, 9 Jul 2021 19:18:35 +0000 (20:18 +0100)
All methods for reading and writing all types in the spec have an
initial (and likely very buggy) implementation.

Note that because of the way D works, some features are not directly
accessible e.g. as there are no built-in varint types, they have
distinct methods and must be called explicitly otherwise data will be
read/written as a ulong/long. Read/write for full-featured types will
come later with schema-based templates.

.gitignore [new file with mode: 0644]
bare.d [new file with mode: 0644]
dub.sdl [new file with mode: 0644]
main.d [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a6ac37a
--- /dev/null
@@ -0,0 +1,2 @@
+bare
+.dub
diff --git a/bare.d b/bare.d
new file mode 100644 (file)
index 0000000..054e90e
--- /dev/null
+++ b/bare.d
@@ -0,0 +1,170 @@
+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 ;
+}
diff --git a/dub.sdl b/dub.sdl
new file mode 100644 (file)
index 0000000..fb34f54
--- /dev/null
+++ b/dub.sdl
@@ -0,0 +1,8 @@
+name "bare"
+description "BARE implementation"
+authors "Olie Ayre"
+license "GPL-3.0"
+homepage "https://git.sr.ht/~otheb/bare"
+sourcePaths "."
+importPaths "."
+targetType "executable"
diff --git a/main.d b/main.d
new file mode 100644 (file)
index 0000000..3a2525d
--- /dev/null
+++ b/main.d
@@ -0,0 +1,26 @@
+import bare ;
+
+import std.stdio ;
+
+import std.typecons ;
+
+struct NamedPoint {
+       uint   x    ;
+       uint   y    ;
+       string name ;
+
+       this( uint x_ , uint y_ , string n ) {
+               x    = x_ ;
+               y    = y_ ;
+               name = n  ;
+       }
+}
+
+void main() {
+       auto a = writeb( Nullable!int( 1234 ) ) ;
+       readb!( Nullable!int )( a ).writeln() ;
+
+       auto p = NamedPoint( 123 , 456 , "descriptive name" ) ;
+       auto d = writeb( p ) ;
+       readb!NamedPoint( d ).writeln() ;
+}