diff options
author | David Robillard <d@drobilla.net> | 2021-08-13 20:31:57 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-01-28 21:57:07 -0500 |
commit | 0e739f34801ff6810064a8fac570f6be2b61ae70 (patch) | |
tree | 4451739f8e9b00d490d2d59aa6b1f370ae99c356 | |
parent | 63e7e57237a79d0447b0450a7fd3148c43052299 (diff) | |
download | serd-0e739f34801ff6810064a8fac570f6be2b61ae70.tar.gz serd-0e739f34801ff6810064a8fac570f6be2b61ae70.tar.bz2 serd-0e739f34801ff6810064a8fac570f6be2b61ae70.zip |
Simplify output stream API
This makes the paging mechanism an internal detail once again. While it's
conceptually elegant to simply have a single write interface and have the block
dumper just be another implementation of that, unfortunately it is not
practical. The inlining of serd_block_dumper_write() is a significant
performance boost, because it avoids a non-inlinable function call of overhead
per character.
Compared to the SerdByteSink approach, this removes the burden and overhead of
needing to dynamically allocate the structure itself.
-rw-r--r-- | .clang-format | 1 | ||||
-rw-r--r-- | doc/conf.py.in | 1 | ||||
-rw-r--r-- | include/serd/serd.h | 101 | ||||
-rw-r--r-- | meson.build | 3 | ||||
-rw-r--r-- | src/block_dumper.c | 59 | ||||
-rw-r--r-- | src/block_dumper.h | 88 | ||||
-rw-r--r-- | src/byte_sink.c | 124 | ||||
-rw-r--r-- | src/byte_sink.h | 66 | ||||
-rw-r--r-- | src/node_syntax.c | 14 | ||||
-rw-r--r-- | src/output_stream.c | 89 | ||||
-rw-r--r-- | src/writer.c | 31 | ||||
-rw-r--r-- | test/meson.build | 1 | ||||
-rw-r--r-- | test/test_byte_sink.c | 32 | ||||
-rw-r--r-- | test/test_free_null.c | 1 | ||||
-rw-r--r-- | test/test_model.c | 62 | ||||
-rw-r--r-- | test/test_reader_writer.c | 14 | ||||
-rw-r--r-- | test/test_terse_write.c | 8 | ||||
-rw-r--r-- | test/test_writer.c | 122 | ||||
-rw-r--r-- | tools/console.c | 23 | ||||
-rw-r--r-- | tools/console.h | 12 | ||||
-rw-r--r-- | tools/serd-filter.c | 2 | ||||
-rw-r--r-- | tools/serd-pipe.c | 2 | ||||
-rw-r--r-- | tools/serd-sort.c | 2 |
23 files changed, 449 insertions, 409 deletions
diff --git a/.clang-format b/.clang-format index 90d9c7ba..08b326c8 100644 --- a/.clang-format +++ b/.clang-format @@ -19,7 +19,6 @@ IndentPPDirectives: AfterHash KeepEmptyLinesAtTheStartOfBlocks: false SpacesInContainerLiterals: false StatementMacros: - - SERD_ALLOCATED - SERD_API - SERD_CONST_API - SERD_CONST_FUNC diff --git a/doc/conf.py.in b/doc/conf.py.in index 059f096f..374b6596 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -22,7 +22,6 @@ except ModuleNotFoundError: # Ignore everything opaque or external for nitpicky mode _opaque = [ "FILE", - "SerdByteSinkImpl", "SerdByteSourceImpl", "SerdCaretImpl", "SerdCursorImpl", diff --git a/include/serd/serd.h b/include/serd/serd.h index c3b674b7..db7520d9 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -2427,78 +2427,74 @@ serd_buffer_close(void* SERD_NONNULL stream); /** @} - @defgroup serd_byte_sink Byte Sink + @defgroup serd_output_stream Output Streams + + An output stream is used for writing output as a raw stream of bytes. It is + compatible with standard C `FILE` streams, but allows different functions to + be provided for things like writing to a buffer or a socket. + @{ */ -/// A sink for bytes that receives text output -typedef struct SerdByteSinkImpl SerdByteSink; +/// An output stream that receives bytes +typedef struct { + void* SERD_NULLABLE stream; ///< Opaque parameter for functions + SerdWriteFunc SERD_NULLABLE write; ///< Write bytes to output + SerdStreamCloseFunc SERD_NULLABLE close; ///< Close output +} SerdOutputStream; /** - Create a new byte sink that writes to a buffer. + Open a stream that writes to a provided function. - 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 write_func Function to write output. + @param close_func Function to close the stream after writing is done. + @param stream Opaque stream parameter for write_func and close_func. - @param buffer Buffer to write output to. + @return An opened output stream, or all zeros on error. */ -SERD_API -SerdByteSink* SERD_ALLOCATED -serd_byte_sink_new_buffer(SerdBuffer* SERD_NONNULL buffer); +SERD_CONST_API +SerdOutputStream +serd_open_output_stream(SerdWriteFunc SERD_NONNULL write_func, + SerdStreamCloseFunc SERD_NULLABLE close_func, + void* SERD_NULLABLE stream); /** - Create a new byte sink that writes to a file. + Open a stream that writes to a buffer. - An arbitrary `FILE*` can be used via serd_byte_sink_new_function() as well, - this is just a convenience function that opens the file properly and sets - flags for optimized I/O if possible. + The `buffer` is owned by the caller, but will be expanded using `realloc` as + necessary. Note that the string in the buffer will not be null terminated + until the stream is closed. - @param path Path of file to open and write to. - @param block_size Number of bytes to write per call. + @param buffer Buffer to write output to. + @return An opened output stream, or all zeros on error. */ -SERD_API -SerdByteSink* SERD_ALLOCATED -serd_byte_sink_new_filename(const char* SERD_NONNULL path, size_t block_size); +SERD_CONST_API +SerdOutputStream +serd_open_output_buffer(SerdBuffer* SERD_NONNULL buffer); /** - Create a new byte sink that writes to a user-specified function. + Create a new byte sink that writes to a file. - The `stream` will be passed to the `write_func`, which is compatible with - the standard C `fwrite` if `stream` is a `FILE*`. + An arbitrary `FILE*` can be used with serd_open_output_stream() as well, + this convenience function opens the file properly for readingn with serd, + and sets flags for optimized I/O if possible. - @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. + @param path Path of file to open and write to. */ SERD_API -SerdByteSink* SERD_ALLOCATED -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 -void -serd_byte_sink_flush(SerdByteSink* SERD_NONNULL sink); +SerdOutputStream +serd_open_output_file(const char* SERD_NONNULL path); /** - Close `sink`, including the underlying file if necessary. + Close an output stream. - If `sink` was created with serd_byte_sink_new_filename(), then the file is - closed. If there was an error, then SERD_ERR_UNKNOWN is returned and - `errno` is set. + This will call the close function, and reset the stream internally so that + no further writes can be made. For convenience, this is safe to call on + NULL, and safe to call several times on the same output. */ SERD_API SerdStatus -serd_byte_sink_close(SerdByteSink* SERD_NONNULL sink); - -/// Free `sink`, flushing and closing first if necessary -SERD_API -void -serd_byte_sink_free(SerdByteSink* SERD_NULLABLE sink); +serd_close_output(SerdOutputStream* SERD_NULLABLE output); /** @} @@ -2579,11 +2575,12 @@ typedef uint32_t SerdWriterFlags; /// Create a new RDF writer SERD_API SerdWriter* SERD_ALLOCATED -serd_writer_new(SerdWorld* SERD_NONNULL world, - SerdSyntax syntax, - SerdWriterFlags flags, - const SerdEnv* SERD_NONNULL env, - SerdByteSink* SERD_NONNULL byte_sink); +serd_writer_new(SerdWorld* SERD_NONNULL world, + SerdSyntax syntax, + SerdWriterFlags flags, + const SerdEnv* SERD_NONNULL env, + SerdOutputStream* SERD_NONNULL output, + size_t block_size); /// Free `writer` SERD_API diff --git a/meson.build b/meson.build index c1713ce6..eebe55be 100644 --- a/meson.build +++ b/meson.build @@ -86,8 +86,8 @@ c_header_files = files(c_headers) c_header = files('include/serd/serd.h') sources = [ + 'src/block_dumper.c', 'src/buffer.c', - 'src/byte_sink.c', 'src/byte_source.c', 'src/canon.c', 'src/caret.c', @@ -103,6 +103,7 @@ sources = [ 'src/node.c', 'src/node_syntax.c', 'src/nodes.c', + 'src/output_stream.c', 'src/read_nquads.c', 'src/read_ntriples.c', 'src/read_utf8.c', diff --git a/src/block_dumper.c b/src/block_dumper.c new file mode 100644 index 00000000..6ffb0013 --- /dev/null +++ b/src/block_dumper.c @@ -0,0 +1,59 @@ +/* + Copyright 2011-2021 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "block_dumper.h" +#include "system.h" + +#include "serd/serd.h" + +#include <stddef.h> + +SerdStatus +serd_block_dumper_open(SerdBlockDumper* const dumper, + SerdOutputStream* const output, + const size_t block_size) +{ + if (!block_size) { + return SERD_ERR_BAD_ARG; + } + + dumper->out = output; + dumper->buf = NULL; + dumper->size = 0u; + dumper->block_size = block_size; + + if (block_size == 1) { + return SERD_SUCCESS; + } + + dumper->buf = (char*)serd_allocate_buffer(block_size); + return dumper->buf ? SERD_SUCCESS : SERD_ERR_INTERNAL; +} + +void +serd_block_dumper_flush(SerdBlockDumper* const dumper) +{ + if (dumper->out->stream && dumper->block_size > 1 && dumper->size > 0) { + dumper->out->write(dumper->buf, 1, dumper->size, dumper->out->stream); + dumper->size = 0; + } +} + +void +serd_block_dumper_close(SerdBlockDumper* const dumper) +{ + serd_free_aligned(dumper->buf); +} diff --git a/src/block_dumper.h b/src/block_dumper.h new file mode 100644 index 00000000..7c718566 --- /dev/null +++ b/src/block_dumper.h @@ -0,0 +1,88 @@ +/* + Copyright 2011-2021 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef SERD_BLOCK_DUMPER_H +#define SERD_BLOCK_DUMPER_H + +#include "serd/serd.h" + +#include <stddef.h> +#include <string.h> + +typedef struct { + SerdOutputStream* SERD_ALLOCATED out; ///< Output stream to write to + char* SERD_ALLOCATED buf; ///< Local buffer if needed + size_t size; ///< Bytes pending for this block + size_t block_size; ///< Block size to write in bytes +} SerdBlockDumper; + +/** + Set up a new output stream wrapper that writes in blocks. + + This allocates a buffer internally, which must be eventually freed by + calling serd_block_dumper_close(). +*/ +SerdStatus +serd_block_dumper_open(SerdBlockDumper* SERD_NONNULL dumper, + SerdOutputStream* SERD_NONNULL output, + size_t block_size); + +void +serd_block_dumper_flush(SerdBlockDumper* SERD_NONNULL dumper); + +void +serd_block_dumper_close(SerdBlockDumper* SERD_NONNULL dumper); + +/** + Write some bytes to the page writer. + + This works like any other SerdWriteFunc, but will append to an internal + buffer and only actually write to the output when a whole block is ready. +*/ +static inline size_t +serd_block_dumper_write(const void* SERD_NONNULL buf, + const size_t size, + const size_t nmemb, + SerdBlockDumper* SERD_NONNULL const dumper) +{ + if (dumper->block_size == 1) { + return dumper->out->write(buf, size, nmemb, dumper->out->stream); + } + + size_t len = size * nmemb; + const size_t orig_len = len; + while (len) { + const size_t space = dumper->block_size - dumper->size; + const size_t n = space < len ? space : len; + + // Write as much as possible into the remaining buffer space + memcpy(dumper->buf + dumper->size, buf, n); + dumper->size += n; + buf = (const char*)buf + n; + len -= n; + + // Flush page if buffer is full + if (dumper->size == dumper->block_size) { + dumper->out->write( + dumper->buf, 1, dumper->block_size, dumper->out->stream); + dumper->size = 0; + } + } + + return orig_len; +} + +#endif // SERD_DUMPER_H diff --git a/src/byte_sink.c b/src/byte_sink.c deleted file mode 100644 index b5b4039e..00000000 --- a/src/byte_sink.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright 2011-2020 David Robillard <d@drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include "byte_sink.h" - -#include "serd_config.h" -#include "system.h" - -#include "serd/serd.h" - -#include <assert.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> - -#if USE_POSIX_FADVISE && USE_FILENO -# include <fcntl.h> -#endif - -SerdByteSink* -serd_byte_sink_new_buffer(SerdBuffer* const buffer) -{ - assert(buffer); - - return serd_byte_sink_new_function( - serd_buffer_write, serd_buffer_close, buffer, 1u); -} - -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; - - if (block_size > 1) { - sink->buf = (char*)serd_allocate_buffer(block_size); - } - - return sink; -} - -SerdByteSink* -serd_byte_sink_new_filename(const char* const path, const size_t block_size) -{ - assert(path); - - if (!block_size) { - return NULL; - } - - FILE* const file = fopen(path, "wb"); - if (!file) { - return NULL; - } - -#if USE_POSIX_FADVISE && USE_FILENO - posix_fadvise(fileno(file), 0, 0, POSIX_FADV_SEQUENTIAL); -#endif - - return serd_byte_sink_new_function( - (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, file, block_size); -} - -void -serd_byte_sink_flush(SerdByteSink* sink) -{ - assert(sink); - - if (sink->stream && sink->block_size > 1 && sink->size > 0) { - sink->write_func(sink->buf, 1, sink->size, sink->stream); - sink->size = 0; - } -} - -SerdStatus -serd_byte_sink_close(SerdByteSink* sink) -{ - assert(sink); - - serd_byte_sink_flush(sink); - - 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; -} - -void -serd_byte_sink_free(SerdByteSink* const sink) -{ - if (sink) { - serd_byte_sink_close(sink); - serd_free_aligned(sink->buf); - free(sink); - } -} diff --git a/src/byte_sink.h b/src/byte_sink.h deleted file mode 100644 index d117a589..00000000 --- a/src/byte_sink.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2011-2020 David Robillard <d@drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#ifndef SERD_BYTE_SINK_H -#define SERD_BYTE_SINK_H - -#include "serd/serd.h" - -#include <stddef.h> -#include <string.h> - -struct SerdByteSinkImpl { - 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 -serd_byte_sink_write(const void* buf, size_t len, SerdByteSink* const sink) -{ - if (len == 0) { - return 0; - } - - if (sink->block_size == 1) { - return sink->write_func(buf, 1, len, sink->stream); - } - - const size_t orig_len = len; - while (len) { - const size_t space = sink->block_size - sink->size; - const size_t n = space < len ? space : len; - - // Write as much as possible into the remaining buffer space - memcpy(sink->buf + sink->size, buf, n); - sink->size += n; - buf = (const char*)buf + n; - len -= n; - - // Flush page if buffer is full - if (sink->size == sink->block_size) { - sink->write_func(sink->buf, 1, sink->block_size, sink->stream); - sink->size = 0; - } - } - - return orig_len; -} - -#endif // SERD_BYTE_SINK_H diff --git a/src/node_syntax.c b/src/node_syntax.c index 1dadc7b8..d2768c3e 100644 --- a/src/node_syntax.c +++ b/src/node_syntax.c @@ -98,22 +98,22 @@ serd_node_to_syntax_in(const SerdNode* const node, const SerdSyntax syntax, const SerdEnv* const env) { - SerdWorld* const world = serd_world_new(); - SerdBuffer buffer = {NULL, 0}; - SerdByteSink* const out = serd_byte_sink_new_buffer(&buffer); - SerdWriter* const writer = serd_writer_new(world, syntax, 0, env, out); + SerdWorld* const world = serd_world_new(); + SerdBuffer buffer = {NULL, 0}; + SerdOutputStream out = serd_open_output_buffer(&buffer); + SerdWriter* const writer = serd_writer_new(world, syntax, 0, env, &out, 1); char* result = NULL; if (!serd_writer_write_node(writer, node) && !serd_writer_finish(writer) && - !serd_byte_sink_close(out)) { + !serd_close_output(&out)) { result = (char*)buffer.buf; } else { - serd_byte_sink_close(out); + serd_close_output(&out); free(buffer.buf); } serd_writer_free(writer); - serd_byte_sink_free(out); + serd_close_output(&out); serd_world_free(world); return result; diff --git a/src/output_stream.c b/src/output_stream.c new file mode 100644 index 00000000..1254aa4f --- /dev/null +++ b/src/output_stream.c @@ -0,0 +1,89 @@ +/* + Copyright 2011-2021 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "serd_config.h" + +#include "serd/serd.h" + +// IWYU pragma: no_include <features.h> + +#include <assert.h> +#include <stddef.h> +#include <stdio.h> + +#if USE_POSIX_FADVISE && USE_FILENO +# include <fcntl.h> +#endif + +SerdOutputStream +serd_open_output_stream(SerdWriteFunc const write_func, + SerdStreamCloseFunc const close_func, + void* const stream) +{ + assert(write_func); + + SerdOutputStream output = {stream, write_func, close_func}; + return output; +} + +SerdOutputStream +serd_open_output_buffer(SerdBuffer* const buffer) +{ + assert(buffer); + + return serd_open_output_stream(serd_buffer_write, serd_buffer_close, buffer); +} + +SerdOutputStream +serd_open_output_file(const char* const path) +{ + assert(path); + +#ifdef __GLIBC__ + FILE* const file = fopen(path, "wbe"); +#else + FILE* const file = fopen(path, "wb"); +#endif + + if (!file) { + const SerdOutputStream failure = {NULL, NULL, NULL}; + return failure; + } + +#if USE_POSIX_FADVISE && USE_FILENO + posix_fadvise(fileno(file), 0, 0, POSIX_FADV_SEQUENTIAL); +#endif + + return serd_open_output_stream( + (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, file); +} + +SerdStatus +serd_close_output(SerdOutputStream* const output) +{ + int ret = 0; + + if (output) { + if (output->close && output->stream) { + ret = output->close(output->stream); + output->stream = NULL; + } + + output->stream = NULL; + } + + return ret ? SERD_ERR_UNKNOWN : SERD_SUCCESS; +} diff --git a/src/writer.c b/src/writer.c index 305c72e1..a9141f78 100644 --- a/src/writer.c +++ b/src/writer.c @@ -16,7 +16,7 @@ #include "writer.h" -#include "byte_sink.h" +#include "block_dumper.h" #include "env.h" #include "node.h" #include "sink.h" @@ -128,7 +128,7 @@ struct SerdWriterImpl { SerdURIView root_uri; WriteContext* anon_stack; size_t anon_stack_size; - SerdByteSink* byte_sink; + SerdBlockDumper output; WriteContext context; Sep last_sep; int indent; @@ -251,7 +251,8 @@ ctx(SerdWriter* writer, const SerdField field) SERD_WARN_UNUSED_RESULT static size_t sink(const void* buf, size_t len, SerdWriter* writer) { - const size_t written = serd_byte_sink_write(buf, len, writer->byte_sink); + const size_t written = serd_block_dumper_write(buf, 1, len, &writer->output); + if (written != len) { if (errno) { char message[1024] = {0}; @@ -1306,6 +1307,9 @@ serd_writer_finish(SerdWriter* writer) } free_context(writer); + + serd_block_dumper_flush(&writer->output); + writer->indent = 0; writer->context = WRITE_CONTEXT_NULL; writer->empty = true; @@ -1313,15 +1317,21 @@ serd_writer_finish(SerdWriter* writer) } SerdWriter* -serd_writer_new(SerdWorld* world, - SerdSyntax syntax, - SerdWriterFlags flags, - const SerdEnv* env, - SerdByteSink* byte_sink) +serd_writer_new(SerdWorld* world, + SerdSyntax syntax, + SerdWriterFlags flags, + const SerdEnv* env, + SerdOutputStream* output, + size_t block_size) { assert(world); assert(env); - assert(byte_sink); + assert(output); + + SerdBlockDumper dumper = {NULL, NULL, 0u, 0u}; + if (serd_block_dumper_open(&dumper, output, block_size)) { + return NULL; + } const WriteContext context = WRITE_CONTEXT_NULL; SerdWriter* writer = (SerdWriter*)calloc(1, sizeof(SerdWriter)); @@ -1332,7 +1342,7 @@ serd_writer_new(SerdWorld* world, writer->env = env; writer->root_node = NULL; writer->root_uri = SERD_URI_NULL; - writer->byte_sink = byte_sink; + writer->output = dumper; writer->context = context; writer->empty = true; @@ -1425,6 +1435,7 @@ serd_writer_free(SerdWriter* writer) } serd_writer_finish(writer); + serd_block_dumper_close(&writer->output); free(writer->anon_stack); serd_node_free(writer->root_node); free(writer); diff --git a/test/meson.build b/test/meson.build index c314e1d4..ab7666e3 100644 --- a/test/meson.build +++ b/test/meson.build @@ -8,7 +8,6 @@ run_sort_suite = find_program('run_sort_suite.py') wrapper = meson.get_cross_property('exe_wrapper', '') unit_tests = [ - 'byte_sink', 'byte_source', 'caret', 'cursor', diff --git a/test/test_byte_sink.c b/test/test_byte_sink.c deleted file mode 100644 index 71f1e62d..00000000 --- a/test/test_byte_sink.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright 2021 David Robillard <d@drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#undef NDEBUG - -#include "serd/serd.h" - -#include <assert.h> -#include <stdio.h> - -int -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, NULL, 0)); - - return 0; -} diff --git a/test/test_free_null.c b/test/test_free_null.c index 1fa87979..51a3c2dc 100644 --- a/test/test_free_null.c +++ b/test/test_free_null.c @@ -25,7 +25,6 @@ main(void) { serd_free(NULL); serd_byte_source_free(NULL); - serd_byte_sink_free(NULL); serd_node_free(NULL); serd_world_free(NULL); serd_env_free(NULL); diff --git a/test/test_model.c b/test/test_model.c index acefe05f..78372997 100644 --- a/test/test_model.c +++ b/test/test_model.c @@ -1052,10 +1052,12 @@ test_write_flat_range(SerdWorld* world, const unsigned n_quads) serd_model_add(model, s, p, b2, NULL); serd_model_add(model, b2, p, o, NULL); - SerdBuffer buffer = {NULL, 0}; - SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); - SerdByteSink* out = serd_byte_sink_new_buffer(&buffer); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out); + SerdBuffer buffer = {NULL, 0}; + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdOutputStream out = serd_open_output_buffer(&buffer); + + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, &out, 1); + assert(writer); SerdCursor* all = serd_model_begin(model); for (const SerdStatement* t = NULL; (t = serd_cursor_get(all)); @@ -1065,7 +1067,7 @@ test_write_flat_range(SerdWorld* world, const unsigned n_quads) serd_cursor_free(all); serd_writer_finish(writer); - serd_byte_sink_close(out); + serd_close_output(&out); const char* const str = (const char*)buffer.buf; static const char* const expected = "<urn:s>\n" @@ -1078,11 +1080,11 @@ test_write_flat_range(SerdWorld* world, const unsigned n_quads) "_:b2\n" "\t<urn:p> <urn:o> .\n"; + assert(str); assert(!strcmp(str, expected)); - free(buffer.buf); + serd_free(buffer.buf); serd_writer_free(writer); - serd_byte_sink_free(out); serd_model_free(model); serd_env_free(env); serd_nodes_free(nodes); @@ -1121,17 +1123,19 @@ test_write_bad_list(SerdWorld* world, const unsigned n_quads) serd_model_add(model, list2, prest, norest, NULL); serd_model_add(model, norest, pfirst, val2, NULL); - SerdBuffer buffer = {NULL, 0}; - SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); - SerdByteSink* out = serd_byte_sink_new_buffer(&buffer); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out); + SerdBuffer buffer = {NULL, 0}; + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdOutputStream out = serd_open_output_buffer(&buffer); + + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, &out, 1); + assert(writer); SerdCursor* all = serd_model_begin(model); serd_describe_range(all, serd_writer_sink(writer), 0); serd_cursor_free(all); serd_writer_finish(writer); - serd_byte_sink_close(out); + serd_close_output(&out); const char* str = (const char*)buffer.buf; const char* expected = "<urn:s>\n" @@ -1142,11 +1146,12 @@ test_write_bad_list(SerdWorld* world, const unsigned n_quads) " \"b\"\n" " ) .\n"; + assert(str); assert(!strcmp(str, expected)); free(buffer.buf); serd_writer_free(writer); - serd_byte_sink_free(out); + serd_close_output(&out); serd_model_free(model); serd_env_free(env); serd_nodes_free(nodes); @@ -1180,10 +1185,12 @@ test_write_infinite_list(SerdWorld* world, const unsigned n_quads) serd_model_add(model, list2, pfirst, val2, NULL); serd_model_add(model, list2, prest, list1, NULL); - SerdBuffer buffer = {NULL, 0}; - SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); - SerdByteSink* out = serd_byte_sink_new_buffer(&buffer); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out); + SerdBuffer buffer = {NULL, 0}; + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdOutputStream out = serd_open_output_buffer(&buffer); + + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, &out, 1); + assert(writer); serd_env_set_prefix( env, @@ -1195,7 +1202,7 @@ test_write_infinite_list(SerdWorld* world, const unsigned n_quads) serd_cursor_free(all); serd_writer_finish(writer); - serd_byte_sink_close(out); + serd_close_output(&out); const char* str = (const char*)buffer.buf; const char* expected = "<urn:s>\n" " <urn:p> _:l1 .\n" @@ -1207,11 +1214,12 @@ test_write_infinite_list(SerdWorld* world, const unsigned n_quads) " rdf:rest _:l1\n" " ] .\n"; + assert(str); assert(!strcmp(str, expected)); free(buffer.buf); serd_writer_free(writer); - serd_byte_sink_free(out); + serd_close_output(&out); serd_model_free(model); serd_env_free(env); serd_nodes_free(nodes); @@ -1272,10 +1280,10 @@ 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, NULL, &state, 1); + SerdOutputStream out = + serd_open_output_stream(failing_write_func, NULL, &state); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, &out, 1); const SerdSink* const sink = serd_writer_sink(writer); SerdCursor* const all = serd_model_begin(model); @@ -1285,7 +1293,7 @@ test_write_error_in_list_subject(SerdWorld* world, const unsigned n_quads) assert(st == SERD_ERR_BAD_WRITE); serd_writer_free(writer); - serd_byte_sink_free(out); + serd_close_output(&out); } serd_env_free(env); @@ -1328,10 +1336,10 @@ 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, NULL, &state, 1); + SerdOutputStream out = + serd_open_output_stream(failing_write_func, NULL, &state); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, &out, 1); const SerdSink* const sink = serd_writer_sink(writer); SerdCursor* const all = serd_model_begin(model); @@ -1341,7 +1349,7 @@ test_write_error_in_list_object(SerdWorld* world, const unsigned n_quads) assert(st == SERD_ERR_BAD_WRITE); serd_writer_free(writer); - serd_byte_sink_free(out); + serd_close_output(&out); } serd_env_free(env); diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c index b8afd649..bd83b082 100644 --- a/test/test_reader_writer.c +++ b/test/test_reader_writer.c @@ -43,11 +43,10 @@ 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, (SerdStreamCloseFunc)fclose, fd, 1); + SerdOutputStream output = serd_open_output_file(path); SerdWriter* writer = - serd_writer_new(world, SERD_TURTLE, SERD_WRITE_LAX, env, byte_sink); + serd_writer_new(world, SERD_TURTLE, SERD_WRITE_LAX, env, &output, 1); assert(writer); @@ -106,13 +105,13 @@ test_writer(const char* const path) assert(!serd_sink_write(iface, 0, s, p, hello, 0)); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); // Test buffer sink SerdBuffer buffer = {NULL, 0}; - byte_sink = serd_byte_sink_new_buffer(&buffer); - writer = serd_writer_new(world, SERD_TURTLE, 0, env, byte_sink); + output = serd_open_output_buffer(&buffer); + writer = serd_writer_new(world, SERD_TURTLE, 0, env, &output, 1); const SerdNode* const base = serd_nodes_uri(nodes, SERD_STRING("http://example.org/base")); @@ -120,9 +119,10 @@ test_writer(const char* const path) serd_writer_set_base_uri(writer, base); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); char* out = (char*)buffer.buf; + assert(out); assert(!strcmp(out, "@base <http://example.org/base> .\n")); serd_free(out); diff --git a/test/test_terse_write.c b/test/test_terse_write.c index aeb264ea..d9ce4d1b 100644 --- a/test/test_terse_write.c +++ b/test/test_terse_write.c @@ -64,9 +64,9 @@ test(void) serd_env_set_prefix(env, SERD_STRING("rdf"), SERD_STRING(NS_RDF)); - SerdByteSink* const byte_sink = serd_byte_sink_new_buffer(&buffer); - SerdWriter* const writer = - serd_writer_new(world, SERD_TURTLE, 0, env, byte_sink); + SerdOutputStream output = serd_open_output_buffer(&buffer); + SerdWriter* const writer = + serd_writer_new(world, SERD_TURTLE, 0, env, &output, 1); const SerdSink* const sink = serd_writer_sink(writer); @@ -99,7 +99,7 @@ test(void) check_output(writer, &buffer, "[]\n\trdf:value ( \"s1\" \"s2\" ) .\n"); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); serd_nodes_free(nodes); serd_env_free(env); serd_world_free(world); diff --git a/test/test_writer.c b/test/test_writer.c index 83d4087c..34eafbb2 100644 --- a/test/test_writer.c +++ b/test/test_writer.c @@ -26,29 +26,43 @@ #include <string.h> static void +test_writer_new(void) +{ + SerdWorld* world = serd_world_new(); + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdBuffer buffer = {NULL, 0}; + SerdOutputStream output = serd_open_output_buffer(&buffer); + + assert(!serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 0)); + + serd_world_free(world); + serd_env_free(env); +} + +static void test_write_bad_event(void) { - SerdWorld* world = serd_world_new(); - SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); - SerdBuffer buffer = {NULL, 0}; - SerdByteSink* byte_sink = serd_byte_sink_new_buffer(&buffer); + SerdWorld* world = serd_world_new(); + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdBuffer buffer = {NULL, 0}; + SerdOutputStream output = serd_open_output_buffer(&buffer); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1); assert(writer); const SerdEvent event = {(SerdEventType)42}; assert(serd_sink_write_event(serd_writer_sink(writer), &event) == SERD_ERR_BAD_ARG); - assert(!serd_byte_sink_close(byte_sink)); + assert(!serd_close_output(&output)); char* const out = (char*)buffer.buf; + assert(out); assert(!strcmp(out, "")); serd_free(out); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); serd_env_free(env); serd_world_free(world); } @@ -56,13 +70,13 @@ test_write_bad_event(void) static void test_write_long_literal(void) { - SerdWorld* world = serd_world_new(); - SerdNodes* nodes = serd_world_nodes(world); - SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); - SerdBuffer buffer = {NULL, 0}; - SerdByteSink* byte_sink = serd_byte_sink_new_buffer(&buffer); + SerdWorld* world = serd_world_new(); + SerdNodes* nodes = serd_world_nodes(world); + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdBuffer buffer = {NULL, 0}; + SerdOutputStream output = serd_open_output_buffer(&buffer); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1); assert(writer); const SerdNode* s = @@ -81,7 +95,7 @@ test_write_long_literal(void) assert(!serd_sink_write(serd_writer_sink(writer), 0, s, p, o, NULL)); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); serd_env_free(env); serd_buffer_close(&buffer); @@ -112,13 +126,12 @@ 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, NULL, 1u); + SerdWorld* world = serd_world_new(); + SerdNodes* nodes = serd_world_nodes(world); + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdOutputStream output = serd_open_output_stream(null_sink, NULL, NULL); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1); const SerdSink* sink = serd_writer_sink(writer); @@ -153,7 +166,7 @@ test_writer_stack_overflow(void) assert(st == SERD_ERR_OVERFLOW); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); serd_env_free(env); serd_world_free(world); } @@ -161,18 +174,16 @@ test_writer_stack_overflow(void) static void test_strict_write(void) { - SerdWorld* world = serd_world_new(); - SerdNodes* nodes = serd_world_nodes(world); - const char* path = "serd_strict_write_test.ttl"; - FILE* fd = fopen(path, "wb"); - assert(fd); + static const char* path = "serd_strict_write_test.ttl"; - SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + 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( - (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, fd, 1); + SerdOutputStream output = serd_open_output_file(path); + assert(output.stream); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, byte_sink); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, &output, 1); assert(writer); const SerdSink* sink = serd_writer_sink(writer); @@ -192,7 +203,7 @@ test_strict_write(void) assert(serd_sink_write(sink, 0, s, p, bad_uri, 0) == SERD_ERR_BAD_TEXT); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); serd_env_free(env); serd_world_free(world); } @@ -233,21 +244,20 @@ test_write_error(void) // Test with setting errno - SerdByteSink* byte_sink = - serd_byte_sink_new_function(faulty_sink, NULL, NULL, 1); + SerdOutputStream output = serd_open_output_stream(faulty_sink, NULL, NULL); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1); assert(writer); SerdStatus st = serd_sink_write(serd_writer_sink(writer), 0u, s, p, o, NULL); assert(st == SERD_ERR_BAD_WRITE); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); // Test without setting errno - byte_sink = serd_byte_sink_new_function(faulty_sink, NULL, world, 1); - writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink); + output = serd_open_output_stream(faulty_sink, NULL, world); + writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1); assert(writer); @@ -255,7 +265,7 @@ test_write_error(void) SERD_ERR_BAD_WRITE); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); serd_env_free(env); serd_world_free(world); @@ -277,24 +287,25 @@ test_write_empty_syntax(void) const SerdNode* o = serd_nodes_uri(nodes, SERD_STRING("http://example.org/o")); - SerdBuffer buffer = {NULL, 0}; - SerdByteSink* byte_sink = serd_byte_sink_new_buffer(&buffer); + SerdBuffer buffer = {NULL, 0}; + SerdOutputStream output = serd_open_output_buffer(&buffer); SerdWriter* writer = - serd_writer_new(world, SERD_SYNTAX_EMPTY, 0u, env, byte_sink); + serd_writer_new(world, SERD_SYNTAX_EMPTY, 0u, env, &output, 1); assert(writer); assert(!serd_sink_write(serd_writer_sink(writer), 0u, s, p, o, NULL)); - assert(!serd_byte_sink_close(byte_sink)); + assert(!serd_close_output(&output)); char* const out = (char*)buffer.buf; + assert(out); assert(strlen(out) == 0); serd_free(out); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); serd_env_free(env); serd_world_free(world); } @@ -314,11 +325,11 @@ test_write_bad_uri(void) const SerdNode* rel = serd_nodes_uri(nodes, SERD_STRING("rel")); - SerdBuffer buffer = {NULL, 0}; - SerdByteSink* byte_sink = serd_byte_sink_new_buffer(&buffer); + SerdBuffer buffer = {NULL, 0}; + SerdOutputStream output = serd_open_output_buffer(&buffer); SerdWriter* writer = - serd_writer_new(world, SERD_NTRIPLES, 0u, env, byte_sink); + serd_writer_new(world, SERD_NTRIPLES, 0u, env, &output, 1); assert(writer); @@ -328,10 +339,10 @@ test_write_bad_uri(void) assert(st); assert(st == SERD_ERR_BAD_ARG); - serd_byte_sink_close(byte_sink); + serd_close_output(&output); serd_free(buffer.buf); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); serd_env_free(env); serd_world_free(world); } @@ -339,13 +350,13 @@ test_write_bad_uri(void) static void check_pname_escape(const char* const lname, const char* const expected) { - SerdWorld* world = serd_world_new(); - SerdNodes* nodes = serd_world_nodes(world); - SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); - SerdBuffer buffer = {NULL, 0}; - SerdByteSink* byte_sink = serd_byte_sink_new_buffer(&buffer); + SerdWorld* world = serd_world_new(); + SerdNodes* nodes = serd_world_nodes(world); + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdBuffer buffer = {NULL, 0}; + SerdOutputStream output = serd_open_output_buffer(&buffer); - SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, byte_sink); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1); assert(writer); static const char* const prefix = "http://example.org/"; @@ -368,7 +379,7 @@ check_pname_escape(const char* const lname, const char* const expected) assert(!serd_sink_write(serd_writer_sink(writer), 0, s, p, node, NULL)); serd_writer_free(writer); - serd_byte_sink_free(byte_sink); + serd_close_output(&output); serd_env_free(env); serd_buffer_close(&buffer); @@ -415,6 +426,7 @@ test_write_pname_escapes(void) int main(void) { + test_writer_new(); test_write_bad_event(); test_write_long_literal(); test_writer_stack_overflow(); diff --git a/tools/console.c b/tools/console.c index d7f67c35..5cfeb56c 100644 --- a/tools/console.c +++ b/tools/console.c @@ -39,7 +39,8 @@ serd_tool_setup(SerdTool* const tool, { // Open the output first, since if that fails we have nothing to do const char* const out_path = options.out_filename; - if (!(tool->out = serd_open_output(out_path, options.block_size))) { + + if (!((tool->out = serd_open_output(out_path)).stream)) { fprintf(stderr, "%s: failed to open output file (%s)\n", program, @@ -57,7 +58,8 @@ serd_tool_setup(SerdTool* const tool, tool->world, options.output, options.out_filename, SERD_NQUADS), options.output.flags, tool->env, - tool->out))) { + &tool->out, + options.block_size))) { fprintf(stderr, "%s: failed to set up writing environment\n", program); return SERD_ERR_INTERNAL; } @@ -66,12 +68,12 @@ serd_tool_setup(SerdTool* const tool, } SerdStatus -serd_tool_cleanup(const SerdTool tool) +serd_tool_cleanup(SerdTool tool) { SerdStatus st = SERD_SUCCESS; - if (tool.out) { + if (tool.out.stream) { // Close the output stream explicitly to check if there were any errors - if (serd_byte_sink_close(tool.out)) { + if (serd_close_output(&tool.out)) { perror("write error"); st = SERD_ERR_BAD_WRITE; } @@ -80,7 +82,6 @@ serd_tool_cleanup(const SerdTool tool) serd_writer_free(tool.writer); serd_env_free(tool.env); serd_world_free(tool.world); - serd_byte_sink_free(tool.out); return st; } @@ -371,16 +372,16 @@ serd_open_input(const char* const filename, const size_t block_size) return byte_source; } -SerdByteSink* -serd_open_output(const char* const filename, const size_t block_size) +SerdOutputStream +serd_open_output(const char* const filename) { if (!filename || !strcmp(filename, "-")) { serd_set_stream_utf8_mode(stdout); - return serd_byte_sink_new_function( - (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, stdout, 1); + return serd_open_output_stream( + (SerdWriteFunc)fwrite, (SerdStreamCloseFunc)fclose, stdout); } - return serd_byte_sink_new_filename(filename, block_size); + return serd_open_output_file(filename); } SerdStatus diff --git a/tools/console.h b/tools/console.h index cb227e8e..97251f68 100644 --- a/tools/console.h +++ b/tools/console.h @@ -47,10 +47,10 @@ typedef struct { // Common "global" state of a command-line tool that writes data typedef struct { - SerdByteSink* out; - SerdWorld* world; - SerdEnv* env; - SerdWriter* writer; + SerdOutputStream out; + SerdWorld* world; + SerdEnv* env; + SerdWriter* writer; } SerdTool; static inline bool @@ -122,8 +122,8 @@ serd_choose_syntax(SerdWorld* world, SerdByteSource* serd_open_input(const char* filename, size_t block_size); -SerdByteSink* -serd_open_output(const char* filename, size_t block_size); +SerdOutputStream +serd_open_output(const char* filename); SerdStatus serd_set_base_uri_from_path(SerdEnv* env, const char* path); diff --git a/tools/serd-filter.c b/tools/serd-filter.c index 6cf8e0e7..a6f9b484 100644 --- a/tools/serd-filter.c +++ b/tools/serd-filter.c @@ -123,7 +123,7 @@ log_error(SerdWorld* const world, const char* const fmt, ...) static SerdStatus run(Options opts) { - SerdTool app = {NULL, NULL, NULL, NULL}; + SerdTool app = {{NULL, NULL, NULL}, NULL, NULL, NULL}; // Set up the writing environment SerdStatus st = SERD_SUCCESS; diff --git a/tools/serd-pipe.c b/tools/serd-pipe.c index 57392c8a..08479a74 100644 --- a/tools/serd-pipe.c +++ b/tools/serd-pipe.c @@ -39,7 +39,7 @@ typedef struct { static SerdStatus run(const Options opts) { - SerdTool app = {NULL, NULL, NULL, NULL}; + SerdTool app = {{NULL, NULL, NULL}, NULL, NULL, NULL}; // Set up the writing environment SerdStatus st = SERD_SUCCESS; diff --git a/tools/serd-sort.c b/tools/serd-sort.c index bbdfa164..1018350d 100644 --- a/tools/serd-sort.c +++ b/tools/serd-sort.c @@ -57,7 +57,7 @@ input_has_graphs(const Options opts) static SerdStatus run(const Options opts) { - SerdTool app = {NULL, NULL, NULL, NULL}; + SerdTool app = {{NULL, NULL, NULL}, NULL, NULL, NULL}; // Set up the writing environment SerdStatus st = SERD_SUCCESS; |