aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-08-13 19:31:26 -0400
committerDavid Robillard <d@drobilla.net>2022-01-28 21:57:07 -0500
commit63e7e57237a79d0447b0450a7fd3148c43052299 (patch)
treeb4c45eca1c208f8bd70a7a50632d423aec39e118
parent547ef6e0600b703dcd42a10622563d7b91434669 (diff)
downloadserd-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.h77
-rw-r--r--meson.build1
-rw-r--r--src/buffer.c52
-rw-r--r--src/byte_sink.c10
-rw-r--r--src/uri.c9
-rw-r--r--src/writer.c28
-rw-r--r--test/test_reader_writer.c2
-rw-r--r--test/test_terse_write.c3
-rw-r--r--test/test_writer.c10
-rw-r--r--tools/serd-pipe.c2
-rw-r--r--tools/serd-sort.c5
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*
diff --git a/src/uri.c b/src/uri.c
index 1862c280..bbd53ca3 100644
--- a/src/uri.c
+++ b/src/uri.c
@@ -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;
}