summary refs log tree commit diff
path: root/src/Stream.c
blob: 00827233aa68c84762dffdfff96ce3a5779debc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
#include "Stream.h"
#include "String.h"
#include "Platform.h"
#include "Funcs.h"
#include "Errors.h"
#include "Utils.h"

/*########################################################################################################################*
*---------------------------------------------------------Stream----------------------------------------------------------*
*#########################################################################################################################*/
cc_result Stream_Read(struct Stream* s, cc_uint8* buffer, cc_uint32 count) {
	cc_uint32 read;
	cc_result res;

	while (count) {
		if ((res = s->Read(s, buffer, count, &read))) return res;
		if (!read) return ERR_END_OF_STREAM;

		buffer += read;
		count  -= read;
	}
	return 0;
}

cc_result Stream_Write(struct Stream* s, const cc_uint8* buffer, cc_uint32 count) {
	cc_uint32 write;
	cc_result res;

	while (count) {
		if ((res = s->Write(s, buffer, count, &write))) return res;
		if (!write) return ERR_END_OF_STREAM;

		buffer += write;
		count  -= write;
	}
	return 0;
}

static cc_result Stream_DefaultRead(struct Stream* s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
	return ERR_NOT_SUPPORTED;
}
static cc_result Stream_DefaultWrite(struct Stream* s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
	return ERR_NOT_SUPPORTED;
}
/* Slow way of reading a U8 integer through stream->Read(stream, 1, tmp) */
static cc_result Stream_DefaultReadU8(struct Stream* s, cc_uint8* data) {
	cc_uint32 modified;
	cc_result res = s->Read(s, data, 1, &modified);
	return res ? res : (modified ? 0 : ERR_END_OF_STREAM);
}

static cc_result Stream_DefaultSkip(struct Stream* s, cc_uint32 count) {
	cc_uint8 tmp[3584]; /* not quite 4 KB to avoid chkstk call */
	cc_uint32 toRead, read;
	cc_result res;

	while (count) {
		toRead = min(count, sizeof(tmp));
		if ((res = s->Read(s, tmp, toRead, &read))) return res;

		if (!read) return ERR_END_OF_STREAM;
		count -= read;
	}
	return 0;
}

static cc_result Stream_DefaultSeek(struct Stream* s, cc_uint32 pos) {
	return ERR_NOT_SUPPORTED;
}
static cc_result Stream_DefaultGet(struct Stream* s, cc_uint32* value) { 
	return ERR_NOT_SUPPORTED;
}
static cc_result Stream_DefaultClose(struct Stream* s) { return 0; }

void Stream_Init(struct Stream* s) {
	s->Read   = Stream_DefaultRead;
	s->ReadU8 = Stream_DefaultReadU8;
	s->Write  = Stream_DefaultWrite;
	s->Skip   = Stream_DefaultSkip;

	s->Seek   = Stream_DefaultSeek;
	s->Position = Stream_DefaultGet;
	s->Length = Stream_DefaultGet;
	s->Close  = Stream_DefaultClose;
}


/*########################################################################################################################*
*-------------------------------------------------------FileStream--------------------------------------------------------*
*#########################################################################################################################*/
static cc_result Stream_FileRead(struct Stream* s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
	return File_Read(s->meta.file, data, count, modified);
}
static cc_result Stream_FileWrite(struct Stream* s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
	return File_Write(s->meta.file, data, count, modified);
}
static cc_result Stream_FileClose(struct Stream* s) {
	cc_result res = File_Close(s->meta.file);
	s->meta.file = 0;
	return res;
}
static cc_result Stream_FileSkip(struct Stream* s, cc_uint32 count) {
	return File_Seek(s->meta.file, count, FILE_SEEKFROM_CURRENT);
}
static cc_result Stream_FileSeek(struct Stream* s, cc_uint32 position) {
	return File_Seek(s->meta.file, position, FILE_SEEKFROM_BEGIN);
}
static cc_result Stream_FilePosition(struct Stream* s, cc_uint32* position) {
	return File_Position(s->meta.file, position);
}
static cc_result Stream_FileLength(struct Stream* s, cc_uint32* length) {
	return File_Length(s->meta.file, length);
}

cc_result Stream_OpenFile(struct Stream* s, const cc_string* path) {
	cc_file file;
	cc_result res = File_Open(&file, path);
	Stream_FromFile(s, file);
	return res;
}

cc_result Stream_CreateFile(struct Stream* s, const cc_string* path) {
	cc_file file;
	cc_result res = File_Create(&file, path);
	Stream_FromFile(s, file);
	return res;
}

cc_result Stream_AppendFile(struct Stream* s, const cc_string* path) {
	cc_file file;
	cc_result res;
	
	if ((res = File_OpenOrCreate(&file, path)))        return res;
	if ((res = File_Seek(file, 0, FILE_SEEKFROM_END))) return res;
	Stream_FromFile(s, file);
	return res;
}

cc_result Stream_WriteAllTo(const cc_string* path, const cc_uint8* data, cc_uint32 length) {
	struct Stream stream;
	cc_result res, closeRes;

	res = Stream_CreateFile(&stream, path);
	if (res) return res;

	res      = Stream_Write(&stream, data, length);
	closeRes = stream.Close(&stream);
	return res ? res : closeRes;
}

void Stream_FromFile(struct Stream* s, cc_file file) {
	Stream_Init(s);
	s->meta.file = file;

	s->Read  = Stream_FileRead;
	s->Write = Stream_FileWrite;
	s->Close = Stream_FileClose;
	s->Skip  = Stream_FileSkip;
	s->Seek  = Stream_FileSeek;
	s->Position = Stream_FilePosition;
	s->Length   = Stream_FileLength;
}


/*########################################################################################################################*
*-----------------------------------------------------PortionStream-------------------------------------------------------*
*#########################################################################################################################*/
static cc_result Stream_PortionRead(struct Stream* s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
	struct Stream* source;
	cc_result res;

	count  = min(count, s->meta.portion.left);
	source = s->meta.portion.source;

	res = source->Read(source, data, count, modified);
	s->meta.portion.left -= *modified;
	return res;
}

static cc_result Stream_PortionReadU8(struct Stream* s, cc_uint8* data) {
	struct Stream* source;
	if (!s->meta.portion.left) return ERR_END_OF_STREAM;
	source = s->meta.portion.source;

	s->meta.portion.left--;
	return source->ReadU8(source, data);
}

static cc_result Stream_PortionSkip(struct Stream* s, cc_uint32 count) {
	struct Stream* source;
	cc_result res;

	if (count > s->meta.portion.left) return ERR_INVALID_ARGUMENT;
	source = s->meta.portion.source;

	res = source->Skip(source, count);
	if (!res) s->meta.portion.left -= count;
	return res;
}

static cc_result Stream_PortionPosition(struct Stream* s, cc_uint32* position) {
	*position = s->meta.portion.length - s->meta.portion.left; return 0;
}
static cc_result Stream_PortionLength(struct Stream* s, cc_uint32* length) {
	*length = s->meta.portion.length; return 0;
}

void Stream_ReadonlyPortion(struct Stream* s, struct Stream* source, cc_uint32 len) {
	Stream_Init(s);
	s->Read     = Stream_PortionRead;
	s->ReadU8   = Stream_PortionReadU8;
	s->Skip     = Stream_PortionSkip;
	s->Position = Stream_PortionPosition;
	s->Length   = Stream_PortionLength;

	s->meta.portion.source = source;
	s->meta.portion.left   = len;
	s->meta.portion.length = len;
}


/*########################################################################################################################*
*-----------------------------------------------------MemoryStream--------------------------------------------------------*
*#########################################################################################################################*/
static cc_result Stream_MemoryRead(struct Stream* s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
	count = min(count, s->meta.mem.left);
	Mem_Copy(data, s->meta.mem.cur, count);
	
	s->meta.mem.cur  += count; 
	s->meta.mem.left -= count;
	*modified = count;
	return 0;
}

static cc_result Stream_MemoryReadU8(struct Stream* s, cc_uint8* data) {
	if (!s->meta.mem.left) return ERR_END_OF_STREAM;

	*data = *s->meta.mem.cur;
	s->meta.mem.cur++; 
	s->meta.mem.left--;
	return 0;
}

static cc_result Stream_MemorySkip(struct Stream* s, cc_uint32 count) {
	if (count > s->meta.mem.left) return ERR_INVALID_ARGUMENT;

	s->meta.mem.cur  += count; 
	s->meta.mem.left -= count;
	return 0;
}

static cc_result Stream_MemorySeek(struct Stream* s, cc_uint32 position) {
	if (position >= s->meta.mem.length) return ERR_INVALID_ARGUMENT;

	s->meta.mem.cur  = s->meta.mem.base   + position;
	s->meta.mem.left = s->meta.mem.length - position;
	return 0;
}

static cc_result Stream_MemoryPosition(struct Stream* s, cc_uint32* position) {
	*position = s->meta.mem.length - s->meta.mem.left; return 0;
}
static cc_result Stream_MemoryLength(struct Stream* s, cc_uint32* length) {
	*length = s->meta.mem.length; return 0;
}

void Stream_ReadonlyMemory(struct Stream* s, void* data, cc_uint32 len) {
	Stream_Init(s);
	s->Read     = Stream_MemoryRead;
	s->ReadU8   = Stream_MemoryReadU8;
	s->Skip     = Stream_MemorySkip;
	s->Seek     = Stream_MemorySeek;
	s->Position = Stream_MemoryPosition;
	s->Length   = Stream_MemoryLength;

	s->meta.mem.cur    = (cc_uint8*)data;
	s->meta.mem.left   = len;
	s->meta.mem.length = len;
	s->meta.mem.base   = (cc_uint8*)data;
}


/*########################################################################################################################*
*----------------------------------------------------BufferedStream-------------------------------------------------------*
*#########################################################################################################################*/
static cc_result Stream_BufferedRead(struct Stream* s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
	struct Stream* source;
	cc_uint32 read;
	cc_result res;

	/* Refill buffer */
	if (!s->meta.buffered.left) {
		source               = s->meta.buffered.source; 
		s->meta.buffered.cur = s->meta.buffered.base;

		res = source->Read(source, s->meta.buffered.cur, s->meta.buffered.length, &read);
		if (res) return res;
		s->meta.buffered.left  = read;
		s->meta.buffered.end  += read;
	}
	
	count = min(count, s->meta.buffered.left);
	Mem_Copy(data, s->meta.buffered.cur, count);

	s->meta.buffered.cur  += count; 
	s->meta.buffered.left -= count;
	*modified = count;
	return 0;
}

static cc_result Stream_BufferedReadU8(struct Stream* s, cc_uint8* data) {
	if (!s->meta.buffered.left) return Stream_DefaultReadU8(s, data);

	*data = *s->meta.buffered.cur;
	s->meta.buffered.cur++; 
	s->meta.buffered.left--;
	return 0;
}

static cc_result Stream_BufferedSeek(struct Stream* s, cc_uint32 position) {
	struct Stream* source;
	cc_uint32 beg, len, offset;
	cc_result res;

	/* Check if seek position is within cached buffer */
	len = s->meta.buffered.left + (cc_uint32)(s->meta.buffered.cur - s->meta.buffered.base);
	beg = s->meta.buffered.end  - len;

	if (position >= beg && position < beg + len) {
		offset = position - beg;
		s->meta.buffered.cur  = s->meta.buffered.base + offset;
		s->meta.buffered.left = len - offset;
		return 0;
	}

	source = s->meta.buffered.source;
	res    = source->Seek(source, position);
	if (res) return res;

	s->meta.buffered.cur  = s->meta.buffered.base;
	s->meta.buffered.left = 0;
	s->meta.buffered.end  = position;
	return res;
}

void Stream_ReadonlyBuffered(struct Stream* s, struct Stream* source, void* data, cc_uint32 size) {
	Stream_Init(s);
	s->Read   = Stream_BufferedRead;
	s->ReadU8 = Stream_BufferedReadU8;
	s->Seek   = Stream_BufferedSeek;

	s->meta.buffered.left   = 0;
	s->meta.buffered.end    = 0;
	s->meta.buffered.cur    = (cc_uint8*)data;
	s->meta.buffered.base   = (cc_uint8*)data;
	s->meta.buffered.length = size;
	s->meta.buffered.source = source;
}


/*########################################################################################################################*
*-----------------------------------------------------CRC32Stream---------------------------------------------------------*
*#########################################################################################################################*/
static cc_result Stream_Crc32Write(struct Stream* stream, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) {
	struct Stream* source;
	cc_uint32 i, crc32 = stream->meta.crc32.crc32;

	/* TODO: Optimise this calculation */
	for (i = 0; i < count; i++) {
		crc32 = Utils_Crc32Table[(crc32 ^ data[i]) & 0xFF] ^ (crc32 >> 8);
	}
	stream->meta.crc32.crc32 = crc32;

	source = stream->meta.crc32.source;
	return source->Write(source, data, count, modified);
}

void Stream_WriteonlyCrc32(struct Stream* s, struct Stream* source) {
	Stream_Init(s);
	s->Write = Stream_Crc32Write;

	s->meta.crc32.source = source;
	s->meta.crc32.crc32  = 0xFFFFFFFFUL;
}


/*########################################################################################################################*
*-------------------------------------------------Read/Write primitives---------------------------------------------------*
*#########################################################################################################################*/
cc_uint16 Stream_GetU16_LE(const cc_uint8* data) {
	return (cc_uint16)(data[0] | (data[1] << 8));
}

cc_uint16 Stream_GetU16_BE(const cc_uint8* data) {
	return (cc_uint16)((data[0] << 8) | data[1]);
}

cc_uint32 Stream_GetU32_LE(const cc_uint8* data) {
	return (cc_uint32)(
		 (cc_uint32)data[0]        | ((cc_uint32)data[1] << 8) |
		((cc_uint32)data[2] << 16) | ((cc_uint32)data[3] << 24));
}

cc_uint32 Stream_GetU32_BE(const cc_uint8* data) {
	return (cc_uint32)(
		((cc_uint32)data[0] << 24) | ((cc_uint32)data[1] << 16) |
		((cc_uint32)data[2] << 8)  |  (cc_uint32)data[3]);
}

void Stream_SetU16_LE(cc_uint8* data, cc_uint16 value) {
	data[0] = (cc_uint8)(value      ); data[1] = (cc_uint8)(value >> 8 );
}

void Stream_SetU16_BE(cc_uint8* data, cc_uint16 value) {
	data[0] = (cc_uint8)(value >> 8 ); data[1] = (cc_uint8)(value      );
}

void Stream_SetU32_LE(cc_uint8* data, cc_uint32 value) {
	data[0] = (cc_uint8)(value      ); data[1] = (cc_uint8)(value >> 8 );
	data[2] = (cc_uint8)(value >> 16); data[3] = (cc_uint8)(value >> 24);
}

void Stream_SetU32_BE(cc_uint8* data, cc_uint32 value) {
	data[0] = (cc_uint8)(value >> 24); data[1] = (cc_uint8)(value >> 16);
	data[2] = (cc_uint8)(value >> 8 ); data[3] = (cc_uint8)(value);
}

cc_result Stream_ReadU32_LE(struct Stream* s, cc_uint32* value) {
	cc_uint8 data[4]; cc_result res;
	if ((res = Stream_Read(s, data, 4))) return res;
	*value = Stream_GetU32_LE(data); return 0;
}

cc_result Stream_ReadU32_BE(struct Stream* s, cc_uint32* value) {
	cc_uint8 data[4]; cc_result res;
	if ((res = Stream_Read(s, data, 4))) return res;
	*value = Stream_GetU32_BE(data); return 0;
}


/*########################################################################################################################*
*--------------------------------------------------Read/Write strings-----------------------------------------------------*
*#########################################################################################################################*/
cc_result Stream_ReadLine(struct Stream* s, cc_string* text) {
	cc_bool readAny = false;
	cc_codepoint cp;
	cc_result res;

	cc_uint8 tmp[8];
	cc_uint32 len;

	text->length = 0;
	for (;;) {
		len = 0;

		/* Read a UTF8 character from the stream */
		/* (in most cases it's just one byte) */
		do {
			if ((res = s->ReadU8(s, &tmp[len]))) break;
			len++;
		} while (!Convert_Utf8ToCodepoint(&cp, tmp, len));

		if (res == ERR_END_OF_STREAM) break;
		if (res) return res;

		readAny = true;
		/* Handle \r\n or \n line endings */
		if (cp == '\r') continue;
		if (cp == '\n') return 0;

		/* ignore byte order mark */
		if (cp == 0xFEFF) continue;
		String_Append(text, Convert_CodepointToCP437(cp));
	}
	return readAny ? 0 : ERR_END_OF_STREAM;
}

cc_result Stream_WriteLine(struct Stream* s, cc_string* text) {
	cc_uint8 buffer[2048 + 10]; /* some space for newline */
	const char* nl;
	cc_uint8* cur;
	cc_result res;
	int i, len = 0;

	for (i = 0; i < text->length; i++) {
		/* For extremely large strings */
		if (len >= 2048) {
			if ((res = Stream_Write(s, buffer, len))) return res;
			len = 0;
		}

		cur = buffer + len;
		len += Convert_CP437ToUtf8(text->buffer[i], cur);
	}
	
	nl = _NL;
	while (*nl) { buffer[len++] = *nl++; }
	return Stream_Write(s, buffer, len);
}