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 }