aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/Manual/en/BH_IO.pod50
-rw-r--r--doc/Manual/ru/BH_IO.pod51
-rw-r--r--include/BH/IO.h19
-rw-r--r--src/Buffer.c9
-rw-r--r--src/Bytes.c10
-rw-r--r--src/IO.c50
-rw-r--r--src/Platform/Dummy/File.c6
-rw-r--r--src/Platform/Posix/File.c9
-rw-r--r--src/Platform/Win32/File.c9
-rw-r--r--test/include/BH/Unit.h14
-rw-r--r--test/tests/TestBuffer.c2
-rw-r--r--test/tests/TestBytes.c3
-rw-r--r--test/tests/TestFile.c2
-rw-r--r--test/tests/TestReadLine.c76
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;
+}
+
diff --git a/src/IO.c b/src/IO.c
index caca243..8bd1568 100644
--- a/src/IO.c
+++ b/src/IO.c
@@ -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();
+}