aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-08-08 20:00:43 -0400
committerDavid Robillard <d@drobilla.net>2022-01-28 21:57:07 -0500
commit9547c806dbf76d6afd1e324fc924abdf944c4bda (patch)
tree012ecf59cac892711d5bcf8f717f0ca579bbf7cd
parentdc01b7e301e91d0d7bfc358f569f4f3849471c52 (diff)
downloadserd-9547c806dbf76d6afd1e324fc924abdf944c4bda.tar.gz
serd-9547c806dbf76d6afd1e324fc924abdf944c4bda.tar.bz2
serd-9547c806dbf76d6afd1e324fc924abdf944c4bda.zip
Add a close function to SerdByteSink
This simplifies everything by replacing special cases with a more general close function. A type is no longer stored in the structure, so the other constructors are now essentially syntactic sugar for the universal serd_byte_sink_new_function().
-rw-r--r--include/serd/serd.h12
-rw-r--r--src/byte_sink.c57
-rw-r--r--src/byte_sink.h18
-rw-r--r--src/node_syntax.c8
-rw-r--r--test/test_byte_sink.c2
-rw-r--r--test/test_model.c35
-rw-r--r--test/test_reader_writer.c5
-rw-r--r--test/test_writer.c29
-rw-r--r--tools/console.c3
-rw-r--r--tools/serdi.c2
10 files changed, 90 insertions, 81 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 96890dbd..e99e4e6a 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -2388,6 +2388,8 @@ typedef struct SerdByteSinkImpl SerdByteSink;
Create a new byte sink that writes to a buffer.
The `buffer` is owned by the caller, but will be expanded as necessary.
+ Note that the string in the buffer will not be null terminated until the
+ byte sink is closed.
@param buffer Buffer to write output to.
*/
@@ -2415,15 +2417,17 @@ serd_byte_sink_new_filename(const char* SERD_NONNULL path, size_t block_size);
The `stream` will be passed to the `write_func`, which is compatible with
the standard C `fwrite` if `stream` is a `FILE*`.
- @param write_func Function called with bytes to consume.
+ @param write_func Stream write function, like `fwrite`.
+ @param close_func Stream close function, like `fclose`.
@param stream Context parameter passed to `sink`.
@param block_size Number of bytes to write per call.
*/
SERD_API
SerdByteSink* SERD_ALLOCATED
-serd_byte_sink_new_function(SerdWriteFunc SERD_NONNULL write_func,
- void* SERD_NULLABLE stream,
- size_t block_size);
+serd_byte_sink_new_function(SerdWriteFunc SERD_NONNULL write_func,
+ SerdStreamCloseFunc SERD_NULLABLE close_func,
+ void* SERD_NULLABLE stream,
+ size_t block_size);
/// Flush any pending output in `sink` to the underlying write function
SERD_API
diff --git a/src/byte_sink.c b/src/byte_sink.c
index 42d12f7b..febd11cd 100644
--- a/src/byte_sink.c
+++ b/src/byte_sink.c
@@ -30,33 +30,39 @@
# include <fcntl.h>
#endif
+static int
+close_buffer(void* const stream)
+{
+ serd_buffer_sink("", 1, 1, stream); // Write null terminator
+
+ return 0;
+}
+
SerdByteSink*
serd_byte_sink_new_buffer(SerdBuffer* const buffer)
{
assert(buffer);
- SerdByteSink* sink = (SerdByteSink*)calloc(1, sizeof(SerdByteSink));
-
- sink->write_func = serd_buffer_sink;
- sink->stream = buffer;
- sink->block_size = 1;
- sink->type = TO_BUFFER;
-
- return sink;
+ return serd_byte_sink_new_function(
+ serd_buffer_sink, close_buffer, buffer, 1u);
}
-static SerdByteSink*
-serd_byte_sink_new_internal(const SerdWriteFunc write_func,
- void* const stream,
- const size_t block_size,
- const SerdByteSinkType type)
+SerdByteSink*
+serd_byte_sink_new_function(const SerdWriteFunc write_func,
+ const SerdStreamCloseFunc close_func,
+ void* const stream,
+ const size_t block_size)
{
+ if (!block_size) {
+ return NULL;
+ }
+
SerdByteSink* sink = (SerdByteSink*)calloc(1, sizeof(SerdByteSink));
sink->write_func = write_func;
+ sink->close_func = close_func;
sink->stream = stream;
sink->block_size = block_size;
- sink->type = type;
if (block_size > 1) {
sink->buf = (char*)serd_allocate_buffer(block_size);
@@ -83,20 +89,8 @@ serd_byte_sink_new_filename(const char* const path, const size_t block_size)
posix_fadvise(fileno(file), 0, 0, POSIX_FADV_SEQUENTIAL);
#endif
- return serd_byte_sink_new_internal(
- (SerdWriteFunc)fwrite, file, block_size, TO_FILENAME);
-}
-
-SerdByteSink*
-serd_byte_sink_new_function(const SerdWriteFunc write_func,
- void* const stream,
- const size_t block_size)
-{
- assert(write_func);
-
- return block_size ? serd_byte_sink_new_internal(
- write_func, stream, block_size, TO_FUNCTION)
- : NULL;
+ return serd_byte_sink_new_function(
+ (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, file, block_size);
}
void
@@ -104,7 +98,7 @@ serd_byte_sink_flush(SerdByteSink* sink)
{
assert(sink);
- if (sink->block_size > 1 && sink->size > 0) {
+ if (sink->stream && sink->block_size > 1 && sink->size > 0) {
sink->write_func(sink->buf, 1, sink->size, sink->stream);
sink->size = 0;
}
@@ -117,12 +111,13 @@ serd_byte_sink_close(SerdByteSink* sink)
serd_byte_sink_flush(sink);
- if (sink->type == TO_FILENAME && sink->stream) {
- const int st = fclose((FILE*)sink->stream);
+ if (sink->stream && sink->close_func) {
+ const int st = sink->close_func(sink->stream);
sink->stream = NULL;
return st ? SERD_ERR_UNKNOWN : SERD_SUCCESS;
}
+ sink->stream = NULL;
return SERD_SUCCESS;
}
diff --git a/src/byte_sink.h b/src/byte_sink.h
index f023f180..d117a589 100644
--- a/src/byte_sink.h
+++ b/src/byte_sink.h
@@ -22,19 +22,13 @@
#include <stddef.h>
#include <string.h>
-typedef enum {
- TO_BUFFER, ///< Writing to a user-provided buffer
- TO_FILENAME, ///< Writing to a file we opened
- TO_FUNCTION, ///< Writing to a user-provided function
-} SerdByteSinkType;
-
struct SerdByteSinkImpl {
- SerdWriteFunc write_func; ///< User sink for TO_FUNCTION
- void* stream; ///< User data for write_func
- char* buf; ///< Local buffer iff block_size > 1
- size_t size; ///< Bytes written so far in this chunk
- size_t block_size; ///< Size of chunks to write
- SerdByteSinkType type; ///< Type of output
+ SerdWriteFunc write_func; ///< User sink for TO_FUNCTION
+ SerdStreamCloseFunc close_func; ///< Optional function to close stream
+ void* stream; ///< User data for write_func
+ char* buf; ///< Local buffer iff block_size > 1
+ size_t size; ///< Bytes written so far in this chunk
+ size_t block_size; ///< Size of chunks to write
};
static inline size_t
diff --git a/src/node_syntax.c b/src/node_syntax.c
index b1d10a5d..0c45e33a 100644
--- a/src/node_syntax.c
+++ b/src/node_syntax.c
@@ -99,8 +99,12 @@ serd_node_to_syntax_in(const SerdNode* const node,
SerdWriter* const writer = serd_writer_new(world, syntax, 0, env, out);
char* result = NULL;
- if (!serd_writer_write_node(writer, node) && !serd_writer_finish(writer)) {
- result = serd_buffer_sink_finish(&buffer);
+ if (!serd_writer_write_node(writer, node) && !serd_writer_finish(writer) &&
+ !serd_byte_sink_close(out)) {
+ result = (char*)buffer.buf;
+ } else {
+ serd_byte_sink_close(out);
+ free(buffer.buf);
}
serd_writer_free(writer);
diff --git a/test/test_byte_sink.c b/test/test_byte_sink.c
index 1982a436..71f1e62d 100644
--- a/test/test_byte_sink.c
+++ b/test/test_byte_sink.c
@@ -26,7 +26,7 @@ main(void)
{
assert(!serd_byte_sink_new_filename("file.ttl", 0));
assert(!serd_byte_sink_new_filename("/does/not/exist.ttl", 1));
- assert(!serd_byte_sink_new_function((SerdWriteFunc)fwrite, NULL, 0));
+ assert(!serd_byte_sink_new_function((SerdWriteFunc)fwrite, NULL, NULL, 0));
return 0;
}
diff --git a/test/test_model.c b/test/test_model.c
index 4cedfb2b..d5b5eb8c 100644
--- a/test/test_model.c
+++ b/test/test_model.c
@@ -1062,16 +1062,18 @@ test_write_flat_range(SerdWorld* world, const unsigned n_quads)
serd_cursor_free(all);
serd_writer_finish(writer);
- const char* str = serd_buffer_sink_finish(&buffer);
- const char* expected = "<urn:s>\n"
- " <urn:p> _:b1 ,\n"
- " _:b2 .\n"
- "\n"
- "_:b1\n"
- " <urn:p> <urn:o> .\n"
- "\n"
- "_:b2\n"
- " <urn:p> <urn:o> .\n";
+ serd_byte_sink_close(out);
+
+ const char* const str = (const char*)buffer.buf;
+ static const char* const expected = "<urn:s>\n"
+ "\t<urn:p> _:b1 ,\n"
+ "\t\t_:b2 .\n"
+ "\n"
+ "_:b1\n"
+ "\t<urn:p> <urn:o> .\n"
+ "\n"
+ "_:b2\n"
+ "\t<urn:p> <urn:o> .\n";
assert(!strcmp(str, expected));
@@ -1126,7 +1128,9 @@ test_write_bad_list(SerdWorld* world, const unsigned n_quads)
serd_cursor_free(all);
serd_writer_finish(writer);
- const char* str = serd_buffer_sink_finish(&buffer);
+ serd_byte_sink_close(out);
+
+ const char* str = (const char*)buffer.buf;
const char* expected = "<urn:s>\n"
" <urn:p> (\n"
" \"a\"\n"
@@ -1188,7 +1192,8 @@ test_write_infinite_list(SerdWorld* world, const unsigned n_quads)
serd_cursor_free(all);
serd_writer_finish(writer);
- const char* str = serd_buffer_sink_finish(&buffer);
+ serd_byte_sink_close(out);
+ const char* str = (const char*)buffer.buf;
const char* expected = "<urn:s>\n"
" <urn:p> _:l1 .\n"
"\n"
@@ -1265,7 +1270,8 @@ test_write_error_in_list_subject(SerdWorld* world, const unsigned n_quads)
for (size_t max_successes = 0; max_successes < 18; ++max_successes) {
FailingWriteFuncState state = {0, max_successes};
SerdByteSink* out =
- serd_byte_sink_new_function(failing_write_func, &state, 1);
+ serd_byte_sink_new_function(failing_write_func, NULL, &state, 1);
+
SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out);
const SerdSink* const sink = serd_writer_sink(writer);
@@ -1320,7 +1326,8 @@ test_write_error_in_list_object(SerdWorld* world, const unsigned n_quads)
for (size_t max_successes = 0; max_successes < 21; ++max_successes) {
FailingWriteFuncState state = {0, max_successes};
SerdByteSink* out =
- serd_byte_sink_new_function(failing_write_func, &state, 1);
+ serd_byte_sink_new_function(failing_write_func, NULL, &state, 1);
+
SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out);
const SerdSink* const sink = serd_writer_sink(writer);
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index cab33ff5..c0af313a 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -43,8 +43,8 @@ test_writer(const char* const path)
SerdWorld* world = serd_world_new();
SerdNodes* nodes = serd_world_nodes(world);
- SerdByteSink* byte_sink =
- serd_byte_sink_new_function((SerdWriteFunc)fwrite, fd, 1);
+ SerdByteSink* byte_sink = serd_byte_sink_new_function(
+ (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, fd, 1);
SerdWriter* writer =
serd_writer_new(world, SERD_TURTLE, SERD_WRITE_LAX, env, byte_sink);
@@ -131,7 +131,6 @@ test_writer(const char* const path)
serd_env_free(env);
serd_world_free(world);
- fclose(fd);
}
static void
diff --git a/test/test_writer.c b/test/test_writer.c
index 9a59d92f..89531eee 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -40,7 +40,9 @@ test_write_bad_event(void)
assert(serd_sink_write_event(serd_writer_sink(writer), &event) ==
SERD_ERR_BAD_ARG);
- char* const out = serd_buffer_sink_finish(&buffer);
+ assert(!serd_byte_sink_close(byte_sink));
+
+ char* const out = (char*)buffer.buf;
assert(!strcmp(out, ""));
serd_free(out);
@@ -109,10 +111,11 @@ null_sink(const void* const buf,
static void
test_writer_stack_overflow(void)
{
- SerdWorld* world = serd_world_new();
- SerdNodes* nodes = serd_world_nodes(world);
- SerdEnv* env = serd_env_new(SERD_EMPTY_STRING());
- SerdByteSink* byte_sink = serd_byte_sink_new_function(null_sink, NULL, 1u);
+ SerdWorld* world = serd_world_new();
+ SerdNodes* nodes = serd_world_nodes(world);
+ SerdEnv* env = serd_env_new(SERD_EMPTY_STRING());
+ SerdByteSink* byte_sink =
+ serd_byte_sink_new_function(null_sink, NULL, NULL, 1u);
SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink);
@@ -165,8 +168,8 @@ test_strict_write(void)
SerdEnv* env = serd_env_new(SERD_EMPTY_STRING());
- SerdByteSink* byte_sink =
- serd_byte_sink_new_function((SerdWriteFunc)fwrite, fd, 1);
+ SerdByteSink* byte_sink = serd_byte_sink_new_function(
+ (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, fd, 1);
SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, byte_sink);
assert(writer);
@@ -190,7 +193,6 @@ test_strict_write(void)
serd_writer_free(writer);
serd_byte_sink_free(byte_sink);
serd_env_free(env);
- fclose(fd);
serd_world_free(world);
}
@@ -230,7 +232,8 @@ test_write_error(void)
// Test with setting errno
- SerdByteSink* byte_sink = serd_byte_sink_new_function(faulty_sink, NULL, 1);
+ SerdByteSink* byte_sink =
+ serd_byte_sink_new_function(faulty_sink, NULL, NULL, 1);
SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink);
assert(writer);
@@ -242,7 +245,7 @@ test_write_error(void)
serd_byte_sink_free(byte_sink);
// Test without setting errno
- byte_sink = serd_byte_sink_new_function(faulty_sink, world, 1);
+ byte_sink = serd_byte_sink_new_function(faulty_sink, NULL, world, 1);
writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink);
assert(writer);
@@ -282,8 +285,9 @@ test_write_empty_syntax(void)
assert(writer);
assert(!serd_sink_write(serd_writer_sink(writer), 0u, s, p, o, NULL));
+ assert(!serd_byte_sink_close(byte_sink));
- char* out = serd_buffer_sink_finish(&buffer);
+ char* const out = (char*)buffer.buf;
assert(strlen(out) == 0);
serd_free(out);
@@ -323,7 +327,8 @@ test_write_bad_uri(void)
assert(st);
assert(st == SERD_ERR_BAD_ARG);
- serd_free(serd_buffer_sink_finish(&buffer));
+ serd_byte_sink_close(byte_sink);
+ serd_free(buffer.buf);
serd_writer_free(writer);
serd_byte_sink_free(byte_sink);
serd_env_free(env);
diff --git a/tools/console.c b/tools/console.c
index 339aca29..235d15d0 100644
--- a/tools/console.c
+++ b/tools/console.c
@@ -168,7 +168,8 @@ serd_open_output(const char* const filename, const size_t block_size)
{
if (!filename || !strcmp(filename, "-")) {
serd_set_stream_utf8_mode(stdout);
- return serd_byte_sink_new_function((SerdWriteFunc)fwrite, stdout, 1);
+ return serd_byte_sink_new_function(
+ (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, stdout, 1);
}
return serd_byte_sink_new_filename(filename, block_size);
diff --git a/tools/serdi.c b/tools/serdi.c
index db1cfde0..db4a016f 100644
--- a/tools/serdi.c
+++ b/tools/serdi.c
@@ -527,7 +527,7 @@ main(int argc, char** argv)
serd_node_free(base);
serd_world_free(world);
- if (serd_byte_sink_close(byte_sink) || (!out_filename && fclose(stdout))) {
+ if (serd_byte_sink_close(byte_sink)) {
perror("serdi: write error");
st = SERD_ERR_UNKNOWN;
}