1 module draklib.bytestream; 2 3 import draklib.util; 4 5 import std.stdio; 6 import std.conv : to; 7 import std.algorithm.mutation : reverse; 8 import std.exception; 9 import std.system; 10 11 /** 12 * Class that allows simple reading and writing of high 13 * level types such as integers and strings to/from bytes. 14 * 15 * Authors: jython234 16 */ 17 class ByteStream { 18 private byte[] buffer; 19 private uint position; 20 private bool dynamic; 21 private Endian endianess; 22 23 private this(byte[] data, bool dynamic) { 24 this.buffer = data; 25 this.dynamic = dynamic; 26 this.position = 0; 27 } 28 29 static ByteStream alloc(in uint size, in Endian endianess = Endian.bigEndian) { 30 assert(size > 0); 31 32 ByteStream stream = new ByteStream(new byte[size], false); 33 stream.setEndianness(endianess); 34 return stream; 35 } 36 37 static ByteStream allocDyn(in Endian endianness = Endian.bigEndian) { 38 byte[] array = []; 39 ByteStream stream = new ByteStream(array, true); 40 stream.setEndianness(endianness); 41 return stream; 42 } 43 44 static ByteStream wrap(byte[] data, in Endian endianess = Endian.bigEndian) { 45 assert(data.length > 0); 46 47 ByteStream stream = new ByteStream(data, false); 48 stream.setEndianness(endianess); 49 return stream; 50 } 51 52 /** 53 * Request "size" amount of bytes to be added to the buffer. 54 */ 55 void allocRequest(in ulong size) { 56 version(ARM) { //Have to cast due to 32 bit 57 this.buffer.length = this.buffer.length + cast(uint) size; 58 } else { 59 version(X86) { //Have to cast due to 32 bit 60 this.buffer.length = this.buffer.length + cast(uint) size; 61 } else { 62 this.buffer.length = this.buffer.length + size; 63 } 64 } 65 } 66 67 /// Read "len" amount of bytes. The bytes returned have been DUPLICATED, to exactly read use readExact(); 68 byte[] read(in int len) { 69 //assert(len > 0 && len < (getSize() - getPosition()), "Length not in bounds"); 70 enforce(len > 0 && len <= (getSize() - getPosition()), new OutOfBoundsException("Attempted to read " ~ to!string(len) ~ " but only " ~ to!string(getSize() - getPosition()) ~ " avaliable out of " ~ to!string(getSize()))); 71 72 int oldPos = getPosition(); 73 setPosition(getPosition() + len); 74 return this.buffer[oldPos .. getPosition()].dup; 75 } 76 77 /// Read "len amount of bytes unsigned. The bytes returned have been DUPLICATED. 78 ubyte[] readU(in int len) { 79 return cast(ubyte[]) read(len); 80 } 81 82 byte[] readExact(in int len) { 83 enforce(len > 0 && len <= (getSize() - getPosition()), new OutOfBoundsException("Attempted to read " ~ to!string(len) ~ " but only " ~ to!string(getSize() - getPosition()) ~ " avaliable out of " ~ to!string(getSize()))); 84 85 int oldPos = getPosition(); 86 setPosition(getPosition() + len); 87 return this.buffer[oldPos .. getPosition()]; 88 } 89 90 /// Write "data" to the buffer 91 void write(in byte[] data) { 92 assert(data.length > 0); 93 if(!dynamic) enforce((data.length + getPosition()) <= getSize(), new OutOfBoundsException(to!string(data.length) ~ " needed but only " ~ to!string(getSize() - getPosition()) ~ " avaliable out of " ~ to!string(getSize()))); 94 else if((data.length + getPosition()) > getSize()) { 95 allocRequest(data.length + getPosition()); 96 } 97 98 uint counter = 0; 99 for(uint i = 0; i < data.length; i++) { 100 ///debug writeln("i: ", i, ", counter: ", counter, ", data: ", data, "data.len: ", data.length, ", buffer.len: ", buffer.length, ", buffer: ", buffer); 101 this.buffer[getPosition() + counter] = data[i]; 102 counter++; 103 } 104 this.setPosition(getPosition() + counter); 105 } 106 107 void writeU(in ubyte[] data) { 108 write(cast(byte[]) data); 109 } 110 111 //Read Methods 112 113 byte readByte() { 114 return read(1)[0]; 115 } 116 117 ubyte readUByte() { 118 return cast(ubyte) readByte(); 119 } 120 121 short readShort() { 122 ubyte[] b = readU(2); 123 switch(getEndianess()) { 124 case Endian.bigEndian: 125 return cast(short) (b[0] << 8) | b[1]; 126 case Endian.littleEndian: 127 return cast(short) (b[1] << 8) | b[0]; 128 default: 129 return 0; 130 } 131 } 132 133 ushort readUShort() { 134 return cast(ushort) readShort(); 135 } 136 137 uint readUInt24_LE() { 138 return (readUByte()) | (readUByte() << 8) | (readUByte() << 16); 139 } 140 141 int readInt() { 142 ubyte[] b = readU(4); 143 144 switch(getEndianess()) { 145 case Endian.bigEndian: 146 return ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | (b[3] & 0xFF); 147 case Endian.littleEndian: 148 return ((b[3] & 0xFF) << 24) | ((b[2] & 0xFF) << 16) | ((b[1] & 0xFF) << 8) | (b[0] & 0xFF); 149 default: 150 return 0; 151 } 152 } 153 154 uint readUInt() { 155 return cast(uint) readInt(); 156 } 157 158 long readLong() { 159 ubyte[] array = cast(ubyte[]) read(8); 160 switch(getEndianess()) { 161 case Endian.bigEndian: 162 return ((cast(long) array[0] & 0xff) << 56) | ((cast(long) array[1] & 0xff) << 48) | ((cast(long) array[2] & 0xff) << 40) | ((cast(long) array[3] & 0xff) << 32) | ((cast(long) array[4] & 0xff) << 24) | ((cast(long) array[5] & 0xff) << 16) | ((cast(long) array[6] & 0xff) << 8) | ((cast(long) array[7] & 0xff)); 163 case Endian.littleEndian: 164 return ((cast(long) array[7] & 0xff) << 56) | ((cast(long) array[6] & 0xff) << 48) | ((cast(long) array[5] & 0xff) << 40) | ((cast(long) array[4] & 0xff) << 32) | ((cast(long) array[3] & 0xff) << 24) | ((cast(long) array[2] & 0xff) << 16) | ((cast(long) array[1] & 0xff) << 8) | ((cast(long) array[0] & 0xff)); 165 default: 166 return 0; 167 } 168 } 169 170 ulong readULong() { 171 return cast(ulong) readLong(); 172 } 173 174 string readStrUTF8() { 175 ushort len = readUShort(); 176 return cast(string) (cast(ubyte[]) read(len)); 177 } 178 179 void readSysAddress(ref string ip, ref ushort port) { 180 ubyte version_ = readUByte(); 181 switch(version_) { 182 case 4: 183 ubyte[] addressBytes = readU(4); 184 ip = to!string(~addressBytes[0]) ~ "." ~ to!string(~addressBytes[1]) ~ "." ~ to!string(~addressBytes[2]) ~ "." ~ to!string(~addressBytes[3]); 185 port = readUShort(); 186 break; 187 default: 188 throw new DecodeException("Invalid IP version: " ~ to!string(version_)); 189 } 190 } 191 192 //Write methods 193 void writeByte(in byte b) { 194 write([b]); 195 } 196 197 void writeUByte(in ubyte b) { 198 writeByte(cast(byte) b); 199 } 200 201 void writeShort(in short s) { 202 switch(getEndianess()) { 203 case Endian.bigEndian: 204 write(cast(byte[]) [(s >> 8) & 0xFF, s & 0xFF]); 205 break; 206 case Endian.littleEndian: 207 write(cast(byte[]) [s & 0xFF, (s >> 8) & 0xFF]); 208 break; 209 default: 210 break; 211 } 212 } 213 214 void writeUShort(in ushort s) { 215 writeShort(cast(short) s); 216 } 217 218 void writeUInt24_LE(in uint i24) { 219 write(cast(byte[]) [i24 & 0xFF, (i24 >> 8) & 0xFF, (i24 >> 16) & 0xFF]); 220 } 221 222 void writeInt(in int i) { 223 byte[] bytes; 224 bytes.length = 4; 225 bytes[0] = cast(byte) ((i >> 24) & 0xFF); 226 bytes[1] = cast(byte) ((i >> 16) & 0xFF); 227 bytes[2] = cast(byte) ((i >> 8) & 0xFF); 228 bytes[3] = cast(byte) (i & 0xFF); 229 230 if(getEndianess() == Endian.littleEndian) 231 reverse(bytes); 232 233 write(bytes); 234 } 235 236 void writeUInt(in uint i) { 237 writeInt(cast(int) i); 238 } 239 240 void writeLong(in long l) { 241 byte[] bytes; 242 bytes.length = 8; 243 bytes[0] = cast(byte) ((l >> 56) & 0xFF); 244 bytes[1] = cast(byte) ((l >> 48) & 0xFF); 245 bytes[2] = cast(byte) ((l >> 40) & 0xFF); 246 bytes[3] = cast(byte) ((l >> 32) & 0xFF); 247 bytes[4] = cast(byte) ((l >> 24) & 0xFF); 248 bytes[5] = cast(byte) ((l >> 16) & 0xFF); 249 bytes[6] = cast(byte) ((l >> 8) & 0xFF); 250 bytes[7] = cast(byte) (l & 0xFF); 251 252 if(getEndianess() == Endian.littleEndian) 253 reverse(bytes); 254 255 write(bytes); 256 } 257 258 void writeULong(in ulong l) { 259 writeLong(cast(long) l); 260 } 261 262 void writeStrUTF8(in string s) { 263 byte[] data = cast(byte[]) s; 264 writeUShort(cast(ushort) data.length); 265 write(data); 266 } 267 268 void writeSysAddress(in string ip, in ushort port, in ubyte version_ = 4) { 269 enforce(version_ == 4, new EncodeException("Invalid IP version: " ~ to!string(version_))); 270 271 import std.array; 272 273 writeUByte(version_); 274 foreach(s; split(ip, ".")) { 275 writeUByte(~to!ubyte(s)); 276 } 277 writeUShort(port); 278 } 279 280 //Util methods 281 282 /** 283 * Skip "bytes" amount of bytes. The buffer's 284 * position will increment by that amount. 285 */ 286 void skip(in uint bytes) { 287 setPosition(getPosition() + bytes); 288 } 289 290 void trimTo(in uint bytes) { 291 enforce(getSize() - bytes > 0 && getSize() - bytes >= getPosition(), new InvalidArgumentException("Can't trim by value greater than buffer size/position: " ~ to!string(bytes) ~ ", " ~ to!string(getPosition()))); 292 buffer.length = bytes; 293 } 294 295 void clear() { 296 buffer = null; 297 position = 0; 298 299 } 300 301 //Getters/Setters 302 303 uint getPosition() { 304 return this.position; 305 } 306 307 void setPosition(in uint position) { 308 this.position = position; 309 } 310 311 uint getSize() { 312 return cast(uint) this.buffer.length; 313 } 314 315 uint getRemainingLength() { 316 return cast(uint) (this.buffer.length - getPosition()); 317 } 318 319 Endian getEndianess() { 320 return this.endianess; 321 } 322 323 void setEndianness(in Endian endianess) { 324 this.endianess = endianess; 325 } 326 327 byte[] getBuffer() { 328 return this.buffer; 329 } 330 } 331 332 class OutOfBoundsException : Exception { 333 this() { 334 super("Data is out of bounds"); 335 } 336 337 this(string msg) { 338 super(msg); 339 } 340 } 341 342 class EncodeException : Exception { 343 this(string msg) { 344 super(msg); 345 } 346 } 347 348 class DecodeException : Exception { 349 this(string msg) { 350 super(msg); 351 } 352 }