diff options
author | David Robillard <d@drobilla.net> | 2018-07-08 18:46:38 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-01-13 23:04:01 -0500 |
commit | 480bcd2744779686916763e9a5a876afd43643b4 (patch) | |
tree | b6ee99ad336a3967e85b911eedad331b8155cbf2 | |
parent | cbb04334e5f422ab28d12f7a4c8a60eda1c94a89 (diff) | |
download | serd-480bcd2744779686916763e9a5a876afd43643b4.tar.gz serd-480bcd2744779686916763e9a5a876afd43643b4.tar.bz2 serd-480bcd2744779686916763e9a5a876afd43643b4.zip |
Expose SerdByteSink in public API
-rw-r--r-- | include/serd/serd.h | 38 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/byte_sink.c | 106 | ||||
-rw-r--r-- | src/writer.c | 9 | ||||
-rw-r--r-- | test/test_free_null.c | 1 |
5 files changed, 150 insertions, 5 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h index 9a414b11..66f22cbd 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -258,6 +258,9 @@ serd_strlen(const char* SERD_NONNULL str, SerdNodeFlags* SERD_NULLABLE flags); @{ */ +/// A sink for bytes that receives text output +typedef struct SerdByteSinkImpl SerdByteSink; + /** Function to detect I/O stream errors. @@ -302,6 +305,41 @@ typedef size_t (*SerdWriteFunc)(const void* SERD_NONNULL buf, void* SERD_NONNULL stream); /** + Create a new byte sink. + + @param write_func Function called with bytes to consume. + @param stream Context parameter passed to `sink`. + @param block_size Number of bytes to write per call. +*/ +SERD_API +SerdByteSink* SERD_ALLOCATED +serd_byte_sink_new(SerdWriteFunc SERD_NONNULL write_func, + void* SERD_NULLABLE stream, + size_t block_size); + +/** + Write to `sink`. + + Compatible with SerdWriteFunc. +*/ +SERD_API +size_t +serd_byte_sink_write(const void* SERD_NONNULL buf, + size_t size, + size_t nmemb, + SerdByteSink* SERD_NONNULL sink); + +/// Flush any pending output in `sink` to the underlying write function +SERD_API +void +serd_byte_sink_flush(SerdByteSink* SERD_NONNULL sink); + +/// Free `sink` +SERD_API +void +serd_byte_sink_free(SerdByteSink* SERD_NULLABLE sink); + +/** @} @defgroup serd_syntax Syntax Utilities @{ diff --git a/meson.build b/meson.build index 29236513..dcca40b1 100644 --- a/meson.build +++ b/meson.build @@ -84,6 +84,7 @@ c_header_files = files(c_headers) c_header = files('include/serd/serd.h') sources = [ + 'src/byte_sink.c', 'src/byte_source.c', 'src/caret.c', 'src/env.c', diff --git a/src/byte_sink.c b/src/byte_sink.c new file mode 100644 index 00000000..6436f9d8 --- /dev/null +++ b/src/byte_sink.c @@ -0,0 +1,106 @@ +/* + 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 "serd_internal.h" +#include "system.h" + +#include "serd/serd.h" + +#include <assert.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +struct SerdByteSinkImpl { + SerdWriteFunc sink; + void* stream; + char* buf; + size_t size; + size_t block_size; +}; + +SerdByteSink* +serd_byte_sink_new(SerdWriteFunc write_func, void* stream, size_t block_size) +{ + SerdByteSink* sink = (SerdByteSink*)calloc(1, sizeof(SerdByteSink)); + + sink->sink = write_func; + sink->stream = stream; + sink->block_size = block_size; + + if (block_size > 1) { + sink->buf = (char*)serd_allocate_buffer(block_size); + } + + return sink; +} + +size_t +serd_byte_sink_write(const void* buf, + size_t size, + size_t nmemb, + SerdByteSink* sink) +{ + assert(size == 1); + (void)size; + + if (nmemb == 0) { + return 0; + } + + if (sink->block_size == 1) { + return sink->sink(buf, 1, nmemb, sink->stream); + } + + const size_t orig_len = nmemb; + while (nmemb) { + const size_t space = sink->block_size - sink->size; + const size_t n = MIN(space, nmemb); + + // 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; + nmemb -= n; + + // Flush page if buffer is full + if (sink->size == sink->block_size) { + sink->sink(sink->buf, 1, sink->block_size, sink->stream); + sink->size = 0; + } + } + + return orig_len; +} + +void +serd_byte_sink_flush(SerdByteSink* sink) +{ + if (sink->block_size > 1 && sink->size > 0) { + sink->sink(sink->buf, 1, sink->size, sink->stream); + sink->size = 0; + } +} + +void +serd_byte_sink_free(SerdByteSink* const sink) +{ + if (sink) { + serd_byte_sink_flush(sink); + serd_free_aligned(sink->buf); + free(sink); + } +} diff --git a/src/writer.c b/src/writer.c index e50cdaf1..a6f73a89 100644 --- a/src/writer.c +++ b/src/writer.c @@ -14,7 +14,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "byte_sink.h" #include "env.h" #include "node.h" #include "serd_internal.h" @@ -93,7 +92,7 @@ struct SerdWriterImpl { SerdNode* root_node; SerdURIView root_uri; SerdStack anon_stack; - SerdByteSink byte_sink; + SerdByteSink* byte_sink; WriteContext context; SerdNode* list_subj; unsigned list_depth; @@ -156,7 +155,7 @@ ctx(SerdWriter* writer, const SerdField field) static size_t sink(const void* buf, size_t len, SerdWriter* writer) { - return serd_byte_sink_write(buf, len, &writer->byte_sink); + return serd_byte_sink_write(buf, 1, len, writer->byte_sink); } // Write a single character, as an escape for single byte characters @@ -944,7 +943,7 @@ serd_writer_finish(SerdWriter* writer) if (ctx(writer, SERD_GRAPH)) { write_sep(writer, SEP_GRAPH_END); } - serd_byte_sink_flush(&writer->byte_sink); + serd_byte_sink_flush(writer->byte_sink); free_context(&writer->context); writer->indent = 0; writer->context = WRITE_CONTEXT_NULL; @@ -1084,7 +1083,7 @@ serd_writer_free(SerdWriter* writer) serd_stack_free(&writer->anon_stack); free(writer->bprefix); - serd_byte_sink_free(&writer->byte_sink); + serd_byte_sink_free(writer->byte_sink); serd_node_free(writer->root_node); free(writer); } diff --git a/test/test_free_null.c b/test/test_free_null.c index 195daeeb..15e751a9 100644 --- a/test/test_free_null.c +++ b/test/test_free_null.c @@ -24,6 +24,7 @@ int main(void) { serd_free(NULL); + serd_byte_sink_free(NULL); serd_node_free(NULL); serd_world_free(NULL); serd_env_free(NULL); |