aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2023-05-04 14:58:45 -0400
committerDavid Robillard <d@drobilla.net>2023-12-02 18:49:08 -0500
commit551faf54e35c757144204bf7a7949c0f7d0a20a3 (patch)
treee8f761fc6361c75dd443d6e91806d27025d4102a
parentc3bc111f7fb2a44e068e5250f7823352e44c76dc (diff)
downloadserd-551faf54e35c757144204bf7a7949c0f7d0a20a3.tar.gz
serd-551faf54e35c757144204bf7a7949c0f7d0a20a3.tar.bz2
serd-551faf54e35c757144204bf7a7949c0f7d0a20a3.zip
Make SerdBuffer an output stream
-rw-r--r--include/serd/buffer.h33
-rw-r--r--meson.build1
-rw-r--r--src/buffer.c39
-rw-r--r--src/node.c6
-rw-r--r--src/uri.c9
-rw-r--r--src/writer.c27
-rw-r--r--test/test_reader_writer.c5
-rw-r--r--test/test_terse_write.c9
-rw-r--r--test/test_writer.c25
9 files changed, 89 insertions, 65 deletions
diff --git a/include/serd/buffer.h b/include/serd/buffer.h
index d4fd63fb..556e7e1f 100644
--- a/include/serd/buffer.h
+++ b/include/serd/buffer.h
@@ -17,6 +17,9 @@ SERD_BEGIN_DECLS
The #SerdBuffer type represents a writable area of memory with a known size.
+ A #SerdWriteFunc function is provided which enable writing output to a
+ memory buffer (as `fwrite` does for files).
+
@{
*/
@@ -27,27 +30,29 @@ typedef struct {
} SerdBuffer;
/**
- A convenience sink function for writing to a string.
+ 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.
- 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. When the write is finished, the string should be
- retrieved with serd_buffer_sink_finish().
+ 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_sink(const void* ZIX_NONNULL buf,
- size_t size,
- size_t nmemb,
- void* ZIX_NONNULL stream);
+serd_buffer_write(const void* ZIX_NONNULL buf,
+ size_t size,
+ size_t nmemb,
+ void* ZIX_NONNULL stream);
/**
- Finish writing to a buffer with serd_buffer_sink().
+ Close the buffer for writing.
- The returned string is the result of the serialisation, which is null
- terminated (by this function) and owned by the caller.
+ This writes a terminating null byte, so the contents of the buffer are safe
+ to read as a string after this call.
*/
-SERD_API char* ZIX_NONNULL
-serd_buffer_sink_finish(SerdBuffer* ZIX_NONNULL stream);
+SERD_API int
+serd_buffer_close(void* ZIX_NONNULL stream);
/**
@}
diff --git a/meson.build b/meson.build
index 366c8faf..3a7a3285 100644
--- a/meson.build
+++ b/meson.build
@@ -152,6 +152,7 @@ c_headers = files(
)
sources = files(
+ 'src/buffer.c',
'src/byte_source.c',
'src/caret.c',
'src/env.c',
diff --git a/src/buffer.c b/src/buffer.c
new file mode 100644
index 00000000..12f343c2
--- /dev/null
+++ b/src/buffer.c
@@ -0,0 +1,39 @@
+// Copyright 2011-2021 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#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/node.c b/src/node.c
index 79fcc38e..189b02d9 100644
--- a/src/node.c
+++ b/src/node.c
@@ -532,11 +532,11 @@ serd_new_file_uri(const SerdStringView path, const SerdStringView hostname)
{
SerdBuffer buffer = {NULL, 0U};
- serd_write_file_uri(path, hostname, serd_buffer_sink, &buffer);
- serd_buffer_sink_finish(&buffer);
+ serd_write_file_uri(path, hostname, serd_buffer_write, &buffer);
+ serd_buffer_close(&buffer);
const size_t length = buffer.len;
- const char* const string = serd_buffer_sink_finish(&buffer);
+ const char* const string = (char*)buffer.buf;
SerdNode* const node = serd_new_string(serd_substring(string, length));
free(buffer.buf);
diff --git a/src/uri.c b/src/uri.c
index e8bc9e05..e7445377 100644
--- a/src/uri.c
+++ b/src/uri.c
@@ -51,23 +51,24 @@ 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 uint8_t hi = hex_digit_value((const uint8_t)s[1]);
const uint8_t lo = hex_digit_value((const uint8_t)s[2]);
const char c = (char)((hi << 4U) | lo);
- 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 482721f9..c2008e2a 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -14,7 +14,6 @@
#include "world.h"
#include "serd/attributes.h"
-#include "serd/buffer.h"
#include "serd/env.h"
#include "serd/event.h"
#include "serd/node.h"
@@ -1490,29 +1489,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(size == 1);
- (void)size;
-
- SerdBuffer* buffer = (SerdBuffer*)stream;
- char* new_buf = (char*)realloc(buffer->buf, buffer->len + nmemb);
- if (new_buf) {
- memcpy(new_buf + buffer->len, buf, nmemb);
- buffer->buf = new_buf;
- buffer->len += nmemb;
- }
- return nmemb;
-}
-
-char*
-serd_buffer_sink_finish(SerdBuffer* const 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 3cf7cf5d..acd43ad1 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -210,7 +210,7 @@ test_writer(const char* const path)
// Test buffer sink
SerdBuffer buffer = {NULL, 0};
writer =
- serd_writer_new(world, SERD_TURTLE, 0, env, serd_buffer_sink, &buffer);
+ serd_writer_new(world, SERD_TURTLE, 0, env, serd_buffer_write, &buffer);
SerdNode* const base = serd_new_uri(serd_string("http://example.org/base"));
@@ -218,8 +218,9 @@ test_writer(const char* const path)
serd_node_free(base);
serd_writer_free(writer);
- char* out = serd_buffer_sink_finish(&buffer);
+ serd_buffer_close(&buffer);
+ char* const 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 277f025e..17bacc13 100644
--- a/test/test_terse_write.c
+++ b/test/test_terse_write.c
@@ -8,7 +8,6 @@
#include "serd/node.h"
#include "serd/sink.h"
#include "serd/statement.h"
-#include "serd/stream.h"
#include "serd/string_view.h"
#include "serd/syntax.h"
#include "serd/world.h"
@@ -25,7 +24,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;
@@ -54,8 +53,8 @@ test(void)
serd_env_set_prefix(env, serd_string("rdf"), serd_string(NS_RDF));
- SerdWriter* writer = serd_writer_new(
- world, SERD_TURTLE, 0, env, (SerdWriteFunc)serd_buffer_sink, &buffer);
+ SerdWriter* writer =
+ serd_writer_new(world, SERD_TURTLE, 0, env, serd_buffer_write, &buffer);
const SerdSink* sink = serd_writer_sink(writer);
@@ -87,7 +86,7 @@ test(void)
serd_sink_write(sink, 0, l2, rdf_rest, rdf_nil, NULL);
check_output(writer, &buffer, "[] rdf:value ( \"s1\" \"s2\" ) .\n");
- serd_buffer_sink_finish(&buffer);
+ serd_buffer_close(&buffer);
serd_writer_free(writer);
serd_node_free(rdf_nil);
serd_node_free(rdf_rest);
diff --git a/test/test_writer.c b/test/test_writer.c
index a4d92c5b..fe685437 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -29,7 +29,7 @@ test_write_bad_event(void)
SerdEnv* env = serd_env_new(serd_empty_string());
SerdBuffer buffer = {NULL, 0};
SerdWriter* writer =
- serd_writer_new(world, SERD_TURTLE, 0U, env, serd_buffer_sink, &buffer);
+ serd_writer_new(world, SERD_TURTLE, 0U, env, serd_buffer_write, &buffer);
assert(writer);
@@ -37,8 +37,9 @@ test_write_bad_event(void)
assert(serd_sink_write_event(serd_writer_sink(writer), &event) ==
SERD_BAD_ARG);
- char* const out = serd_buffer_sink_finish(&buffer);
+ serd_buffer_close(&buffer);
+ char* const out = (char*)buffer.buf;
assert(!strcmp(out, ""));
serd_free(out);
@@ -54,7 +55,7 @@ test_write_bad_prefix(void)
SerdEnv* env = serd_env_new(serd_empty_string());
SerdBuffer buffer = {NULL, 0};
SerdWriter* writer =
- serd_writer_new(world, SERD_TURTLE, 0U, env, serd_buffer_sink, &buffer);
+ serd_writer_new(world, SERD_TURTLE, 0U, env, serd_buffer_write, &buffer);
assert(writer);
@@ -64,8 +65,9 @@ test_write_bad_prefix(void)
assert(serd_sink_write_prefix(serd_writer_sink(writer), name, uri) ==
SERD_BAD_ARG);
- char* const out = serd_buffer_sink_finish(&buffer);
+ serd_buffer_close(&buffer);
+ char* const out = (char*)buffer.buf;
assert(!strcmp(out, ""));
serd_free(out);
@@ -83,7 +85,7 @@ test_write_long_literal(void)
SerdEnv* env = serd_env_new(serd_empty_string());
SerdBuffer buffer = {NULL, 0};
SerdWriter* writer =
- serd_writer_new(world, SERD_TURTLE, 0U, env, serd_buffer_sink, &buffer);
+ serd_writer_new(world, SERD_TURTLE, 0U, env, serd_buffer_write, &buffer);
assert(writer);
@@ -98,8 +100,9 @@ test_write_long_literal(void)
serd_node_free(s);
serd_writer_free(writer);
serd_env_free(env);
+ serd_buffer_close(&buffer);
- char* out = serd_buffer_sink_finish(&buffer);
+ char* const out = (char*)buffer.buf;
static const char* const expected =
"<http://example.org/s>\n"
@@ -307,14 +310,15 @@ test_write_empty_syntax(void)
SerdBuffer buffer = {NULL, 0};
SerdWriter* writer = serd_writer_new(
- world, SERD_SYNTAX_EMPTY, 0U, env, serd_buffer_sink, &buffer);
+ world, SERD_SYNTAX_EMPTY, 0U, env, serd_buffer_write, &buffer);
assert(writer);
assert(!serd_sink_write(serd_writer_sink(writer), 0U, s, p, o, NULL));
- char* out = serd_buffer_sink_finish(&buffer);
+ serd_buffer_close(&buffer);
+ char* const out = (char*)buffer.buf;
assert(strlen(out) == 0);
serd_free(out);
@@ -334,7 +338,7 @@ check_pname_escape(const char* const lname, const char* const expected)
SerdBuffer buffer = {NULL, 0};
SerdWriter* writer =
- serd_writer_new(world, SERD_TURTLE, 0U, env, serd_buffer_sink, &buffer);
+ serd_writer_new(world, SERD_TURTLE, 0U, env, serd_buffer_write, &buffer);
assert(writer);
@@ -359,8 +363,9 @@ check_pname_escape(const char* const lname, const char* const expected)
serd_node_free(s);
serd_writer_free(writer);
serd_env_free(env);
+ serd_buffer_close(&buffer);
- char* out = serd_buffer_sink_finish(&buffer);
+ char* const out = (char*)buffer.buf;
assert(!strcmp((char*)out, expected));
serd_free(out);