1 module xbuffer.util; 2 3 import std.range : OutputRange; 4 import std.system : Endian, endian; 5 import std.traits : isArray; 6 7 import xbuffer.buffer : ForeachType, Buffer, canSwapEndianness; 8 9 /** 10 * Extension template for a buffer that provides methods 11 * and properties useful for working with a single type 12 * of data. 13 */ 14 class Typed(T, B:Buffer=Buffer) : B, OutputRange!T if(canSwapEndianness!T) { 15 16 this(size_t chunk) pure nothrow @safe @nogc { 17 super(chunk * T.sizeof); 18 } 19 20 this(in T[] data) pure nothrow @safe @nogc { 21 super(data); 22 } 23 24 /** 25 * Gets the data as an array of the template's type. 26 * The data can also be obtained in the specified format. 27 * Example: 28 * --- 29 * auto buffer = new Typed!char("hello"); 30 * assert(buffer.data == "hello"); 31 * assert(buffer.data!ubyte == [104, 101, 108, 108, 111]); 32 * --- 33 */ 34 @property D[] data(D=T)() pure nothrow @trusted @nogc if(D.sizeof == 1) { 35 return super.data!D; 36 } 37 38 /// ditto 39 @property D[] data(D=T)() pure nothrow @nogc if(D.sizeof != 1) { 40 return super.data!D; 41 } 42 43 /** 44 * Writes a value of type `T` or array of type `T[]`. 45 * Example: 46 * --- 47 * auto buffer = new Typed!short(4); 48 * buffer.put(1); 49 * buffer.put(2, 3); 50 * buffer.put([4, 5]); 51 * assert(buffer.data == [1, 2, 3, 4, 5]); 52 * --- 53 */ 54 void put(T value) pure nothrow @safe @nogc { 55 this.write(value); 56 } 57 58 /// ditto 59 void put(in T[] value...) pure nothrow @safe @nogc { 60 this.write(value); 61 } 62 63 /** 64 * Reads a value of type `T` or an array of type `T[]`. 65 * Example: 66 * --- 67 * auto buffer = new Typed!int(4); 68 * buffer.data = [1, 2, 3, 4, 5]; 69 * assert(buffer.get == 1); 70 * assert(buffer.get == 2); 71 * assert(buffer.get(3) == [3, 4, 5]); 72 * --- 73 */ 74 T get() pure @safe { 75 return this.read!T(); 76 } 77 78 /// ditto 79 T[] get(size_t length) pure @safe { 80 return this.read!(T[])(length); 81 } 82 83 } 84 85 /// ditto 86 template Typed(T, B:Buffer=Buffer) if(isArray!T && canSwapEndianness!(ForeachType!T)) { alias Typed = Typed!(ForeachType!T, B); } 87 88 /// 89 pure @safe unittest { 90 91 import std.range; 92 93 alias ByteBuffer = Typed!ubyte; 94 95 static assert(isOutputRange!(ByteBuffer, ubyte)); 96 97 auto buffer = new ByteBuffer([0, 0, 0, 4]); 98 assert(buffer.read!(Endian.bigEndian, uint)() == 4); 99 100 buffer.reset(); 101 buffer.put(1); 102 buffer.put([2, 3]); 103 buffer.put(4, 5, 6); 104 assert(buffer.data == [1, 2, 3, 4, 5, 6]); 105 assert(buffer.get == 1); 106 assert(buffer.get(3) == [2, 3, 4]); 107 108 } 109 110 /// 111 pure @safe unittest { 112 113 alias IntBuffer = Typed!int; 114 115 auto buffer = new IntBuffer(2); 116 assert(buffer.capacity == 8); 117 buffer.write(1); 118 buffer.write(2); 119 version(BigEndian) assert(buffer.data!ubyte == [0, 0, 0, 1, 0, 0, 0, 2]); 120 version(LittleEndian) assert(buffer.data!ubyte == [1, 0, 0, 0, 2, 0, 0, 0]); 121 assert(buffer.read!int() == 1); 122 assert(buffer.read!int() == 2); 123 124 } 125 126 /// 127 pure @trusted unittest { 128 129 static struct Test { 130 131 int a; 132 short b; 133 134 } 135 136 static assert(Test.sizeof == 8); // because of the alignment 137 138 alias TestBuffer = Typed!Test; 139 140 auto buffer = new TestBuffer(1); 141 buffer.put(Test(1, 2)); 142 version(BigEndian) assert(buffer.data!ubyte == [0, 0, 0, 1, 0, 2, 0, 0]); 143 version(LittleEndian) assert(buffer.data!ubyte == [1, 0, 0, 0, 2, 0, 0, 0]); 144 assert(buffer.data == [Test(1, 2)]); 145 assert(buffer.get == Test(1, 2)); 146 147 buffer.reset(); 148 buffer.write!(cast(Endian)!endian)(Test(1, 2)); 149 version(BigEndian) assert(buffer.data!ubyte == [0, 0, 2, 0, 1, 0, 0, 0]); 150 version(LittleEndian) assert(buffer.data!ubyte == [0, 0, 0, 2, 0, 0, 0, 1]); 151 152 } 153 154 /// 155 pure nothrow @safe @nogc unittest { 156 157 static struct Test { 158 159 int a; 160 ubyte[] b; 161 162 } 163 164 static assert(!canSwapEndianness!Test); 165 166 } 167 168 /// 169 pure @safe nothrow unittest { 170 171 alias StringBuffer = Typed!string; 172 173 static assert(is(StringBuffer == Typed!(immutable(char)))); 174 175 auto buffer = new StringBuffer(64); 176 buffer.put("hello"); 177 assert(buffer.data!char == ['h', 'e', 'l', 'l', 'o']); 178 179 }