diff --git a/doc/xml/release.xml b/doc/xml/release.xml
index 9ab0569cd..49ffc4b55 100644
--- a/doc/xml/release.xml
+++ b/doc/xml/release.xml
@@ -94,6 +94,10 @@
Info objects now parse JSON and use specified storage.
+
+ Add ioReadLine()
/ioWriteLine()
to IoRead
/IoWrite
objects.
+
+
Add helper for repository storage.
diff --git a/src/common/io/read.c b/src/common/io/read.c
index dff43b75d..83276766c 100644
--- a/src/common/io/read.c
+++ b/src/common/io/read.c
@@ -1,6 +1,8 @@
/***********************************************************************************************************************************
IO Read Interface
***********************************************************************************************************************************/
+#include
+
#include "common/debug.h"
#include "common/io/io.h"
#include "common/io/read.intern.h"
@@ -17,6 +19,7 @@ struct IoRead
IoReadInterface interface; // Driver interface
IoFilterGroup *filterGroup; // IO filters
Buffer *input; // Input buffer
+ Buffer *output; // Output buffer (holds extra data from line read)
bool eofAll; // Is the read done (read and filters complete)?
@@ -105,11 +108,12 @@ ioReadEofDriver(const IoRead *this)
/***********************************************************************************************************************************
Read data from IO and process filters
***********************************************************************************************************************************/
-size_t
-ioRead(IoRead *this, Buffer *buffer)
+static void
+ioReadInternal(IoRead *this, Buffer *buffer)
{
FUNCTION_DEBUG_BEGIN(logLevelTrace);
FUNCTION_DEBUG_PARAM(IO_READ, this);
+ FUNCTION_DEBUG_PARAM(BUFFER, buffer);
FUNCTION_TEST_ASSERT(this != NULL);
FUNCTION_TEST_ASSERT(buffer != NULL);
@@ -117,8 +121,6 @@ ioRead(IoRead *this, Buffer *buffer)
FUNCTION_DEBUG_END();
// Loop until EOF or the output buffer is full
- size_t outputRemains = bufRemains(buffer);
-
while (!this->eofAll && bufRemains(buffer) > 0)
{
// Process input buffer again to get more output
@@ -147,9 +149,103 @@ ioRead(IoRead *this, Buffer *buffer)
this->eofAll = ioReadEofDriver(this) && ioFilterGroupDone(this->filterGroup);
}
+ FUNCTION_DEBUG_RESULT_VOID();
+}
+
+/***********************************************************************************************************************************
+Read data and use buffered line read output when present
+***********************************************************************************************************************************/
+size_t
+ioRead(IoRead *this, Buffer *buffer)
+{
+ FUNCTION_DEBUG_BEGIN(logLevelTrace);
+ FUNCTION_DEBUG_PARAM(IO_READ, this);
+ FUNCTION_DEBUG_PARAM(BUFFER, buffer);
+ FUNCTION_DEBUG_PARAM(BUFFER, this->output);
+
+ FUNCTION_TEST_ASSERT(this != NULL);
+ FUNCTION_TEST_ASSERT(buffer != NULL);
+ FUNCTION_TEST_ASSERT(this->opened && !this->closed);
+ FUNCTION_DEBUG_END();
+
+ // Store size of remaining portion of buffer to calculate total read at the end
+ size_t outputRemains = bufRemains(buffer);
+
+ // Use any data in the output buffer left over from a line read
+ if (this->output != NULL && bufUsed(this->output) > 0 && bufRemains(buffer) > 0)
+ {
+ // Determine how much data should be copied
+ size_t size = bufUsed(this->output) > bufRemains(buffer) ? bufRemains(buffer) : bufUsed(this->output);
+
+ // Copy data to the user buffer
+ bufCatSub(buffer, this->output, 0, size);
+
+ // Remove copied data from the output buffer
+ memmove(bufPtr(this->output), bufPtr(this->output) + size, bufUsed(this->output) - size);
+ bufUsedSet(this->output, bufUsed(this->output) - size);
+ }
+
+ // Read data
+ ioReadInternal(this, buffer);
+
FUNCTION_DEBUG_RESULT(SIZE, outputRemains - bufRemains(buffer));
}
+/***********************************************************************************************************************************
+Read linefeed-terminated string
+***********************************************************************************************************************************/
+String *
+ioReadLine(IoRead *this)
+{
+ FUNCTION_DEBUG_BEGIN(logLevelTrace);
+ FUNCTION_DEBUG_PARAM(IO_READ, this);
+ FUNCTION_DEBUG_PARAM(BUFFER, this->output);
+
+ FUNCTION_TEST_ASSERT(this != NULL);
+ FUNCTION_TEST_ASSERT(this->opened && !this->closed);
+ FUNCTION_DEBUG_END();
+
+ // Allocate the output buffer if it has not already been allocated. This buffer is not allocated at object creation because it
+ // is not always used.
+ if (this->output == NULL)
+ {
+ MEM_CONTEXT_BEGIN(this->memContext)
+ {
+ this->output = bufNew(ioBufferSize());
+ }
+ MEM_CONTEXT_END();
+ }
+
+ // Read more data if there is any. The entire string we are searching for must fit within the buffer so we'll make sure that
+ // the buffer is full
+ ioReadInternal(this, this->output);
+
+ // If some data was read search for a linefeed
+ String *result = NULL;
+
+ if (bufUsed(this->output) > 0)
+ {
+ // Search for a linefeed in the buffer
+ char *linefeed = memchr(bufPtr(this->output), '\n', bufUsed(this->output));
+
+ // A linefeed was found so get the string
+ if (linefeed != NULL)
+ {
+ // Get the string size
+ size_t size = (size_t)(linefeed - (char *)bufPtr(this->output) + 1);
+
+ // Create the string
+ result = strNewN((char *)bufPtr(this->output), size - 1);
+
+ // Remove string from the output buffer
+ memmove(bufPtr(this->output), bufPtr(this->output) + size, bufUsed(this->output) - size);
+ bufUsedSet(this->output, bufUsed(this->output) - size);
+ }
+ }
+
+ FUNCTION_DEBUG_RESULT(STRING, result);
+}
+
/***********************************************************************************************************************************
Close the IO
***********************************************************************************************************************************/
diff --git a/src/common/io/read.h b/src/common/io/read.h
index c619b1fa6..22e0ba85d 100644
--- a/src/common/io/read.h
+++ b/src/common/io/read.h
@@ -22,6 +22,7 @@ Functions
***********************************************************************************************************************************/
bool ioReadOpen(IoRead *this);
size_t ioRead(IoRead *this, Buffer *buffer);
+String *ioReadLine(IoRead *this);
void ioReadClose(IoRead *this);
/***********************************************************************************************************************************
diff --git a/src/common/io/write.c b/src/common/io/write.c
index 53936d03c..141aead2c 100644
--- a/src/common/io/write.c
+++ b/src/common/io/write.c
@@ -1,6 +1,8 @@
/***********************************************************************************************************************************
IO Write Interface
***********************************************************************************************************************************/
+#include
+
#include "common/debug.h"
#include "common/io/io.h"
#include "common/io/write.intern.h"
@@ -112,6 +114,37 @@ ioWrite(IoWrite *this, const Buffer *buffer)
FUNCTION_DEBUG_RESULT_VOID();
}
+/***********************************************************************************************************************************
+Write linefeed-terminated string
+***********************************************************************************************************************************/
+void
+ioWriteLine(IoWrite *this, const String *string)
+{
+ FUNCTION_DEBUG_BEGIN(logLevelTrace);
+ FUNCTION_DEBUG_PARAM(IO_WRITE, this);
+ FUNCTION_DEBUG_PARAM(STRING, string);
+
+ FUNCTION_TEST_ASSERT(this != NULL);
+ FUNCTION_TEST_ASSERT(string != NULL);
+ FUNCTION_TEST_ASSERT(this->opened && !this->closed);
+ FUNCTION_DEBUG_END();
+
+ MEM_CONTEXT_TEMP_BEGIN()
+ {
+ // Load a buffer with the linefeed-terminated string
+ Buffer *buffer = bufNew(strSize(string) + 1);
+ memcpy(bufPtr(buffer), strPtr(string), strSize(string));
+ bufPtr(buffer)[strSize(string)] = '\n';
+ bufUsedSet(buffer, bufSize(buffer));
+
+ // Write the string
+ ioWrite(this, buffer);
+ }
+ MEM_CONTEXT_TEMP_END()
+
+ FUNCTION_DEBUG_RESULT_VOID();
+}
+
/***********************************************************************************************************************************
Close the IO and write any additional data that has not been written yet
***********************************************************************************************************************************/
diff --git a/src/common/io/write.h b/src/common/io/write.h
index 57bbebb12..9488eaa2e 100644
--- a/src/common/io/write.h
+++ b/src/common/io/write.h
@@ -21,6 +21,7 @@ Functions
***********************************************************************************************************************************/
void ioWriteOpen(IoWrite *this);
void ioWrite(IoWrite *this, const Buffer *buffer);
+void ioWriteLine(IoWrite *this, const String *string);
void ioWriteClose(IoWrite *this);
/***********************************************************************************************************************************
diff --git a/src/storage/driver/posix/fileRead.c b/src/storage/driver/posix/fileRead.c
index d98d11201..6cc2ae01c 100644
--- a/src/storage/driver/posix/fileRead.c
+++ b/src/storage/driver/posix/fileRead.c
@@ -122,7 +122,7 @@ storageDriverPosixFileRead(StorageDriverPosixFileRead *this, Buffer *buffer)
size_t expectedBytes = bufRemains(buffer);
actualBytes = read(this->handle, bufRemainsPtr(buffer), expectedBytes);
- // Error occurred during write
+ // Error occurred during read
if (actualBytes == -1)
THROW_SYS_ERROR_FMT(FileReadError, "unable to read '%s'", strPtr(this->name));
diff --git a/test/src/module/common/ioTest.c b/test/src/module/common/ioTest.c
index 92f97bb88..f432a0529 100644
--- a/test/src/module/common/ioTest.c
+++ b/test/src/module/common/ioTest.c
@@ -315,6 +315,46 @@ testRun(void)
TEST_RESULT_VOID(ioFilterGroupFree(filterGroup), " free filter group object");
TEST_RESULT_VOID(ioFilterGroupFree(NULL), " free NULL filter group object");
+
+ // Mixed line and buffer read
+ // -------------------------------------------------------------------------------------------------------------------------
+ ioBufferSizeSet(5);
+ read = ioBufferReadIo(ioBufferReadNew(bufNewZ("AAA123\n1234\n\n12\nBDDDEFF")));
+ ioReadOpen(read);
+ buffer = bufNew(3);
+
+ // Start with a buffer read
+ TEST_RESULT_INT(ioRead(read, buffer), 3, "read buffer");
+ TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "AAA", " check buffer");
+
+ // Do line reads of various lengths
+ TEST_RESULT_STR(strPtr(ioReadLine(read)), "123", "read line");
+ TEST_RESULT_STR(strPtr(ioReadLine(read)), "1234", "read line");
+ TEST_RESULT_STR(strPtr(ioReadLine(read)), "", "read line");
+ TEST_RESULT_STR(strPtr(ioReadLine(read)), "12", "read line");
+
+ // Read what was left in the line buffer
+ TEST_RESULT_INT(ioRead(read, buffer), 0, "read buffer");
+ bufUsedSet(buffer, 2);
+ TEST_RESULT_INT(ioRead(read, buffer), 1, "read buffer");
+ TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "AAB", " check buffer");
+ bufUsedSet(buffer, 0);
+
+ // Now do a full buffer read from the input
+ TEST_RESULT_INT(ioRead(read, buffer), 3, "read buffer");
+ TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "DDD", " check buffer");
+
+ // Read line doesn't work without a linefeed
+ TEST_RESULT_STR(strPtr(ioReadLine(read)), NULL, "read line");
+
+ // But those bytes can be picked up by a buffer read
+ bufUsedSet(buffer, 0);
+ TEST_RESULT_INT(ioRead(read, buffer), 3, "read buffer");
+ TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "EFF", " check buffer");
+
+ // Nothing left to read
+ TEST_RESULT_STR(strPtr(ioReadLine(read)), NULL, "read line");
+ TEST_RESULT_INT(ioRead(read, buffer), 0, "read buffer");
}
// *****************************************************************************************************************************
@@ -359,16 +399,16 @@ testRun(void)
TEST_RESULT_VOID(ioWriteFilterGroupSet(ioBufferWriteIo(bufferWrite), filterGroup), " add filter group to write io");
TEST_RESULT_VOID(ioWriteOpen(ioBufferWriteIo(bufferWrite)), " open buffer write object");
- TEST_RESULT_VOID(ioWrite(ioBufferWriteIo(bufferWrite), bufNewZ("ABC")), " write 3 bytes");
+ TEST_RESULT_VOID(ioWriteLine(ioBufferWriteIo(bufferWrite), strNew("AB")), " write string");
TEST_RESULT_VOID(ioWrite(ioBufferWriteIo(bufferWrite), bufNew(0)), " write 0 bytes");
TEST_RESULT_VOID(ioWrite(ioBufferWriteIo(bufferWrite), NULL), " write 0 bytes");
- TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "AABBCC", " check write");
+ TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "AABB\n\n", " check write");
TEST_RESULT_VOID(ioWrite(ioBufferWriteIo(bufferWrite), bufNewZ("12345")), " write 4 bytes");
- TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "AABBCC112233445", " check write");
+ TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "AABB\n\n112233445", " check write");
TEST_RESULT_VOID(ioWriteClose(ioBufferWriteIo(bufferWrite)), " close buffer write object");
- TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "AABBCC1122334455XXX", " check write after close");
+ TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "AABB\n\n1122334455XXX", " check write after close");
TEST_RESULT_PTR(ioWriteFilterGroup(ioBufferWriteIo(bufferWrite)), filterGroup, " check filter group");
TEST_RESULT_UINT(