aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-08-13 20:31:57 -0400
committerDavid Robillard <d@drobilla.net>2022-01-28 21:57:07 -0500
commit0e739f34801ff6810064a8fac570f6be2b61ae70 (patch)
tree4451739f8e9b00d490d2d59aa6b1f370ae99c356
parent63e7e57237a79d0447b0450a7fd3148c43052299 (diff)
downloadserd-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-format1
-rw-r--r--doc/conf.py.in1
-rw-r--r--include/serd/serd.h101
-rw-r--r--meson.build3
-rw-r--r--src/block_dumper.c59
-rw-r--r--src/block_dumper.h88
-rw-r--r--src/byte_sink.c124
-rw-r--r--src/byte_sink.h66
-rw-r--r--src/node_syntax.c14
-rw-r--r--src/output_stream.c89
-rw-r--r--src/writer.c31
-rw-r--r--test/meson.build1
-rw-r--r--test/test_byte_sink.c32
-rw-r--r--test/test_free_null.c1
-rw-r--r--test/test_model.c62
-rw-r--r--test/test_reader_writer.c14
-rw-r--r--test/test_terse_write.c8
-rw-r--r--test/test_writer.c122
-rw-r--r--tools/console.c23
-rw-r--r--tools/console.h12
-rw-r--r--tools/serd-filter.c2
-rw-r--r--tools/serd-pipe.c2
-rw-r--r--tools/serd-sort.c2
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;