From 5c6b16d108c0af6a93aba6aa629b5aa83e0116c9 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 8 Jul 2018 18:46:38 +0200 Subject: Expose SerdByteSink in public API --- serd/serd.h | 42 ++++++++++++++++++++++++++++ src/byte_sink.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/byte_sink.h | 70 +++------------------------------------------- src/writer.c | 8 +++--- wscript | 1 + 5 files changed, 137 insertions(+), 70 deletions(-) create mode 100644 src/byte_sink.c diff --git a/serd/serd.h b/serd/serd.h index 93a2454d..5349afc1 100644 --- a/serd/serd.h +++ b/serd/serd.h @@ -105,6 +105,11 @@ typedef struct SerdWriterImpl SerdWriter; */ typedef struct SerdSinkImpl SerdSink; +/** + A sink for bytes that receives string output. +*/ +typedef struct SerdByteSinkImpl SerdByteSink; + /** Return status code. */ @@ -422,6 +427,43 @@ typedef size_t (*SerdWriteFunc)(const void* buf, size_t nmemb, void* 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_byte_sink_new(SerdWriteFunc write_func, void* stream, size_t block_size); + +/** + Write to `sink`. + + Compatible with SerdWriteFunc. +*/ +SERD_API +size_t +serd_byte_sink_write(const void* buf, + size_t size, + size_t nmemb, + SerdByteSink* sink); + +/** + Flush any pending output in `sink` to the underlying write function. +*/ +SERD_API +void +serd_byte_sink_flush(SerdByteSink* sink); + +/** + Free `sink`. +*/ +SERD_API +void +serd_byte_sink_free(SerdByteSink* sink); + /** @} @name Syntax Utilities diff --git a/src/byte_sink.c b/src/byte_sink.c new file mode 100644 index 00000000..416ba30f --- /dev/null +++ b/src/byte_sink.c @@ -0,0 +1,86 @@ +/* + Copyright 2011-2018 David Robillard + + 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_internal.h" +#include "system.h" + +#include +#include + +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; + sink->buf = + ((block_size > 1) ? (char*)serd_allocate_buffer(block_size) : NULL); + return sink; +} + +size_t +serd_byte_sink_write(const void* buf, + size_t size, + size_t nmemb, + SerdByteSink* sink) +{ + if (nmemb == 0) { + return 0; + } else 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* sink) +{ + serd_byte_sink_flush(sink); + free(sink->buf); + free(sink); +} diff --git a/src/byte_sink.h b/src/byte_sink.h index 4425f91f..14053bd6 100644 --- a/src/byte_sink.h +++ b/src/byte_sink.h @@ -17,81 +17,19 @@ #ifndef SERD_BYTE_SINK_H #define SERD_BYTE_SINK_H -#include "serd_internal.h" -#include "system.h" - #include "serd/serd.h" +#include "serd_internal.h" + #include -#include #include -typedef struct SerdByteSinkImpl { +struct SerdByteSinkImpl { SerdWriteFunc sink; void* stream; char* buf; size_t size; size_t block_size; -} SerdByteSink; - -static inline SerdByteSink -serd_byte_sink_new(SerdWriteFunc sink, void* stream, size_t block_size) -{ - SerdByteSink bsink; - bsink.sink = sink; - bsink.stream = stream; - bsink.size = 0; - bsink.block_size = block_size; - bsink.buf = ((block_size > 1) - ? (char*)serd_allocate_buffer(block_size) - : NULL); - return bsink; -} - -static inline void -serd_byte_sink_flush(SerdByteSink* bsink) -{ - if (bsink->block_size > 1 && bsink->size > 0) { - bsink->sink(bsink->buf, 1, bsink->size, bsink->stream); - bsink->size = 0; - } -} - -static inline void -serd_byte_sink_free(SerdByteSink* bsink) -{ - serd_byte_sink_flush(bsink); - free(bsink->buf); - bsink->buf = NULL; -} - -static inline size_t -serd_byte_sink_write(const void* buf, size_t len, SerdByteSink* bsink) -{ - if (len == 0) { - return 0; - } else if (bsink->block_size == 1) { - return bsink->sink(buf, 1, len, bsink->stream); - } - - const size_t orig_len = len; - while (len) { - const size_t space = bsink->block_size - bsink->size; - const size_t n = MIN(space, len); - - // Write as much as possible into the remaining buffer space - memcpy(bsink->buf + bsink->size, buf, n); - bsink->size += n; - buf = (const char*)buf + n; - len -= n; - - // Flush page if buffer is full - if (bsink->size == bsink->block_size) { - bsink->sink(bsink->buf, 1, bsink->block_size, bsink->stream); - bsink->size = 0; - } - } - return orig_len; -} +}; #endif // SERD_BYTE_SINK_H diff --git a/src/writer.c b/src/writer.c index 274e2b20..b1f68e59 100644 --- a/src/writer.c +++ b/src/writer.c @@ -94,7 +94,7 @@ struct SerdWriterImpl { SerdNode* root_node; SerdURI root_uri; SerdStack anon_stack; - SerdByteSink byte_sink; + SerdByteSink* byte_sink; SerdErrorSink error_sink; void* error_handle; WriteContext context; @@ -159,7 +159,7 @@ ctx(SerdWriter* writer, const SerdField field) static inline 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 @@ -853,7 +853,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); writer->indent = 0; writer->context = WRITE_CONTEXT_NULL; @@ -971,7 +971,7 @@ serd_writer_free(SerdWriter* writer) serd_writer_finish(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/wscript b/wscript index cb978265..14eea8e1 100644 --- a/wscript +++ b/wscript @@ -75,6 +75,7 @@ def configure(conf): 'Build unit tests': conf.is_defined('HAVE_GL')}) lib_source = ['src/base64.c', + 'src/byte_sink.c', 'src/byte_source.c', 'src/cursor.c', 'src/env.c', -- cgit v1.2.1