1 module d2d.core.bytestream; 2 3 public import std.system : Endian; 4 public import std.bitmanip; 5 import std.traits; 6 7 /// Struct for wrapping a ubyte[] with read functions and endianness. 8 struct ByteStreamImpl (Endian endianness) 9 { 10 /// Contains the data. 11 ubyte[] stream; 12 alias stream this; 13 14 /// Sets the stream to data. 15 this(ubyte[] data) 16 { 17 stream = data; 18 } 19 20 /// Sets the stream to data. 21 void opAssign(ubyte[] data) 22 { 23 stream = data; 24 } 25 26 /// Advances the stream by `amount` bytes. 27 void skip(size_t amount) 28 { 29 stream = stream[amount .. $]; 30 } 31 32 /// Returns the first `T.sizeof` bytes from the stream and advances the stream. 33 T read(T = ubyte)() 34 { 35 return stream.read!(T, endianness)(); 36 } 37 38 /// Returns a `T[]` from the stream and advances the stream. 39 T[] read(T = ubyte)(size_t length) 40 { 41 T[] data = new T[length]; 42 for (size_t i = 0; i < length; i++) 43 data[i] = read!(T)(); 44 return data; 45 } 46 47 /// Reads until value is `value` and returns everything read excluding `value`. Advances after found `value`. Returns `null` if `value` was not found. 48 T readTo(T)(ubyte value) 49 { 50 // Check for ForeachType!T.sizeof == 1 maybe? 51 static assert(isArray!T, "T must be an Array!"); 52 for (size_t i = 0; i < stream.length; i++) 53 { 54 if (stream[i] == value) 55 { 56 scope (exit) stream = stream[i + 1 .. $]; 57 return cast(T) stream[0 .. i]; 58 } 59 } 60 return cast(T) null; 61 } 62 63 /// Reads until value is `value` and returns everything read including `value`. Advances after found `value`. Returns `null` if `value` was not found. 64 T readToIncluding(T)(ubyte value) 65 { 66 // Check for ForeachType!T.sizeof == 1 maybe? 67 static assert(isArray!T, "T must be an Array!"); 68 for (size_t i = 0; i < stream.length; i++) 69 { 70 if (stream[i] == value) 71 { 72 scope (exit) stream = stream[i + 1 .. $]; 73 return cast(T) stream[0 .. i + 1]; 74 } 75 } 76 return cast(T) null; 77 } 78 79 /// Returns `T.sizeof` bytes from the stream at position `index` without advancing the stream. 80 T peek(T = ubyte)(size_t index = 0) 81 { 82 return stream.peek!(T, endianness)(index); 83 } 84 85 /// Returns a `T[]` from the stream. 86 T[] peek(T = ubyte)(size_t index, size_t length) 87 { 88 T[] data = new T[length]; 89 for (size_t i = 0; i < length; i++) 90 data[i] = peek!(T)(index + i * T.sizeof); 91 return data; 92 } 93 94 /// Writes `data` to the stream at position `index`. 95 void write(T)(T data, size_t index) 96 { 97 static if (isArray!(T)) 98 { 99 for (size_t i = 0; i < data.length; i++) 100 write(data[i], index + i * typeof(data[0]).sizeof); 101 } 102 else 103 { 104 stream.write!(T, endianness)(data, index); 105 } 106 } 107 108 /// Appends `data` to the stream. 109 void append(T)(T data) 110 { 111 static if (isArray!(T)) 112 { 113 for (size_t i = 0; i < data.length; i++) 114 append(data[i]); 115 } 116 else 117 { 118 stream.length += T.sizeof; 119 write(data, stream.length - T.sizeof); 120 } 121 } 122 } 123 124 alias ByteStream = ByteStreamImpl!(Endian.bigEndian); 125 alias BigByteStream = ByteStream; 126 alias LittleByteStream = ByteStreamImpl!(Endian.littleEndian); 127 128 /// 129 unittest 130 { 131 ByteStream stream = cast(ubyte[])[0x01, 0x05, 0x16, 0x09, 0x2C, 0xFF, 0x08]; 132 133 assert(stream.peek!uint () == 0x01_05_16_09); 134 135 stream.append!(ushort[])([0x4A_2B, 0x59_12]); 136 assert(stream.read!ulong () == 0x01_05_16_09_2C_FF_08_4AUL); 137 assert(stream == [0x2B, 0x59, 0x12]); 138 139 stream.write!ubyte (0x44, 1); 140 assert(stream == [0x2B, 0x44, 0x12]); 141 142 stream.write!(ubyte[])([0x01, 0x02, 0x03], 0); 143 assert(stream == [0x01, 0x02, 0x03]); 144 } 145 146 /// 147 unittest 148 { 149 ByteStream stream = (cast(ubyte[]) "abc\0") ~cast(ubyte[])[0x44]; 150 assert(stream.readTo!string(cast(ubyte) '\0') == "abc"); 151 assert(stream == [0x44]); 152 } 153 154 /// 155 unittest 156 { 157 ByteStream stream = (cast(ubyte[]) "abc\0") ~cast(ubyte[])[0x44]; 158 assert(stream.readToIncluding!string(cast(ubyte) '\0') == "abc\0"); 159 assert(stream == [0x44]); 160 } 161 162 unittest 163 { 164 ByteStream stream = cast(ubyte[])[0x01, 0x05, 0x16, 0x09, 0x2C, 0xFF, 0x08]; 165 166 assert(stream.peek!uint () == 0x01_05_16_09); 167 assert(stream.peek!ushort () == 0x01_05); 168 assert(stream.peek!ubyte () == 0x01); 169 170 assert(stream.peek!uint (2) == 0x16_09_2C_FF); 171 assert(stream.peek!ushort (2) == 0x16_09); 172 assert(stream.peek!ubyte (2) == 0x16); 173 174 assert(stream.peek!ushort (1, 2) == [0x05_16, 0x09_2C]); 175 176 stream ~= 0x4A; 177 178 assert(stream.peek!uint (0, 2) == [0x01_05_16_09, 0x2C_FF_08_4A]); 179 } 180 181 unittest 182 { 183 ByteStream stream = cast(ubyte[])[0x01, 0x05]; 184 185 stream.append!ushort (0x4A_2B); 186 assert(stream == [0x01, 0x05, 0x4A, 0x2B]); 187 188 stream.append!(ubyte[])(cast(ubyte[])[0x16, 0x09, 0x2C, 0xFF, 0x08]); 189 assert(stream == [0x01, 0x05, 0x4A, 0x2B, 0x16, 0x09, 0x2C, 0xFF, 0x08]); 190 } 191 192 unittest 193 { 194 ByteStream stream = cast(ubyte[])[0x01, 0x05, 0x16, 0x09, 0x2C, 0xFF, 0x08]; 195 196 stream.write!ubyte (0x06, 1); 197 assert(stream == [0x01, 0x06, 0x16, 0x09, 0x2C, 0xFF, 0x08]); 198 199 stream.write!(ushort[])(cast(ushort[])[0x07_4A, 0x2B_EA], 1); 200 assert(stream == [0x01, 0x07, 0x4A, 0x2B, 0xEA, 0xFF, 0x08]); 201 } 202 203 unittest 204 { 205 ByteStream stream = cast(ubyte[])[0x01, 0x05, 0x16, 0x09, 0x2C, 0xFF, 0x08]; 206 207 assert(stream.read() == 0x01); 208 assert(stream.read!ushort () == 0x05_16); 209 assert(stream.read!uint () == 0x09_2C_FF_08); 210 assert(stream.length == 0); 211 }