diff options
author | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
---|---|---|
committer | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
commit | abef6da56913f1c55528103e60a50451a39628b1 (patch) | |
tree | b3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Stream.c |
initial commit
Diffstat (limited to 'src/Stream.c')
-rw-r--r-- | src/Stream.c | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/src/Stream.c b/src/Stream.c new file mode 100644 index 0000000..0082723 --- /dev/null +++ b/src/Stream.c @@ -0,0 +1,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); +} |