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