diff options
| author | Mikhail Romanko <me@blankhex.com> | 2025-11-08 18:50:13 +0300 |
|---|---|---|
| committer | Mikhail Romanko <me@blankhex.com> | 2025-11-08 18:50:13 +0300 |
| commit | 6d02598e20a126ea159ef4529340b93e5fa7b5ea (patch) | |
| tree | 09f6f4e971ed3cb43ec8f86fe73da2b3d5d26ff0 | |
| parent | 44057e96f33f110cf182ef7df697b21e779e5aae (diff) | |
| download | bhlib-6d02598e20a126ea159ef4529340b93e5fa7b5ea.tar.gz | |
Add IO type checking functions, add ReadLine
| -rw-r--r-- | doc/Manual/en/BH_IO.pod | 50 | ||||
| -rw-r--r-- | doc/Manual/ru/BH_IO.pod | 51 | ||||
| -rw-r--r-- | include/BH/IO.h | 19 | ||||
| -rw-r--r-- | src/Buffer.c | 9 | ||||
| -rw-r--r-- | src/Bytes.c | 10 | ||||
| -rw-r--r-- | src/IO.c | 50 | ||||
| -rw-r--r-- | src/Platform/Dummy/File.c | 6 | ||||
| -rw-r--r-- | src/Platform/Posix/File.c | 9 | ||||
| -rw-r--r-- | src/Platform/Win32/File.c | 9 | ||||
| -rw-r--r-- | test/include/BH/Unit.h | 14 | ||||
| -rw-r--r-- | test/tests/TestBuffer.c | 2 | ||||
| -rw-r--r-- | test/tests/TestBytes.c | 3 | ||||
| -rw-r--r-- | test/tests/TestFile.c | 2 | ||||
| -rw-r--r-- | test/tests/TestReadLine.c | 76 |
14 files changed, 310 insertions, 0 deletions
diff --git a/doc/Manual/en/BH_IO.pod b/doc/Manual/en/BH_IO.pod index 3e9dcab..62ef4d8 100644 --- a/doc/Manual/en/BH_IO.pod +++ b/doc/Manual/en/BH_IO.pod @@ -177,6 +177,13 @@ The optional parameter I<result> returns 0 or an error code. This function returns a pointer to a new BH_IO object or NULL. +=head2 BH_IOIsFile + + int BH_IOIsFile(BH_IO *device); + +Checks if I/O device is a file. + + =head2 BH_BufferNew BH_IO *BH_BufferNew(BH_IO *device, @@ -193,6 +200,13 @@ If successful, this function returns a pointer to the new BH_IO object or NULL in case of an error. +=head2 BH_IOIsBuffer + + int BH_IOIsBuffer(BH_IO *device); + +Checks if I/O device is a buffer. + + =head2 BH_BytesNew BH_IO *BH_BytesNew(char *data, @@ -207,6 +221,13 @@ If successful, this function returns a pointer to the new BH_IO object or NULL in case of an error. +=head2 BH_IOIsBytes + + int BH_IOIsBytes(BH_IO *device); + +Checks if I/O device is a memory region/bytes. + + =head2 BH_IOFree void BH_IOFree(BH_IO *device); @@ -456,6 +477,35 @@ This function is equivalent to the following code: (BH_IOFlags(device) & BH_IO_FLAG_EOF) +=head2 BH_IOReadLine + + char *BH_IOReadLine(BH_IO *device, + char *str, + size_t size); + +Reads a line from I<device> into I<str>, up to I<size-1> bytes. + +Stops at I<\n> or EOF. The result is null-terminated. Partial lines may remain +in the stream if longer than buffer. + +Returns I<str> on success, NULL on error. + + +=head2 BH_IOReadLineFull + + char *BH_IOReadLineFull(BH_IO *device, + char *str, + size_t size); + +Reads a line from I<device> into I<str>, up to I<size-1> bytes. + +Stops at I<\n> or EOF. The result is null-terminated. Consumes the entire line +from the stream, discarding excess data if the line is too long. Ensures no +partial line remains. + +Returns I<str> on success, NULL on error. + + =head1 STRUCTURES diff --git a/doc/Manual/ru/BH_IO.pod b/doc/Manual/ru/BH_IO.pod index 14a8cdc..4d43b7f 100644 --- a/doc/Manual/ru/BH_IO.pod +++ b/doc/Manual/ru/BH_IO.pod @@ -178,6 +178,13 @@ BH_IO предоставляет разработчику возможность Данная функция возвращает указатель на новый BH_IO объект или NULL. +=head2 BH_IOIsFile + + int BH_IOIsFile(BH_IO *device); + +Проверяет, является ли устройство ввода-вывода файлом. + + =head2 BH_BufferNew BH_IO *BH_BufferNew(BH_IO *device, @@ -195,6 +202,13 @@ I<device>. NULL в случае ошибки. +=head2 BH_IOIsBuffer + + int BH_IOIsBuffer(BH_IO *device); + +Проверяет, является ли устройство ввода-вывода буфером. + + =head2 BH_BytesNew BH_IO *BH_BytesNew(char *data, @@ -209,6 +223,13 @@ NULL в случае ошибки. NULL в случае ошибки. +=head2 BH_IOIsBytes + + int BH_IOIsBytes(BH_IO *device); + +Проверяет, является ли устройство ввода-вывода регионом памяти. + + =head2 BH_IOFree void BH_IOFree(BH_IO *device); @@ -460,6 +481,36 @@ I<size>. (BH_IOFlags(device) & BH_IO_FLAG_EOF) +=head2 BH_IOReadLine + + char *BH_IOReadLine(BH_IO *device, + char *str, + size_t size); + +Читает строку размером до I<size - 1> байт из устройства ввода-вывода и +записывает данные в I<str>. Результирующая строка нуль-терминированная. + +Останавливается на символе I<\n> или конце потока (EOF). Если строка длиннее +буфера, её остаток остаётся в устройстве ввода-вывода для последующего чтения. + +Данная функция возвращает указатель на новый I<str> или NULL. + + +=head2 BH_IOReadLineFull + + char *BH_IOReadLineFull(BH_IO *device, + char *str, + size_t size); + +Читает строку размером до I<size - 1> байт из устройства ввода-вывода и +записывает данные в I<str>. Результирующая строка нуль-терминированная. + +Останавливается на символе I<\n> или конце потока (EOF). Полностью читает строку +из устройства ввода-вывода, отбрасывая избыточные данные. + +Данная функция возвращает указатель на новый I<str> или NULL. + + =head1 СТРУКТУРЫ ДАННЫХ diff --git a/include/BH/IO.h b/include/BH/IO.h index 5aad2ed..d3c9161 100644 --- a/include/BH/IO.h +++ b/include/BH/IO.h @@ -86,16 +86,25 @@ BH_IO *BH_FileNew(const char *path, int *result); +int BH_IOIsFile(BH_IO *device); + + BH_IO *BH_BufferNew(BH_IO *device, size_t size, int *result); +int BH_IOIsBuffer(BH_IO *device); + + BH_IO *BH_BytesNew(char *data, size_t size, int *result); +int BH_IOIsBytes(BH_IO *device); + + void BH_IOFree(BH_IO *device); @@ -155,4 +164,14 @@ int BH_IOEndOfFile(BH_IO *device); int BH_IOClear(BH_IO *device); +char *BH_IOReadLine(BH_IO *device, + char *str, + size_t size); + + +char *BH_IOReadLineFull(BH_IO *device, + char *str, + size_t size); + + #endif /* BH_IO_H */ diff --git a/src/Buffer.c b/src/Buffer.c index 69e8ea3..8f8c361 100644 --- a/src/Buffer.c +++ b/src/Buffer.c @@ -302,3 +302,12 @@ BH_IO *BH_BufferNew(BH_IO *device, return (BH_IO*)buffer; } + + +int BH_IOIsBuffer(BH_IO *device) +{ + if (!device) + return 0; + + return device->callback == (BH_IOCallback)bufferCallback; +} diff --git a/src/Bytes.c b/src/Bytes.c index 8949380..07242af 100644 --- a/src/Bytes.c +++ b/src/Bytes.c @@ -215,3 +215,13 @@ BH_IO *BH_BytesNew(char *data, return (BH_IO*)bytes; } + + +int BH_IOIsBytes(BH_IO *device) +{ + if (!device) + return 0; + + return device->callback == (BH_IOCallback)bytesCallback; +} + @@ -178,3 +178,53 @@ int BH_IOEndOfFile(BH_IO *device) return flags & BH_IO_FLAG_EOF; } + + +char *BH_IOReadLine(BH_IO *device, + char *str, + size_t size) +{ + size_t i, actual; + + if (size < 1) + return NULL; + + i = 0; + while (i < size - 1) + { + if (BH_IORead(device, str + i, 1, &actual) || actual != 1) + break; + + if (str[i++] == '\n') + break; + } + str[i] = 0; + return i ? str : NULL; +} + + +char *BH_IOReadLineFull(BH_IO *device, + char *str, + size_t size) +{ + size_t i, actual; + char data; + + if (size < 1) + return NULL; + + i = 0; + while (1) + { + if (BH_IORead(device, &data, 1, &actual) || actual != 1) + break; + + if (i < size - 1) + str[i++] = data; + + if (data == '\n') + break; + } + str[i] = 0; + return i ? str : NULL; +} diff --git a/src/Platform/Dummy/File.c b/src/Platform/Dummy/File.c index 5e5b515..d12a8fb 100644 --- a/src/Platform/Dummy/File.c +++ b/src/Platform/Dummy/File.c @@ -13,3 +13,9 @@ BH_IO *BH_FileNew(const char *path, return NULL; } + + +int BH_IOIsFile(BH_IO *device) +{ + return 0; +} diff --git a/src/Platform/Posix/File.c b/src/Platform/Posix/File.c index c91a46f..16a6109 100644 --- a/src/Platform/Posix/File.c +++ b/src/Platform/Posix/File.c @@ -306,3 +306,12 @@ BH_IO *BH_FileNew(const char *path, return (BH_IO*)file; } + + +int BH_IOIsFile(BH_IO *device) +{ + if (!device) + return 0; + + return device->callback == (BH_IOCallback)fileCallback; +} diff --git a/src/Platform/Win32/File.c b/src/Platform/Win32/File.c index 05a0d56..4d647fb 100644 --- a/src/Platform/Win32/File.c +++ b/src/Platform/Win32/File.c @@ -315,3 +315,12 @@ BH_IO *BH_FileNew(const char *path, return (BH_IO*)file; } + + +int BH_IOIsFile(BH_IO *device) +{ + if (!device) + return 0; + + return device->callback == (BH_IOCallback)fileCallback; +} diff --git a/test/include/BH/Unit.h b/test/include/BH/Unit.h index f0eab71..9f9b3bb 100644 --- a/test/include/BH/Unit.h +++ b/test/include/BH/Unit.h @@ -2,10 +2,13 @@ #define BH_UNIT_H #include <stdio.h> +#include <string.h> #include <math.h> + typedef int (*BH_UnitCallback)(void); + #define BH_VERIFY(e) \ do { \ if (!(e)) { \ @@ -35,6 +38,17 @@ typedef int (*BH_UnitCallback)(void); } while(0) +#define BH_VERIFY_STR_EQ(actual, expected) \ + do { \ + BH_VERIFY((actual) != NULL && (expected) != NULL); \ + if (strcmp((actual), (expected)) != 0) { \ + printf("%s:%d\tExpected '%s', got '%s'\n", \ + __FILE__, __LINE__, (expected), (actual)); \ + return -1; \ + } \ + } while(0) + + #define BH_UNIT_TEST(name) \ static int unit##name(void) diff --git a/test/tests/TestBuffer.c b/test/tests/TestBuffer.c index 05d6923..81425ef 100644 --- a/test/tests/TestBuffer.c +++ b/test/tests/TestBuffer.c @@ -7,6 +7,7 @@ BH_UNIT_TEST(Null) { BH_VERIFY(BH_BufferNew(NULL, 0, NULL) == NULL); + BH_VERIFY(BH_IOIsBuffer(NULL) == 0); return 0; } @@ -21,6 +22,7 @@ BH_UNIT_TEST(Write) memset(data, 0, 16); BH_VERIFY((io = BH_BytesNew(data, 16, NULL)) != NULL); BH_VERIFY((buffer = BH_BufferNew(io, 4, NULL)) != NULL); + BH_VERIFY(BH_IOIsBuffer(buffer)); BH_VERIFY(BH_IOWrite(buffer, "1234567", 7, &size) == BH_OK); BH_VERIFY(size == 7); diff --git a/test/tests/TestBytes.c b/test/tests/TestBytes.c index e718a96..21504bf 100644 --- a/test/tests/TestBytes.c +++ b/test/tests/TestBytes.c @@ -10,6 +10,8 @@ BH_UNIT_TEST(Null) BH_VERIFY(BH_BytesNew(NULL, 0, NULL) == NULL); BH_VERIFY(BH_BytesNew(&data, 0, NULL) == NULL); BH_VERIFY(BH_BytesNew(NULL, 1234, NULL) == NULL); + BH_VERIFY(BH_IOIsBytes(NULL) == 0); + return 0; } @@ -22,6 +24,7 @@ BH_UNIT_TEST(Read) size_t size; BH_VERIFY((io = BH_BytesNew(buffer1, 14, NULL)) != NULL); + BH_VERIFY(BH_IOIsBytes(io)); BH_VERIFY(BH_IORead(io, buffer2, 14, &size) == BH_OK); BH_VERIFY(size == 14); BH_VERIFY(memcmp(buffer1, buffer2, 14) == 0); diff --git a/test/tests/TestFile.c b/test/tests/TestFile.c index 37542bf..8ad7000 100644 --- a/test/tests/TestFile.c +++ b/test/tests/TestFile.c @@ -40,6 +40,7 @@ static int checkNull(void) BH_VERIFY(BH_IOCap(NULL, 0) != BH_OK); BH_VERIFY(BH_IOEndOfFile(NULL) != BH_OK); BH_VERIFY(BH_IOError(NULL) != BH_OK); + BH_VERIFY(BH_IOIsFile(NULL) == 0); BH_IOFree(NULL); return 0; @@ -58,6 +59,7 @@ static int checkNormal(void) /* Check operations for write only access */ BH_VERIFY((io = BH_FileNew(FILENAME1, BH_FILE_WRITE, NULL)) != NULL); + BH_VERIFY(BH_IOIsFile(io)); BH_VERIFY(BH_IOWrite(io, "1234567890", 10, &actual) == BH_OK); BH_VERIFY(actual == 10); diff --git a/test/tests/TestReadLine.c b/test/tests/TestReadLine.c new file mode 100644 index 0000000..de26026 --- /dev/null +++ b/test/tests/TestReadLine.c @@ -0,0 +1,76 @@ +#include <BH/Unit.h> +#include <BH/IO.h> +#include <string.h> + + +static const char testInput[] = + "this is a very long line that exceeds the buffer\n" + "short\n" + "another line\n"; + + +BH_UNIT_TEST(ReadLine) +{ + char buffer[32], *result; + BH_IO *io; + + BH_VERIFY((io = BH_BytesNew((char*)testInput, strlen(testInput), NULL))); + + /* Check splited read */ + BH_VERIFY((result = BH_IOReadLine(io, buffer, sizeof(buffer)))); + BH_VERIFY_STR_EQ(buffer, "this is a very long line that e"); + + BH_VERIFY((result = BH_IOReadLine(io, buffer, sizeof(buffer)))); + BH_VERIFY_STR_EQ(buffer, "xceeds the buffer\n"); + + /* Check smaller reads */ + BH_VERIFY((result = BH_IOReadLine(io, buffer, sizeof(buffer)))); + BH_VERIFY_STR_EQ(buffer, "short\n"); + + BH_VERIFY((result = BH_IOReadLine(io, buffer, sizeof(buffer)))); + BH_VERIFY_STR_EQ(buffer, "another line\n"); + + /* Check EOF */ + BH_VERIFY(!(result = BH_IOReadLine(io, buffer, sizeof(buffer)))); + BH_IOFree(io); + + return 0; +} + + +BH_UNIT_TEST(ReadLineFull) +{ + char buffer[32], *result; + BH_IO *io; + + BH_VERIFY((io = BH_BytesNew((char*)testInput, strlen(testInput), NULL))); + + /* Check truncated read */ + BH_VERIFY((result = BH_IOReadLineFull(io, buffer, sizeof(buffer)))); + BH_VERIFY_STR_EQ(buffer, "this is a very long line that e"); + + /* Check smaller reads */ + BH_VERIFY((result = BH_IOReadLineFull(io, buffer, sizeof(buffer)))); + BH_VERIFY_STR_EQ(buffer, "short\n"); + + BH_VERIFY((result = BH_IOReadLineFull(io, buffer, sizeof(buffer)))); + BH_VERIFY_STR_EQ(buffer, "another line\n"); + + /* Check EOF */ + BH_VERIFY(!(result = BH_IOReadLineFull(io, buffer, sizeof(buffer)))); + BH_IOFree(io); + + return 0; +} + + +int main(int argc, char **argv) +{ + BH_UNUSED(argc); + BH_UNUSED(argv); + + BH_UNIT_ADD(ReadLine); + BH_UNIT_ADD(ReadLineFull); + + return BH_UnitRun(); +} |
