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 }