diff options
author | David Robillard <d@drobilla.net> | 2021-08-08 20:00:43 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-01-28 21:57:07 -0500 |
commit | 9547c806dbf76d6afd1e324fc924abdf944c4bda (patch) | |
tree | 012ecf59cac892711d5bcf8f717f0ca579bbf7cd | |
parent | dc01b7e301e91d0d7bfc358f569f4f3849471c52 (diff) | |
download | serd-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.h | 12 | ||||
-rw-r--r-- | src/byte_sink.c | 57 | ||||
-rw-r--r-- | src/byte_sink.h | 18 | ||||
-rw-r--r-- | src/node_syntax.c | 8 | ||||
-rw-r--r-- | test/test_byte_sink.c | 2 | ||||
-rw-r--r-- | test/test_model.c | 35 | ||||
-rw-r--r-- | test/test_reader_writer.c | 5 | ||||
-rw-r--r-- | test/test_writer.c | 29 | ||||
-rw-r--r-- | tools/console.c | 3 | ||||
-rw-r--r-- | tools/serdi.c | 2 |
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; } |