diff options
author | David Robillard <d@drobilla.net> | 2021-08-13 19:31:26 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-01-28 21:57:07 -0500 |
commit | 63e7e57237a79d0447b0450a7fd3148c43052299 (patch) | |
tree | b4c45eca1c208f8bd70a7a50632d423aec39e118 | |
parent | 547ef6e0600b703dcd42a10622563d7b91434669 (diff) | |
download | serd-63e7e57237a79d0447b0450a7fd3148c43052299.tar.gz serd-63e7e57237a79d0447b0450a7fd3148c43052299.tar.bz2 serd-63e7e57237a79d0447b0450a7fd3148c43052299.zip |
Provide a full output stream implementation for SerdBuffer
Essentially replaces serd_buffer_sink_finish() with serd_buffer_close(), which
makes writing to a buffer consistent with writing to a file or anything else.
-rw-r--r-- | include/serd/serd.h | 77 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/buffer.c | 52 | ||||
-rw-r--r-- | src/byte_sink.c | 10 | ||||
-rw-r--r-- | src/uri.c | 9 | ||||
-rw-r--r-- | src/writer.c | 28 | ||||
-rw-r--r-- | test/test_reader_writer.c | 2 | ||||
-rw-r--r-- | test/test_terse_write.c | 3 | ||||
-rw-r--r-- | test/test_writer.c | 10 | ||||
-rw-r--r-- | tools/serd-pipe.c | 2 | ||||
-rw-r--r-- | tools/serd-sort.c | 5 |
11 files changed, 120 insertions, 79 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h index daf903a2..c3b674b7 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -199,12 +199,6 @@ typedef struct { @} */ -/// A mutable buffer in memory -typedef struct { - void* SERD_NULLABLE buf; ///< Buffer - size_t len; ///< Size of buffer in bytes -} SerdBuffer; - /** Free memory allocated by Serd. @@ -2387,6 +2381,52 @@ serd_reader_free(SerdReader* SERD_NULLABLE reader); /** @} + @defgroup serd_buffer Buffer + + The #SerdBuffer type represents a writable area of memory with a known size. + An implementation of #SerdWriteFunc, #SerdStreamErrorFunc, and + #SerdStreamCloseFunc are provided which allow output to be written to a + buffer in memory instead of to a file as with `fwrite`, `ferror`, and + `fclose`. + + @{ +*/ + +/// A mutable buffer in memory +typedef struct { + void* SERD_NULLABLE buf; ///< Buffer + size_t len; ///< Size of buffer in bytes +} SerdBuffer; + +/** + A function for writing to a buffer, resizing it if necessary. + + This function can be used as a #SerdWriteFunc to write to a #SerdBuffer + which is resized as necessary with realloc(). The `stream` parameter must + point to an initialized #SerdBuffer. + + Note that when writing a string, the string in the buffer will not be + null-terminated until serd_buffer_close() is called. +*/ +SERD_API +size_t +serd_buffer_write(const void* SERD_NONNULL buf, + size_t size, + size_t nmemb, + void* SERD_NONNULL stream); + +/** + Close the buffer for writing. + + This writes a terminating null byte, so the contents of the buffer are safe + to read as a string after this call. +*/ +SERD_API +int +serd_buffer_close(void* SERD_NONNULL stream); + +/** + @} @defgroup serd_byte_sink Byte Sink @{ */ @@ -2556,31 +2596,6 @@ const SerdSink* SERD_NONNULL serd_writer_sink(SerdWriter* SERD_NONNULL writer); /** - A convenience sink function for writing to a string. - - This function can be used as a SerdSink to write to a SerdBuffer which is - resized as necessary with realloc(). The `stream` parameter must point to - an initialized SerdBuffer. When the write is finished, the string should be - retrieved with serd_buffer_sink_finish(). -*/ -SERD_API -size_t -serd_buffer_sink(const void* SERD_NONNULL buf, - size_t size, - size_t nmemb, - void* SERD_NONNULL stream); - -/** - Finish writing to a buffer with serd_buffer_sink(). - - The returned string is the result of the serialisation, which is null - terminated (by this function) and owned by the caller. -*/ -SERD_API -char* SERD_NONNULL -serd_buffer_sink_finish(SerdBuffer* SERD_NONNULL stream); - -/** Set the current output base URI, and emit a directive if applicable. Note this function can be safely casted to SerdBaseSink. diff --git a/meson.build b/meson.build index f75ce560..c1713ce6 100644 --- a/meson.build +++ b/meson.build @@ -86,6 +86,7 @@ c_header_files = files(c_headers) c_header = files('include/serd/serd.h') sources = [ + 'src/buffer.c', 'src/byte_sink.c', 'src/byte_source.c', 'src/canon.c', diff --git a/src/buffer.c b/src/buffer.c new file mode 100644 index 00000000..85ec09de --- /dev/null +++ b/src/buffer.c @@ -0,0 +1,52 @@ +/* + 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/serd.h" + +#include <assert.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +size_t +serd_buffer_write(const void* const buf, + const size_t size, + const size_t nmemb, + void* const stream) +{ + assert(buf); + assert(stream); + + SerdBuffer* const buffer = (SerdBuffer*)stream; + const size_t n_bytes = size * nmemb; + + char* const new_buf = (char*)realloc(buffer->buf, buffer->len + n_bytes); + if (new_buf) { + memcpy(new_buf + buffer->len, buf, n_bytes); + buffer->buf = new_buf; + buffer->len += nmemb; + } + + return new_buf ? nmemb : 0; +} + +int +serd_buffer_close(void* const stream) +{ + serd_buffer_write("", 1, 1, stream); // Write null terminator + + return 0; +} diff --git a/src/byte_sink.c b/src/byte_sink.c index febd11cd..b5b4039e 100644 --- a/src/byte_sink.c +++ b/src/byte_sink.c @@ -30,21 +30,13 @@ # include <fcntl.h> #endif -static int -close_buffer(void* const stream) -{ - serd_buffer_sink("", 1, 1, stream); // Write null terminator - - return 0; -} - SerdByteSink* serd_byte_sink_new_buffer(SerdBuffer* const buffer) { assert(buffer); return serd_byte_sink_new_function( - serd_buffer_sink, close_buffer, buffer, 1u); + serd_buffer_write, serd_buffer_close, buffer, 1u); } SerdByteSink* @@ -60,22 +60,23 @@ serd_parse_file_uri(const char* const uri, char** const hostname) for (const char* s = path; *s; ++s) { if (*s == '%') { if (*(s + 1) == '%') { - serd_buffer_sink("%", 1, 1, &buffer); + serd_buffer_write("%", 1, 1, &buffer); ++s; } else if (is_hexdig(*(s + 1)) && is_hexdig(*(s + 2))) { const char code[3] = {*(s + 1), *(s + 2), 0}; const char c = (char)strtoul(code, NULL, 16); - serd_buffer_sink(&c, 1, 1, &buffer); + serd_buffer_write(&c, 1, 1, &buffer); s += 2; } else { s += 2; // Junk escape, ignore } } else { - serd_buffer_sink(s, 1, 1, &buffer); + serd_buffer_write(s, 1, 1, &buffer); } } - return serd_buffer_sink_finish(&buffer); + serd_buffer_close(&buffer); + return (char*)buffer.buf; } /// RFC3986: scheme ::= ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) diff --git a/src/writer.c b/src/writer.c index b7814158..305c72e1 100644 --- a/src/writer.c +++ b/src/writer.c @@ -1436,31 +1436,3 @@ serd_writer_sink(SerdWriter* writer) assert(writer); return &writer->iface; } - -size_t -serd_buffer_sink(const void* const buf, - const size_t size, - const size_t nmemb, - void* const stream) -{ - assert(buf); - assert(size == 1); - assert(stream); - - (void)size; - - SerdBuffer* buffer = (SerdBuffer*)stream; - buffer->buf = (char*)realloc(buffer->buf, buffer->len + nmemb); - memcpy((uint8_t*)buffer->buf + buffer->len, buf, nmemb); - buffer->len += nmemb; - return nmemb; -} - -char* -serd_buffer_sink_finish(SerdBuffer* const stream) -{ - assert(stream); - - serd_buffer_sink("", 1, 1, stream); - return (char*)stream->buf; -} diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c index 1bad22cd..b8afd649 100644 --- a/test/test_reader_writer.c +++ b/test/test_reader_writer.c @@ -121,7 +121,7 @@ test_writer(const char* const path) serd_writer_free(writer); serd_byte_sink_free(byte_sink); - char* out = serd_buffer_sink_finish(&buffer); + char* out = (char*)buffer.buf; 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 59227332..aeb264ea 100644 --- a/test/test_terse_write.c +++ b/test/test_terse_write.c @@ -29,7 +29,7 @@ static void check_output(SerdWriter* writer, SerdBuffer* buffer, const char* expected) { serd_writer_finish(writer); - serd_buffer_sink_finish(buffer); + serd_buffer_close(buffer); const char* output = (const char*)buffer->buf; @@ -98,7 +98,6 @@ test(void) serd_sink_write(sink, 0, l2, rdf_rest, rdf_nil, NULL); check_output(writer, &buffer, "[]\n\trdf:value ( \"s1\" \"s2\" ) .\n"); - serd_buffer_sink_finish(&buffer); serd_writer_free(writer); serd_byte_sink_free(byte_sink); serd_nodes_free(nodes); diff --git a/test/test_writer.c b/test/test_writer.c index 89531eee..83d4087c 100644 --- a/test/test_writer.c +++ b/test/test_writer.c @@ -83,14 +83,15 @@ test_write_long_literal(void) serd_writer_free(writer); serd_byte_sink_free(byte_sink); serd_env_free(env); + serd_buffer_close(&buffer); - char* out = serd_buffer_sink_finish(&buffer); + char* out = (char*)buffer.buf; static const char* const expected = "<http://example.org/s>\n" "\t<http://example.org/p> \"\"\"hello \"\"\\\"world\"\"\\\"!\"\"\" .\n"; - assert(!strcmp((char*)out, expected)); + assert(!strcmp(out, expected)); serd_free(out); serd_world_free(world); @@ -369,10 +370,11 @@ check_pname_escape(const char* const lname, const char* const expected) serd_writer_free(writer); serd_byte_sink_free(byte_sink); serd_env_free(env); + serd_buffer_close(&buffer); - char* out = serd_buffer_sink_finish(&buffer); + char* out = (char*)buffer.buf; - assert(!strcmp((char*)out, expected)); + assert(!strcmp(out, expected)); serd_free(out); free(uri); diff --git a/tools/serd-pipe.c b/tools/serd-pipe.c index 25efb54f..57392c8a 100644 --- a/tools/serd-pipe.c +++ b/tools/serd-pipe.c @@ -83,10 +83,12 @@ run(const Options opts) (st = serd_read_inputs( app.world, opts.common, app.env, opts.n_inputs, opts.inputs, sink)) || (st = serd_writer_finish(app.writer))) { + serd_sink_free(canon); serd_tool_cleanup(app); return st; } + serd_sink_free(canon); return serd_tool_cleanup(app); } diff --git a/tools/serd-sort.c b/tools/serd-sort.c index 5e71e95e..bbdfa164 100644 --- a/tools/serd-sort.c +++ b/tools/serd-sort.c @@ -95,6 +95,8 @@ run(const Options opts) opts.n_inputs, opts.inputs, inserter))) { + serd_sink_free(inserter); + serd_model_free(model); serd_tool_cleanup(app); return st; } @@ -127,6 +129,9 @@ run(const Options opts) st = serd_writer_finish(app.writer); } + serd_sink_free(inserter); + serd_model_free(model); + const SerdStatus cst = serd_tool_cleanup(app); return st ? st : cst; } |