aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-02-20 19:18:28 -0500
committerDavid Robillard <d@drobilla.net>2022-01-13 23:03:59 -0500
commit7a0d649727b4b5a15a88a1270aa444d4ed8ce779 (patch)
treed94e1969c0b2b75d5d94f4cc6118983a845d216f
parentcbdf34638857a1c75268704c037d13b01a3b0346 (diff)
downloadserd-7a0d649727b4b5a15a88a1270aa444d4ed8ce779.tar.gz
serd-7a0d649727b4b5a15a88a1270aa444d4ed8ce779.tar.bz2
serd-7a0d649727b4b5a15a88a1270aa444d4ed8ce779.zip
Replace multiple stream callbacks with SerdEvent
This makes plumbing easier since everything goes through the same "stream" and only one callback is required to handling everything. It's also more easily extensible in case more event types need to be added in the future.
-rw-r--r--include/serd/serd.h119
-rw-r--r--src/env.c6
-rw-r--r--src/sink.c53
-rw-r--r--src/sink.h9
-rw-r--r--src/writer.c26
-rw-r--r--test/test_env.c16
-rw-r--r--test/test_overflow.c2
-rw-r--r--test/test_read_chunk.c24
-rw-r--r--test/test_reader_writer.c36
-rw-r--r--test/test_sink.c49
-rw-r--r--test/test_writer.c26
11 files changed, 228 insertions, 138 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 0120c73b..db03108c 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -1198,6 +1198,14 @@ serd_world_set_error_func(SerdWorld* SERD_NONNULL world,
@{
*/
+/// Type of a SerdEvent
+typedef enum {
+ SERD_BASE = 1, ///< Base URI changed
+ SERD_PREFIX = 2, ///< New URI prefix
+ SERD_STATEMENT = 3, ///< Statement
+ SERD_END = 4 ///< End of anonymous node
+} SerdEventType;
+
/// Flags indicating inline abbreviation information for a statement
typedef enum {
SERD_EMPTY_S = 1u << 1u, ///< Empty blank node subject
@@ -1214,42 +1222,69 @@ typedef enum {
typedef uint32_t SerdStatementFlags;
/**
- Sink function for base URI changes
+ Event for base URI changes.
+
+ Emitted whenever the base URI changes.
+*/
+typedef struct {
+ SerdEventType type; ///< #SERD_BASE
+ const SerdNode* SERD_NONNULL uri; ///< Base URI
+} SerdBaseEvent;
+
+/**
+ Event for namespace definitions.
- Called whenever the base URI of the serialisation changes.
+ Emitted whenever a prefix is defined.
*/
-typedef SerdStatus (*SerdBaseFunc)(void* SERD_NULLABLE handle,
- const SerdNode* SERD_NONNULL uri);
+typedef struct {
+ SerdEventType type; ///< #SERD_PREFIX
+ const SerdNode* SERD_NONNULL name; ///< Prefix name
+ const SerdNode* SERD_NONNULL uri; ///< Namespace URI
+} SerdPrefixEvent;
/**
- Sink function for namespace definitions.
+ Event for statements.
- Called whenever a prefix is defined in the serialisation.
+ Emitted for every statement.
*/
-typedef SerdStatus (*SerdPrefixFunc)(void* SERD_NULLABLE handle,
- const SerdNode* SERD_NONNULL name,
- const SerdNode* SERD_NONNULL uri);
+typedef struct {
+ SerdEventType type; ///< #SERD_STATEMENT
+ SerdStatementFlags flags; ///< Flags for pretty-printing
+ const SerdStatement* SERD_NONNULL statement; ///< Statement
+} SerdStatementEvent;
/**
- Sink function for statements.
+ Event for the end of anonymous node descriptions.
- Called for every RDF statement in the serialisation.
+ This is emitted to indicate that the given anonymous node will no longer be
+ described. This is used by the writer which may, for example, need to
+ write a delimiter.
*/
-typedef SerdStatus (*SerdStatementFunc)(void* SERD_NULLABLE handle,
- SerdStatementFlags flags,
- const SerdStatement* SERD_NONNULL
- statement);
+typedef struct {
+ SerdEventType type; ///< #SERD_END
+ const SerdNode* SERD_NONNULL node; ///< Anonymous node that is finished
+} SerdEndEvent;
/**
- Sink function for anonymous node end markers.
+ An event in a data stream.
- This is called to indicate that the anonymous node with the given `value`
- will no longer be referred to by any future statements (so the anonymous
- node is finished).
+ Streams of data are represented as a series of events. Events represent
+ everything that can occur in an RDF document, and are used to plumb together
+ different components. For example, when parsing a document, a reader emits
+ a stream of events which can be sent to a writer to rewrite a document, or
+ to an inserter to build a model in memory.
*/
-typedef SerdStatus (*SerdEndFunc)(void* SERD_NULLABLE handle,
- const SerdNode* SERD_NONNULL node);
+typedef union {
+ SerdEventType type; ///< Event type (always set)
+ SerdBaseEvent base; ///< Base URI changed
+ SerdPrefixEvent prefix; ///< New namespace prefix
+ SerdStatementEvent statement; ///< Statement
+ SerdEndEvent end; ///< End of anonymous node
+} SerdEvent;
+/// Function for handling events
+typedef SerdStatus (*SerdEventFunc)(void* SERD_NULLABLE handle,
+ const SerdEvent* SERD_NONNULL event);
/**
@}
@defgroup serd_sink Sink
@@ -1265,45 +1300,26 @@ typedef void (*SerdFreeFunc)(void* SERD_NULLABLE ptr);
/**
Create a new sink.
- Initially, the sink has no set functions and will do nothing. Use the
- serd_sink_set_*_func functions to set handlers for various events.
-
@param handle Opaque handle that will be passed to sink functions.
+ @param event_func Function that will be called for every event.
@param free_handle Free function to call on handle in serd_sink_free().
*/
SERD_API
SerdSink* SERD_ALLOCATED
-serd_sink_new(void* SERD_NULLABLE handle,
- SerdFreeFunc SERD_NULLABLE free_handle);
+serd_sink_new(void* SERD_NULLABLE handle,
+ SerdEventFunc SERD_NULLABLE event_func,
+ SerdFreeFunc SERD_NULLABLE free_handle);
/// Free `sink`
SERD_API
void
serd_sink_free(SerdSink* SERD_NULLABLE sink);
-/// Set a function to be called when the base URI changes
-SERD_API
-SerdStatus
-serd_sink_set_base_func(SerdSink* SERD_NONNULL sink,
- SerdBaseFunc SERD_NULLABLE base_func);
-
-/// Set a function to be called when a namespace prefix is defined
-SERD_API
-SerdStatus
-serd_sink_set_prefix_func(SerdSink* SERD_NONNULL sink,
- SerdPrefixFunc SERD_NULLABLE prefix_func);
-
-/// Set a function to be called when a statement is emitted
-SERD_API
-SerdStatus
-serd_sink_set_statement_func(SerdSink* SERD_NONNULL sink,
- SerdStatementFunc SERD_NULLABLE statement_func);
-
-/// Set a function to be called when an anonymous node ends
+/// Send an event to the sink
SERD_API
SerdStatus
-serd_sink_set_end_func(SerdSink* SERD_NONNULL sink,
- SerdEndFunc SERD_NULLABLE end_func);
+serd_sink_write_event(const SerdSink* SERD_NONNULL sink,
+ const SerdEvent* SERD_NONNULL event);
/// Set the base URI
SERD_API
@@ -1414,12 +1430,11 @@ SerdNode* SERD_ALLOCATED
serd_env_expand_node(const SerdEnv* SERD_NULLABLE env,
const SerdNode* SERD_NONNULL node);
-/// Call `func` for each prefix defined in `env`
+/// Write all prefixes in `env` to `sink`
SERD_API
void
-serd_env_foreach(const SerdEnv* SERD_NONNULL env,
- SerdPrefixFunc SERD_NONNULL func,
- void* SERD_NULLABLE handle);
+serd_env_write_prefixes(const SerdEnv* SERD_NONNULL env,
+ const SerdSink* SERD_NONNULL sink);
/**
@}
@@ -1539,7 +1554,7 @@ serd_reader_free(SerdReader* SERD_NULLABLE reader);
@{
*/
-/// Streaming serialiser that writes a text stream as statements are pushed
+/// Streaming writer that writes a text stream as it receives events
typedef struct SerdWriterImpl SerdWriter;
/**
diff --git a/src/env.c b/src/env.c
index 526e72c5..c8ac437e 100644
--- a/src/env.c
+++ b/src/env.c
@@ -249,11 +249,9 @@ serd_env_expand_node(const SerdEnv* const env, const SerdNode* const node)
}
void
-serd_env_foreach(const SerdEnv* const env,
- const SerdPrefixFunc func,
- void* const handle)
+serd_env_write_prefixes(const SerdEnv* const env, const SerdSink* const sink)
{
for (size_t i = 0; i < env->n_prefixes; ++i) {
- func(handle, env->prefixes[i].name, env->prefixes[i].uri);
+ serd_sink_write_prefix(sink, env->prefixes[i].name, env->prefixes[i].uri);
}
}
diff --git a/src/sink.c b/src/sink.c
index f78a0f18..4b7c6646 100644
--- a/src/sink.c
+++ b/src/sink.c
@@ -24,11 +24,14 @@
#include <stdlib.h>
SerdSink*
-serd_sink_new(void* handle, SerdFreeFunc free_handle)
+serd_sink_new(void* const handle,
+ SerdEventFunc event_func,
+ SerdFreeFunc free_handle)
{
SerdSink* sink = (SerdSink*)calloc(1, sizeof(SerdSink));
sink->handle = handle;
+ sink->on_event = event_func;
sink->free_handle = free_handle;
return sink;
@@ -47,37 +50,18 @@ serd_sink_free(SerdSink* sink)
}
SerdStatus
-serd_sink_set_base_func(SerdSink* sink, SerdBaseFunc base_func)
+serd_sink_write_event(const SerdSink* sink, const SerdEvent* event)
{
- sink->base = base_func;
- return SERD_SUCCESS;
-}
-
-SerdStatus
-serd_sink_set_prefix_func(SerdSink* sink, SerdPrefixFunc prefix_func)
-{
- sink->prefix = prefix_func;
- return SERD_SUCCESS;
-}
-
-SerdStatus
-serd_sink_set_statement_func(SerdSink* sink, SerdStatementFunc statement_func)
-{
- sink->statement = statement_func;
- return SERD_SUCCESS;
-}
-
-SerdStatus
-serd_sink_set_end_func(SerdSink* sink, SerdEndFunc end_func)
-{
- sink->end = end_func;
- return SERD_SUCCESS;
+ return sink->on_event ? sink->on_event(sink->handle, event) : SERD_SUCCESS;
}
SerdStatus
serd_sink_write_base(const SerdSink* sink, const SerdNode* uri)
{
- return sink->base ? sink->base(sink->handle, uri) : SERD_SUCCESS;
+ const SerdBaseEvent ev = {SERD_BASE, uri};
+
+ return sink->on_event ? sink->on_event(sink->handle, (const SerdEvent*)&ev)
+ : SERD_SUCCESS;
}
SerdStatus
@@ -85,7 +69,10 @@ serd_sink_write_prefix(const SerdSink* sink,
const SerdNode* name,
const SerdNode* uri)
{
- return sink->prefix ? sink->prefix(sink->handle, name, uri) : SERD_SUCCESS;
+ const SerdPrefixEvent ev = {SERD_PREFIX, name, uri};
+
+ return sink->on_event ? sink->on_event(sink->handle, (const SerdEvent*)&ev)
+ : SERD_SUCCESS;
}
SerdStatus
@@ -93,8 +80,11 @@ serd_sink_write_statement(const SerdSink* sink,
const SerdStatementFlags flags,
const SerdStatement* statement)
{
- return sink->statement ? sink->statement(sink->handle, flags, statement)
- : SERD_SUCCESS;
+ const SerdStatementEvent statement_ev = {SERD_STATEMENT, flags, statement};
+ SerdEvent ev = {SERD_STATEMENT};
+ ev.statement = statement_ev;
+
+ return sink->on_event ? sink->on_event(sink->handle, &ev) : SERD_SUCCESS;
}
SerdStatus
@@ -117,5 +107,8 @@ serd_sink_write(const SerdSink* sink,
SerdStatus
serd_sink_write_end(const SerdSink* sink, const SerdNode* node)
{
- return sink->end ? sink->end(sink->handle, node) : SERD_SUCCESS;
+ const SerdEndEvent ev = {SERD_END, node};
+
+ return sink->on_event ? sink->on_event(sink->handle, (const SerdEvent*)&ev)
+ : SERD_SUCCESS;
}
diff --git a/src/sink.h b/src/sink.h
index e0f99a53..6e8dffe3 100644
--- a/src/sink.h
+++ b/src/sink.h
@@ -23,12 +23,9 @@
An interface that receives a stream of RDF data.
*/
struct SerdSinkImpl {
- void* handle;
- SerdFreeFunc free_handle;
- SerdBaseFunc base;
- SerdPrefixFunc prefix;
- SerdStatementFunc statement;
- SerdEndFunc end;
+ void* handle;
+ SerdFreeFunc free_handle;
+ SerdEventFunc on_event;
};
#endif // SERD_SINK_H
diff --git a/src/writer.c b/src/writer.c
index 4e628cc2..66a53819 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -915,6 +915,25 @@ serd_writer_end_anon(SerdWriter* writer, const SerdNode* node)
return SERD_SUCCESS;
}
+static SerdStatus
+serd_writer_on_event(SerdWriter* writer, const SerdEvent* event)
+{
+ switch (event->type) {
+ case SERD_BASE:
+ return serd_writer_set_base_uri(writer, event->base.uri);
+ case SERD_PREFIX:
+ return serd_writer_set_prefix(
+ writer, event->prefix.name, event->prefix.uri);
+ case SERD_STATEMENT:
+ return serd_writer_write_statement(
+ writer, event->statement.flags, event->statement.statement);
+ case SERD_END:
+ return serd_writer_end_anon(writer, event->end.node);
+ }
+
+ return SERD_ERR_BAD_ARG;
+}
+
SerdStatus
serd_writer_finish(SerdWriter* writer)
{
@@ -955,11 +974,8 @@ serd_writer_new(SerdWorld* world,
writer->byte_sink = serd_byte_sink_new(
ssink, stream, (flags & SERD_WRITE_BULK) ? SERD_PAGE_SIZE : 1);
- writer->iface.handle = writer;
- writer->iface.base = (SerdBaseFunc)serd_writer_set_base_uri;
- writer->iface.prefix = (SerdPrefixFunc)serd_writer_set_prefix;
- writer->iface.statement = (SerdStatementFunc)serd_writer_write_statement;
- writer->iface.end = (SerdEndFunc)serd_writer_end_anon;
+ writer->iface.handle = writer;
+ writer->iface.on_event = (SerdEventFunc)serd_writer_on_event;
return writer;
}
diff --git a/test/test_env.c b/test/test_env.c
index 1748b4cf..e5be688e 100644
--- a/test/test_env.c
+++ b/test/test_env.c
@@ -22,12 +22,12 @@
#include <string.h>
static SerdStatus
-count_prefixes(void* handle, const SerdNode* name, const SerdNode* uri)
+count_prefixes(void* handle, const SerdEvent* event)
{
- (void)name;
- (void)uri;
+ if (event->type == SERD_PREFIX) {
+ ++*(int*)handle;
+ }
- ++*(int*)handle;
return SERD_SUCCESS;
}
@@ -87,10 +87,13 @@ test_env(void)
const SerdNode* blank = serd_nodes_blank(nodes, SERD_STRING("b1"));
assert(!serd_env_expand_node(env, blank));
- int n_prefixes = 0;
+ size_t n_prefixes = 0;
+ SerdSink* const count_prefixes_sink =
+ serd_sink_new(&n_prefixes, count_prefixes, NULL);
+
serd_env_set_prefix(
env, SERD_STRING("eg.2"), SERD_STRING("http://example.org/"));
- serd_env_foreach(env, count_prefixes, &n_prefixes);
+ serd_env_write_prefixes(env, count_prefixes_sink);
assert(n_prefixes == 1);
const SerdNode* shorter_uri = serd_nodes_uri(nodes, SERD_STRING("urn:foo"));
@@ -107,6 +110,7 @@ test_env(void)
assert(!serd_env_set_base_uri(env, SERD_EMPTY_STRING()));
assert(!serd_env_base_uri(env));
+ serd_sink_free(count_prefixes_sink);
serd_nodes_free(nodes);
serd_env_free(env);
}
diff --git a/test/test_overflow.c b/test/test_overflow.c
index 2d7c2542..f29c8652 100644
--- a/test/test_overflow.c
+++ b/test/test_overflow.c
@@ -30,7 +30,7 @@ test_size(SerdWorld* const world,
const SerdSyntax syntax,
const size_t stack_size)
{
- SerdSink* sink = serd_sink_new(NULL, NULL);
+ SerdSink* sink = serd_sink_new(NULL, NULL, NULL);
SerdReader* const reader = serd_reader_new(world, syntax, sink, stack_size);
assert(reader);
diff --git a/test/test_read_chunk.c b/test/test_read_chunk.c
index f5ba8d93..0055ef3a 100644
--- a/test/test_read_chunk.c
+++ b/test/test_read_chunk.c
@@ -70,15 +70,29 @@ on_end(void* handle, const SerdNode* node)
return SERD_SUCCESS;
}
+static SerdStatus
+on_event(void* handle, const SerdEvent* event)
+{
+ switch (event->type) {
+ case SERD_BASE:
+ return on_base(handle, event->base.uri);
+ case SERD_PREFIX:
+ return on_prefix(handle, event->prefix.name, event->prefix.uri);
+ case SERD_STATEMENT:
+ return on_statement(
+ handle, event->statement.flags, event->statement.statement);
+ case SERD_END:
+ break;
+ }
+
+ return on_end(handle, event->end.node);
+}
+
int
main(void)
{
SerdWorld* world = serd_world_new();
- SerdSink* sink = serd_sink_new(NULL, NULL);
- serd_sink_set_base_func(sink, on_base);
- serd_sink_set_prefix_func(sink, on_prefix);
- serd_sink_set_statement_func(sink, on_statement);
- serd_sink_set_end_func(sink, on_end);
+ SerdSink* sink = serd_sink_new(NULL, on_event, NULL);
SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
assert(reader);
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index 11939e2c..706f5962 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -25,14 +25,11 @@
#include <string.h>
static SerdStatus
-test_sink(void* handle,
- SerdStatementFlags flags,
- const SerdStatement* statement)
+count_statements(void* handle, const SerdEvent* event)
{
- (void)flags;
- (void)statement;
-
- ++*(size_t*)handle;
+ if (event->type == SERD_STATEMENT) {
+ ++*(size_t*)handle;
+ }
return SERD_SUCCESS;
}
@@ -88,13 +85,12 @@ test_read_chunks(void)
size_t n_statements = 0;
FILE* const f = tmpfile();
static const char null = 0;
- SerdSink* sink = serd_sink_new(&n_statements, NULL);
- SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
- assert(reader);
+ SerdSink* const sink = serd_sink_new(&n_statements, count_statements, NULL);
assert(sink);
- assert(f);
- serd_sink_set_statement_func(sink, test_sink);
+
+ SerdReader* const reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
+ assert(reader);
SerdStatus st = serd_reader_start_stream(
reader, (SerdReadFunc)fread, (SerdStreamErrorFunc)ferror, f, NULL, 1);
@@ -147,17 +143,14 @@ test_read_chunks(void)
static void
test_read_string(void)
{
- SerdWorld* world = serd_world_new();
- size_t n_statements = 0;
- SerdSink* sink = serd_sink_new(&n_statements, NULL);
- SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
+ SerdWorld* world = serd_world_new();
+ size_t n_statements = 0;
- assert(reader);
+ SerdSink* sink = serd_sink_new(&n_statements, count_statements, NULL);
assert(sink);
- serd_sink_set_statement_func(sink, test_sink);
-
- serd_sink_set_statement_func(sink, test_sink);
+ SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
+ assert(reader);
// Test reading a string that ends exactly at the end of input (no newline)
assert(
@@ -274,9 +267,8 @@ test_reader(const char* path)
{
SerdWorld* world = serd_world_new();
size_t n_statements = 0;
- SerdSink* const sink = serd_sink_new(&n_statements, NULL);
+ SerdSink* const sink = serd_sink_new(&n_statements, count_statements, NULL);
assert(sink);
- serd_sink_set_statement_func(sink, test_sink);
// Test that too little stack space fails gracefully
assert(!serd_reader_new(world, SERD_TURTLE, sink, 32));
diff --git a/test/test_sink.c b/test/test_sink.c
index 8b69d1df..29bb793f 100644
--- a/test/test_sink.c
+++ b/test/test_sink.c
@@ -76,6 +76,24 @@ on_end(void* handle, const SerdNode* node)
return state->return_status;
}
+static SerdStatus
+on_event(void* const handle, const SerdEvent* const event)
+{
+ switch (event->type) {
+ case SERD_BASE:
+ return on_base(handle, event->base.uri);
+ case SERD_PREFIX:
+ return on_prefix(handle, event->prefix.name, event->prefix.uri);
+ case SERD_STATEMENT:
+ return on_statement(
+ handle, event->statement.flags, event->statement.statement);
+ case SERD_END:
+ return on_end(handle, event->end.node);
+ }
+
+ return SERD_ERR_BAD_ARG;
+}
+
static void
test_callbacks(void)
{
@@ -93,23 +111,37 @@ test_callbacks(void)
State state = {0, 0, 0, 0, 0, SERD_SUCCESS};
+ const SerdBaseEvent base_event = {SERD_BASE, uri};
+ const SerdPrefixEvent prefix_event = {SERD_PREFIX, name, uri};
+ const SerdStatementEvent statement_event = {SERD_STATEMENT, 0u, statement};
+ const SerdEndEvent end_event = {SERD_END, blank};
+
// Call functions on a sink with no functions set
- SerdSink* null_sink = serd_sink_new(&state, NULL);
+ SerdSink* null_sink = serd_sink_new(&state, NULL, NULL);
+
assert(!serd_sink_write_base(null_sink, base));
assert(!serd_sink_write_prefix(null_sink, name, uri));
assert(!serd_sink_write_statement(null_sink, 0, statement));
assert(!serd_sink_write(null_sink, 0, base, uri, blank, NULL));
assert(!serd_sink_write_end(null_sink, blank));
+
+ SerdEvent event = {SERD_BASE};
+
+ event.base = base_event;
+ assert(!serd_sink_write_event(null_sink, &event));
+ event.prefix = prefix_event;
+ assert(!serd_sink_write_event(null_sink, &event));
+ event.statement = statement_event;
+ assert(!serd_sink_write_event(null_sink, &event));
+ event.end = end_event;
+ assert(!serd_sink_write_event(null_sink, &event));
+
serd_sink_free(null_sink);
// Try again with a sink that has the event handler set
- SerdSink* sink = serd_sink_new(&state, NULL);
- serd_sink_set_base_func(sink, on_base);
- serd_sink_set_prefix_func(sink, on_prefix);
- serd_sink_set_statement_func(sink, on_statement);
- serd_sink_set_end_func(sink, on_end);
+ SerdSink* sink = serd_sink_new(&state, on_event, NULL);
assert(!serd_sink_write_base(sink, base));
assert(serd_node_equals(state.last_base, base));
@@ -124,6 +156,9 @@ test_callbacks(void)
assert(!serd_sink_write_end(sink, blank));
assert(serd_node_equals(state.last_end, blank));
+ const SerdEvent junk = {(SerdEventType)42};
+ assert(serd_sink_write_event(sink, &junk) == SERD_ERR_BAD_ARG);
+
serd_sink_free(sink);
serd_statement_free(statement);
@@ -139,7 +174,7 @@ test_free(void)
// Set up a sink with dynamically allocated data and a free function
uintptr_t* data = (uintptr_t*)calloc(1, sizeof(uintptr_t));
- SerdSink* sink = serd_sink_new(data, free);
+ SerdSink* sink = serd_sink_new(data, NULL, free);
// Free the sink, which should free the data (rely on valgrind or sanitizers)
serd_sink_free(sink);
diff --git a/test/test_writer.c b/test/test_writer.c
index 6f336ca4..d7c4eac8 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -23,6 +23,31 @@
#include <string.h>
static void
+test_write_bad_event(void)
+{
+ SerdWorld* world = serd_world_new();
+ 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);
+
+ assert(writer);
+
+ const SerdEvent event = {(SerdEventType)42};
+ assert(serd_sink_write_event(serd_writer_sink(writer), &event) ==
+ SERD_ERR_BAD_ARG);
+
+ char* const out = serd_buffer_sink_finish(&buffer);
+
+ assert(!strcmp(out, ""));
+ serd_free(out);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+ serd_world_free(world);
+}
+
+static void
test_write_bad_prefix(void)
{
SerdWorld* world = serd_world_new();
@@ -148,6 +173,7 @@ test_writer_stack_overflow(void)
int
main(void)
{
+ test_write_bad_event();
test_write_bad_prefix();
test_write_long_literal();
test_writer_stack_overflow();