aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/conf.py.in1
-rw-r--r--include/serd/reader.h12
-rw-r--r--include/serd/sink.h68
-rw-r--r--include/serd/writer.h40
-rw-r--r--meson.build1
-rw-r--r--src/n3.c16
-rw-r--r--src/reader.c51
-rw-r--r--src/reader.h37
-rw-r--r--src/serdi.c9
-rw-r--r--src/sink.c102
-rw-r--r--src/sink.h21
-rw-r--r--src/writer.c24
-rw-r--r--test/meson.build1
-rw-r--r--test/test_free_null.c2
-rw-r--r--test/test_reader_writer.c109
-rw-r--r--test/test_sink.c151
-rw-r--r--test/test_writer.c31
17 files changed, 480 insertions, 196 deletions
diff --git a/doc/conf.py.in b/doc/conf.py.in
index 8f99424c..3ce284b1 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -30,6 +30,7 @@ _opaque = [
"SerdEnvImpl",
"SerdNodeImpl",
"SerdReaderImpl",
+ "SerdSinkImpl",
"SerdWriterImpl",
"int64_t",
"size_t",
diff --git a/include/serd/reader.h b/include/serd/reader.h
index 63c2c5ac..779505cf 100644
--- a/include/serd/reader.h
+++ b/include/serd/reader.h
@@ -29,13 +29,7 @@ typedef struct SerdReaderImpl SerdReader;
/// Create a new RDF reader
SERD_API SerdReader* SERD_ALLOCATED
-serd_reader_new(SerdSyntax syntax,
- void* SERD_NULLABLE handle,
- void (*SERD_NULLABLE free_handle)(void* SERD_NULLABLE),
- SerdBaseFunc SERD_NULLABLE base_func,
- SerdPrefixFunc SERD_NULLABLE prefix_func,
- SerdStatementFunc SERD_NULLABLE statement_func,
- SerdEndFunc SERD_NULLABLE end_func);
+serd_reader_new(SerdSyntax syntax, const SerdSink* SERD_NONNULL sink);
/**
Enable or disable strict parsing.
@@ -58,10 +52,6 @@ serd_reader_set_error_sink(SerdReader* SERD_NONNULL reader,
SerdErrorFunc SERD_NULLABLE error_func,
void* SERD_NULLABLE error_handle);
-/// Return the `handle` passed to serd_reader_new()
-SERD_PURE_API void* SERD_NULLABLE
-serd_reader_handle(const SerdReader* SERD_NONNULL reader);
-
/**
Set a prefix to be added to all blank node identifiers.
diff --git a/include/serd/sink.h b/include/serd/sink.h
index 702197d5..f24db683 100644
--- a/include/serd/sink.h
+++ b/include/serd/sink.h
@@ -56,6 +56,74 @@ typedef SerdStatus (*SerdStatementFunc)(void* SERD_NULLABLE handle,
typedef SerdStatus (*SerdEndFunc)(void* SERD_NULLABLE handle,
const SerdNode* SERD_NONNULL node);
+/// An interface that receives a stream of RDF data
+typedef struct SerdSinkImpl SerdSink;
+
+/// Function to free an opaque handle
+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 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);
+
+/// 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
+SERD_API SerdStatus
+serd_sink_set_end_func(SerdSink* SERD_NONNULL sink,
+ SerdEndFunc SERD_NULLABLE end_func);
+
+/// Set the base URI
+SERD_API SerdStatus
+serd_sink_write_base(const SerdSink* SERD_NONNULL sink,
+ const SerdNode* SERD_NONNULL uri);
+
+/// Set a namespace prefix
+SERD_API SerdStatus
+serd_sink_write_prefix(const SerdSink* SERD_NONNULL sink,
+ const SerdNode* SERD_NONNULL name,
+ const SerdNode* SERD_NONNULL uri);
+
+/// Write a statement from individual nodes
+SERD_API SerdStatus
+serd_sink_write(const SerdSink* SERD_NONNULL sink,
+ SerdStatementFlags flags,
+ const SerdNode* SERD_NONNULL subject,
+ const SerdNode* SERD_NONNULL predicate,
+ const SerdNode* SERD_NONNULL object,
+ const SerdNode* SERD_NULLABLE graph);
+
+/// Mark the end of an anonymous node
+SERD_API SerdStatus
+serd_sink_write_end(const SerdSink* SERD_NONNULL sink,
+ const SerdNode* SERD_NONNULL node);
+
/**
@}
*/
diff --git a/include/serd/writer.h b/include/serd/writer.h
index 94747309..5f1d0535 100644
--- a/include/serd/writer.h
+++ b/include/serd/writer.h
@@ -8,7 +8,7 @@
#include "serd/env.h"
#include "serd/error.h"
#include "serd/node.h"
-#include "serd/statement.h"
+#include "serd/sink.h"
#include "serd/status.h"
#include "serd/stream.h"
#include "serd/syntax.h"
@@ -57,9 +57,9 @@ serd_writer_new(SerdSyntax syntax,
SERD_API void
serd_writer_free(SerdWriter* SERD_NULLABLE writer);
-/// Return the env used by `writer`
-SERD_PURE_API SerdEnv* SERD_NONNULL
-serd_writer_env(SerdWriter* SERD_NONNULL writer);
+/// Return a sink interface that emits statements via `writer`
+SERD_CONST_API const SerdSink* SERD_NONNULL
+serd_writer_sink(SerdWriter* SERD_NONNULL writer);
/**
Set a function to be called when errors occur during writing.
@@ -106,38 +106,6 @@ serd_writer_set_root_uri(SerdWriter* SERD_NONNULL writer,
const SerdNode* SERD_NULLABLE uri);
/**
- Set a namespace prefix (and emit directive if applicable).
-
- Note this function can be safely casted to #SerdPrefixFunc.
-*/
-SERD_API SerdStatus
-serd_writer_set_prefix(SerdWriter* SERD_NONNULL writer,
- const SerdNode* SERD_NONNULL name,
- const SerdNode* SERD_NONNULL uri);
-
-/**
- Write a statement.
-
- Note this function can be safely casted to #SerdStatementFunc.
-*/
-SERD_API SerdStatus
-serd_writer_write_statement(SerdWriter* SERD_NONNULL writer,
- SerdStatementFlags flags,
- const SerdNode* SERD_NULLABLE graph,
- const SerdNode* SERD_NONNULL subject,
- const SerdNode* SERD_NONNULL predicate,
- const SerdNode* SERD_NONNULL object);
-
-/**
- Mark the end of an anonymous node's description.
-
- Note this function can be safely casted to #SerdEndFunc.
-*/
-SERD_API SerdStatus
-serd_writer_end_anon(SerdWriter* SERD_NONNULL writer,
- const SerdNode* SERD_NULLABLE node);
-
-/**
Finish a write.
This flushes any pending output, for example terminating punctuation, so
diff --git a/meson.build b/meson.build
index 30ac2374..70f8835e 100644
--- a/meson.build
+++ b/meson.build
@@ -132,6 +132,7 @@ sources = files(
'src/n3.c',
'src/node.c',
'src/reader.c',
+ 'src/sink.c',
'src/string.c',
'src/system.c',
'src/uri.c',
diff --git a/src/n3.c b/src/n3.c
index 929b1a97..cd7e5a03 100644
--- a/src/n3.c
+++ b/src/n3.c
@@ -11,6 +11,7 @@
#include "serd/node.h"
#include "serd/reader.h"
+#include "serd/sink.h"
#include "serd/statement.h"
#include "serd/status.h"
#include "serd/syntax.h"
@@ -1099,9 +1100,7 @@ read_anon(SerdReader* const reader,
return r_err(reader, SERD_BAD_SYNTAX, "'.' inside blank\n");
}
read_ws_star(reader);
- if (reader->end_func) {
- reader->end_func(reader->handle, deref(reader, *dest));
- }
+ serd_sink_write_end(reader->sink, deref(reader, *dest));
*ctx.flags = old_flags;
}
@@ -1453,9 +1452,7 @@ read_base(SerdReader* const reader, const bool sparql, const bool token)
Ref uri = 0;
TRY(st, read_IRIREF(reader, &uri));
- if (reader->base_func) {
- TRY(st, reader->base_func(reader->handle, deref(reader, uri)));
- }
+ TRY(st, serd_sink_write_base(reader->sink, deref(reader, uri)));
pop_node(reader, uri);
read_ws_star(reader);
@@ -1491,10 +1488,8 @@ read_prefixID(SerdReader* const reader, const bool sparql, const bool token)
Ref uri = 0;
TRY(st, read_IRIREF(reader, &uri));
- if (reader->prefix_func) {
- st = reader->prefix_func(
- reader->handle, deref(reader, name), deref(reader, uri));
- }
+ st = serd_sink_write_prefix(
+ reader->sink, deref(reader, name), deref(reader, uri));
pop_node(reader, uri);
pop_node(reader, name);
@@ -1502,7 +1497,6 @@ read_prefixID(SerdReader* const reader, const bool sparql, const bool token)
read_ws_star(reader);
st = eat_byte_check(reader, '.') ? SERD_SUCCESS : SERD_BAD_SYNTAX;
}
-
return st;
}
diff --git a/src/reader.c b/src/reader.c
index c7919ca6..284de45d 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -143,14 +143,12 @@ emit_statement(SerdReader* const reader, const ReadContext ctx, const Ref o)
graph = reader->default_graph;
}
- const SerdStatus st = !reader->statement_func
- ? SERD_SUCCESS
- : reader->statement_func(reader->handle,
- *ctx.flags,
- graph,
- deref(reader, ctx.subject),
- deref(reader, ctx.predicate),
- deref(reader, o));
+ const SerdStatus st = serd_sink_write(reader->sink,
+ *ctx.flags,
+ deref(reader, ctx.subject),
+ deref(reader, ctx.predicate),
+ deref(reader, o),
+ graph);
*ctx.flags &= SERD_ANON_CONT | SERD_LIST_CONT; // Preserve only cont flags
return st;
@@ -164,26 +162,16 @@ read_doc(SerdReader* const reader)
}
SerdReader*
-serd_reader_new(const SerdSyntax syntax,
- void* const handle,
- void (*const free_handle)(void*),
- const SerdBaseFunc base_func,
- const SerdPrefixFunc prefix_func,
- const SerdStatementFunc statement_func,
- const SerdEndFunc end_func)
+serd_reader_new(const SerdSyntax syntax, const SerdSink* const sink)
{
- SerdReader* me = (SerdReader*)calloc(1, sizeof(SerdReader));
- me->handle = handle;
- me->free_handle = free_handle;
- me->base_func = base_func;
- me->prefix_func = prefix_func;
- me->statement_func = statement_func;
- me->end_func = end_func;
- me->default_graph = NULL;
- me->stack = serd_stack_new(SERD_PAGE_SIZE);
- me->syntax = syntax;
- me->next_id = 1;
- me->strict = true;
+ SerdReader* me = (SerdReader*)calloc(1, sizeof(SerdReader));
+
+ me->sink = sink;
+ me->default_graph = NULL;
+ me->stack = serd_stack_new(SERD_PAGE_SIZE);
+ me->syntax = syntax;
+ me->next_id = 1;
+ me->strict = true;
me->rdf_first = push_node(me, SERD_URI, NS_RDF "first", 48);
me->rdf_rest = push_node(me, SERD_URI, NS_RDF "rest", 47);
@@ -224,18 +212,9 @@ serd_reader_free(SerdReader* const reader)
#endif
free(reader->stack.buf);
free(reader->bprefix);
- if (reader->free_handle) {
- reader->free_handle(reader->handle);
- }
free(reader);
}
-void*
-serd_reader_handle(const SerdReader* const reader)
-{
- return reader->handle;
-}
-
void
serd_reader_add_blank_prefix(SerdReader* const reader, const char* const prefix)
{
diff --git a/src/reader.h b/src/reader.h
index 0de1df2a..db749fe8 100644
--- a/src/reader.h
+++ b/src/reader.h
@@ -45,27 +45,22 @@ typedef struct {
} ReadContext;
struct SerdReaderImpl {
- void* handle;
- void (*free_handle)(void* ptr);
- SerdBaseFunc base_func;
- SerdPrefixFunc prefix_func;
- SerdStatementFunc statement_func;
- SerdEndFunc end_func;
- SerdErrorFunc error_func;
- void* error_handle;
- Ref rdf_first;
- Ref rdf_rest;
- Ref rdf_nil;
- SerdNode* default_graph;
- SerdByteSource source;
- SerdStack stack;
- SerdSyntax syntax;
- unsigned next_id;
- uint8_t* buf;
- char* bprefix;
- size_t bprefix_len;
- bool strict; ///< True iff strict parsing
- bool seen_genid;
+ const SerdSink* sink;
+ SerdErrorFunc error_func;
+ void* error_handle;
+ Ref rdf_first;
+ Ref rdf_rest;
+ Ref rdf_nil;
+ SerdNode* default_graph;
+ SerdByteSource source;
+ SerdStack stack;
+ SerdSyntax syntax;
+ unsigned next_id;
+ uint8_t* buf;
+ char* bprefix;
+ size_t bprefix_len;
+ bool strict; ///< True iff strict parsing
+ bool seen_genid;
#ifdef SERD_STACK_CHECK
Ref* allocs; ///< Stack of push offsets
size_t n_allocs; ///< Number of stack pushes
diff --git a/src/serdi.c b/src/serdi.c
index e3b41204..0de64b81 100644
--- a/src/serdi.c
+++ b/src/serdi.c
@@ -9,7 +9,6 @@
#include "serd/error.h"
#include "serd/node.h"
#include "serd/reader.h"
-#include "serd/sink.h"
#include "serd/status.h"
#include "serd/stream.h"
#include "serd/string_view.h"
@@ -348,13 +347,7 @@ main(int argc, char** argv)
output_syntax, writer_flags, env, (SerdWriteFunc)fwrite, out_fd);
SerdReader* const reader =
- serd_reader_new(input_syntax,
- writer,
- NULL,
- (SerdBaseFunc)serd_writer_set_base_uri,
- (SerdPrefixFunc)serd_writer_set_prefix,
- (SerdStatementFunc)serd_writer_write_statement,
- (SerdEndFunc)serd_writer_end_anon);
+ serd_reader_new(input_syntax, serd_writer_sink(writer));
serd_reader_set_strict(reader, !lax);
if (quiet) {
diff --git a/src/sink.c b/src/sink.c
new file mode 100644
index 00000000..5e04ced6
--- /dev/null
+++ b/src/sink.c
@@ -0,0 +1,102 @@
+// Copyright 2011-2020 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#include "sink.h"
+
+#include "serd/node.h"
+#include "serd/sink.h"
+#include "serd/statement.h"
+#include "serd/status.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+SerdSink*
+serd_sink_new(void* handle, SerdFreeFunc free_handle)
+{
+ SerdSink* sink = (SerdSink*)calloc(1, sizeof(SerdSink));
+
+ sink->handle = handle;
+ sink->free_handle = free_handle;
+
+ return sink;
+}
+
+void
+serd_sink_free(SerdSink* sink)
+{
+ if (sink) {
+ if (sink->free_handle) {
+ sink->free_handle(sink->handle);
+ }
+
+ free(sink);
+ }
+}
+
+SerdStatus
+serd_sink_set_base_func(SerdSink* sink, SerdBaseFunc base_func)
+{
+ 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;
+}
+
+SerdStatus
+serd_sink_write_base(const SerdSink* sink, const SerdNode* uri)
+{
+ return sink->base ? sink->base(sink->handle, uri) : SERD_SUCCESS;
+}
+
+SerdStatus
+serd_sink_write_prefix(const SerdSink* sink,
+ const SerdNode* name,
+ const SerdNode* uri)
+{
+ return sink->prefix ? sink->prefix(sink->handle, name, uri) : SERD_SUCCESS;
+}
+
+SerdStatus
+serd_sink_write(const SerdSink* sink,
+ const SerdStatementFlags flags,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const SerdNode* object,
+ const SerdNode* graph)
+{
+ assert(sink);
+ assert(subject);
+ assert(predicate);
+ assert(object);
+
+ return sink->statement
+ ? sink->statement(
+ sink->handle, flags, graph, subject, predicate, object)
+ : SERD_SUCCESS;
+}
+
+SerdStatus
+serd_sink_write_end(const SerdSink* sink, const SerdNode* node)
+{
+ return sink->end ? sink->end(sink->handle, node) : SERD_SUCCESS;
+}
diff --git a/src/sink.h b/src/sink.h
new file mode 100644
index 00000000..98884fe2
--- /dev/null
+++ b/src/sink.h
@@ -0,0 +1,21 @@
+// Copyright 2011-2020 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#ifndef SERD_SRC_SINK_H
+#define SERD_SRC_SINK_H
+
+#include "serd/sink.h"
+
+/**
+ An interface that receives a stream of RDF data.
+*/
+struct SerdSinkImpl {
+ void* handle;
+ SerdFreeFunc free_handle;
+ SerdBaseFunc base;
+ SerdPrefixFunc prefix;
+ SerdStatementFunc statement;
+ SerdEndFunc end;
+};
+
+#endif // SERD_SRC_SINK_H
diff --git a/src/writer.c b/src/writer.c
index 01beb315..e674f2b1 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -5,6 +5,7 @@
#include "env.h"
#include "node.h"
#include "serd_internal.h"
+#include "sink.h"
#include "stack.h"
#include "string_utils.h"
#include "try.h"
@@ -15,6 +16,7 @@
#include "serd/env.h"
#include "serd/error.h"
#include "serd/node.h"
+#include "serd/sink.h"
#include "serd/statement.h"
#include "serd/status.h"
#include "serd/stream.h"
@@ -123,6 +125,7 @@ static const SepRule rules[] = {
#undef SEP_EACH
struct SerdWriterImpl {
+ SerdSink iface;
SerdSyntax syntax;
SerdWriterFlags flags;
SerdEnv* env;
@@ -143,6 +146,11 @@ typedef enum { WRITE_STRING, WRITE_LONG_STRING } TextContext;
typedef enum { RESET_GRAPH = 1U << 0U, RESET_INDENT = 1U << 1U } ResetFlag;
SERD_NODISCARD static SerdStatus
+serd_writer_set_prefix(SerdWriter* writer,
+ const SerdNode* name,
+ const SerdNode* uri);
+
+SERD_NODISCARD static SerdStatus
write_node(SerdWriter* writer,
const SerdNode* node,
Field field,
@@ -951,7 +959,7 @@ terminate_context(SerdWriter* writer)
return st;
}
-SerdStatus
+static SerdStatus
serd_writer_write_statement(SerdWriter* const writer,
const SerdStatementFlags flags,
const SerdNode* const graph,
@@ -1090,7 +1098,7 @@ serd_writer_write_statement(SerdWriter* const writer,
return st;
}
-SerdStatus
+static SerdStatus
serd_writer_end_anon(SerdWriter* writer, const SerdNode* node)
{
SerdStatus st = SERD_SUCCESS;
@@ -1146,6 +1154,12 @@ serd_writer_new(SerdSyntax syntax,
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;
+
return writer;
}
@@ -1259,10 +1273,10 @@ serd_writer_free(SerdWriter* writer)
free(writer);
}
-SerdEnv*
-serd_writer_env(SerdWriter* writer)
+const SerdSink*
+serd_writer_sink(SerdWriter* writer)
{
- return writer->env;
+ return &writer->iface;
}
size_t
diff --git a/test/meson.build b/test/meson.build
index f6f56c2b..5742c545 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -123,6 +123,7 @@ unit_tests = [
'free_null',
'node',
'reader_writer',
+ 'sink',
'string',
'uri',
'writer',
diff --git a/test/test_free_null.c b/test/test_free_null.c
index 10ff4d10..d3248cb5 100644
--- a/test/test_free_null.c
+++ b/test/test_free_null.c
@@ -7,6 +7,7 @@
#include "serd/memory.h"
#include "serd/node.h"
#include "serd/reader.h"
+#include "serd/sink.h"
#include "serd/writer.h"
#include <stddef.h>
@@ -17,6 +18,7 @@ main(void)
serd_free(NULL);
serd_node_free(NULL);
serd_env_free(NULL);
+ serd_sink_free(NULL);
serd_reader_free(NULL);
serd_writer_free(NULL);
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index 3b656c2e..96c81d12 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -185,17 +185,16 @@ test_read_nquads_chunks(const char* const path)
fseek(f, 0, SEEK_SET);
ReaderTest* const rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
- SerdReader* const reader = serd_reader_new(SERD_NQUADS,
- rt,
- free,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
+ SerdSink* const sink = serd_sink_new(rt, NULL);
+ SerdReader* const reader = serd_reader_new(SERD_NQUADS, sink);
assert(reader);
- assert(serd_reader_handle(reader) == rt);
+ assert(sink);
assert(f);
+ serd_sink_set_base_func(sink, test_base_sink);
+ serd_sink_set_prefix_func(sink, test_prefix_sink);
+ serd_sink_set_statement_func(sink, test_statement_sink);
+ serd_sink_set_end_func(sink, test_end_sink);
SerdStatus st = serd_reader_start_stream(reader, f, NULL, false);
assert(st == SERD_SUCCESS);
@@ -243,6 +242,8 @@ test_read_nquads_chunks(const char* const path)
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
serd_reader_free(reader);
+ serd_sink_free(sink);
+ free(rt);
fclose(f);
remove(path);
}
@@ -265,17 +266,16 @@ test_read_turtle_chunks(const char* const path)
fseek(f, 0, SEEK_SET);
ReaderTest* const rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
- SerdReader* const reader = serd_reader_new(SERD_TURTLE,
- rt,
- free,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
+ SerdSink* const sink = serd_sink_new(rt, NULL);
+ SerdReader* const reader = serd_reader_new(SERD_TURTLE, sink);
assert(reader);
- assert(serd_reader_handle(reader) == rt);
+ assert(sink);
assert(f);
+ serd_sink_set_base_func(sink, test_base_sink);
+ serd_sink_set_prefix_func(sink, test_prefix_sink);
+ serd_sink_set_statement_func(sink, test_statement_sink);
+ serd_sink_set_end_func(sink, test_end_sink);
SerdStatus st = serd_reader_start_stream(reader, f, NULL, false);
assert(st == SERD_SUCCESS);
@@ -339,6 +339,8 @@ test_read_turtle_chunks(const char* const path)
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
serd_reader_free(reader);
+ serd_sink_free(sink);
+ free(rt);
fclose(f);
remove(path);
}
@@ -347,16 +349,16 @@ static void
test_read_string(void)
{
ReaderTest* rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
- SerdReader* reader = serd_reader_new(SERD_TURTLE,
- rt,
- free,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
+ SerdSink* sink = serd_sink_new(rt, NULL);
+ SerdReader* reader = serd_reader_new(SERD_TURTLE, sink);
assert(reader);
- assert(serd_reader_handle(reader) == rt);
+ assert(sink);
+
+ serd_sink_set_base_func(sink, test_base_sink);
+ serd_sink_set_prefix_func(sink, test_prefix_sink);
+ serd_sink_set_statement_func(sink, test_statement_sink);
+ serd_sink_set_end_func(sink, test_end_sink);
// Test reading a string that ends exactly at the end of input (no newline)
const SerdStatus st =
@@ -371,6 +373,8 @@ test_read_string(void)
assert(rt->n_end == 0);
serd_reader_free(reader);
+ serd_sink_free(sink);
+ free(rt);
}
static size_t
@@ -425,14 +429,8 @@ test_write_errors(void)
SerdWriter* const writer =
serd_writer_new(syntax, style, env, faulty_sink, &ctx);
- SerdReader* const reader =
- serd_reader_new(SERD_TRIG,
- writer,
- NULL,
- (SerdBaseFunc)serd_writer_set_base_uri,
- (SerdPrefixFunc)serd_writer_set_prefix,
- (SerdStatementFunc)serd_writer_write_statement,
- (SerdEndFunc)serd_writer_end_anon);
+ const SerdSink* const sink = serd_writer_sink(writer);
+ SerdReader* const reader = serd_reader_new(SERD_TRIG, sink);
serd_reader_set_error_sink(reader, quiet_error_sink, NULL);
serd_writer_set_error_sink(writer, quiet_error_sink, NULL);
@@ -463,10 +461,10 @@ test_writer(const char* const path)
SerdNode* lit = serd_new_string(serd_string("hello"));
- assert(serd_writer_set_base_uri(writer, lit));
- assert(serd_writer_set_prefix(writer, lit, lit));
- assert(serd_writer_end_anon(writer, NULL));
- assert(serd_writer_env(writer) == env);
+ const SerdSink* const iface = serd_writer_sink(writer);
+ assert(serd_sink_write_base(iface, lit));
+ assert(serd_sink_write_prefix(iface, lit, lit));
+ assert(serd_sink_write_end(iface, lit));
static const uint8_t buf[] = {0xEF, 0xBF, 0xBD, 0};
const SerdStringView buf_view = {(const char*)buf, 3};
@@ -478,8 +476,7 @@ test_writer(const char* const path)
// Write 3 invalid statements (should write nothing)
const SerdNode* junk[][3] = {{s, o, o}, {o, p, o}, {s, o, p}};
for (size_t i = 0; i < sizeof(junk) / (sizeof(SerdNode*) * 3); ++i) {
- assert(serd_writer_write_statement(
- writer, 0, NULL, junk[i][0], junk[i][1], junk[i][2]));
+ assert(serd_sink_write(iface, 0, junk[i][0], junk[i][1], junk[i][2], NULL));
}
const SerdStringView urn_Type = serd_string("urn:Type");
@@ -490,8 +487,8 @@ test_writer(const char* const path)
const SerdNode* good[][3] = {{s, p, o}, {s, p, t}, {s, p, l}};
for (size_t i = 0; i < sizeof(good) / (sizeof(SerdNode*) * 3); ++i) {
- assert(!serd_writer_write_statement(
- writer, 0, NULL, good[i][0], good[i][1], good[i][2]));
+ assert(
+ !serd_sink_write(iface, 0, good[i][0], good[i][1], good[i][2], NULL));
}
// Write statements with bad UTF-8 (should be replaced)
@@ -499,16 +496,15 @@ test_writer(const char* const path)
const SerdStringView bad_view = {(const char*)bad_str, 4};
SerdNode* bad_lit = serd_new_string(bad_view);
SerdNode* bad_uri = serd_new_uri(bad_view);
- assert(!serd_writer_write_statement(writer, 0, NULL, s, p, bad_lit));
- assert(!serd_writer_write_statement(writer, 0, NULL, s, p, bad_uri));
-
+ assert(!serd_sink_write(iface, 0, s, p, bad_lit, 0));
+ assert(!serd_sink_write(iface, 0, s, p, bad_uri, 0));
serd_node_free(bad_uri);
serd_node_free(bad_lit);
// Write 1 valid statement
- serd_node_free(o);
- o = serd_new_string(serd_string("hello"));
- assert(!serd_writer_write_statement(writer, 0, NULL, s, p, o));
+ SerdNode* const hello = serd_new_string(serd_string("hello"));
+ assert(!serd_sink_write(iface, 0, s, p, hello, 0));
+ serd_node_free(hello);
serd_writer_free(writer);
serd_node_free(lit);
@@ -541,17 +537,16 @@ test_writer(const char* const path)
static void
test_reader(const char* path)
{
- ReaderTest* rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
- SerdReader* reader = serd_reader_new(SERD_TURTLE,
- rt,
- free,
- test_base_sink,
- test_prefix_sink,
- test_statement_sink,
- test_end_sink);
-
+ ReaderTest* rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
+ SerdSink* const sink = serd_sink_new(rt, NULL);
+ SerdReader* reader = serd_reader_new(SERD_TURTLE, sink);
+ assert(sink);
assert(reader);
- assert(serd_reader_handle(reader) == rt);
+
+ serd_sink_set_base_func(sink, test_base_sink);
+ serd_sink_set_prefix_func(sink, test_prefix_sink);
+ serd_sink_set_statement_func(sink, test_statement_sink);
+ serd_sink_set_end_func(sink, test_end_sink);
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
@@ -615,6 +610,8 @@ test_reader(const char* path)
}
serd_reader_free(reader);
+ serd_sink_free(sink);
+ free(rt);
}
int
diff --git a/test/test_sink.c b/test/test_sink.c
new file mode 100644
index 00000000..944dea1a
--- /dev/null
+++ b/test/test_sink.c
@@ -0,0 +1,151 @@
+// Copyright 2019-2020 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
+
+#undef NDEBUG
+
+#include "serd/env.h"
+#include "serd/node.h"
+#include "serd/sink.h"
+#include "serd/statement.h"
+#include "serd/status.h"
+#include "serd/string_view.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define NS_EG "http://example.org/"
+
+typedef struct {
+ const SerdNode* last_base;
+ const SerdNode* last_name;
+ const SerdNode* last_namespace;
+ const SerdNode* last_end;
+ const SerdNode* last_subject;
+ const SerdNode* last_predicate;
+ const SerdNode* last_object;
+ const SerdNode* last_graph;
+ SerdStatus return_status;
+} State;
+
+static SerdStatus
+on_base(void* handle, const SerdNode* uri)
+{
+ State* state = (State*)handle;
+
+ state->last_base = uri;
+ return state->return_status;
+}
+
+static SerdStatus
+on_prefix(void* handle, const SerdNode* name, const SerdNode* uri)
+{
+ State* state = (State*)handle;
+
+ state->last_name = name;
+ state->last_namespace = uri;
+ return state->return_status;
+}
+
+static SerdStatus
+on_statement(void* handle,
+ SerdStatementFlags flags,
+ const SerdNode* const graph,
+ const SerdNode* const subject,
+ const SerdNode* const predicate,
+ const SerdNode* const object)
+{
+ (void)flags;
+
+ State* state = (State*)handle;
+
+ state->last_subject = subject;
+ state->last_predicate = predicate;
+ state->last_object = object;
+ state->last_graph = graph;
+
+ return state->return_status;
+}
+
+static SerdStatus
+on_end(void* handle, const SerdNode* node)
+{
+ State* state = (State*)handle;
+
+ state->last_end = node;
+ return state->return_status;
+}
+
+static void
+test_callbacks(void)
+{
+ SerdNode* const base = serd_new_uri(serd_string(NS_EG));
+ SerdNode* const name = serd_new_string(serd_string("eg"));
+ SerdNode* const uri = serd_new_uri(serd_string(NS_EG "uri"));
+ SerdNode* const blank = serd_new_blank(serd_string("b1"));
+ SerdEnv* env = serd_env_new(serd_node_string_view(base));
+ State state = {0, 0, 0, 0, 0, 0, 0, 0, SERD_SUCCESS};
+
+ // Call functions on a sink with no functions set
+
+ SerdSink* null_sink = serd_sink_new(&state, NULL);
+ assert(!serd_sink_write_base(null_sink, base));
+ assert(!serd_sink_write_prefix(null_sink, name, uri));
+ assert(!serd_sink_write(null_sink, 0, base, uri, blank, NULL));
+ assert(!serd_sink_write_end(null_sink, blank));
+ 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);
+
+ assert(!serd_sink_write_base(sink, base));
+ assert(serd_node_equals(state.last_base, base));
+
+ assert(!serd_sink_write_prefix(sink, name, uri));
+ assert(serd_node_equals(state.last_name, name));
+ assert(serd_node_equals(state.last_namespace, uri));
+
+ assert(!serd_sink_write(sink, 0, base, uri, blank, NULL));
+ assert(serd_node_equals(state.last_subject, base));
+ assert(serd_node_equals(state.last_predicate, uri));
+ assert(serd_node_equals(state.last_object, blank));
+ assert(!state.last_graph);
+
+ assert(!serd_sink_write_end(sink, blank));
+ assert(serd_node_equals(state.last_end, blank));
+
+ serd_sink_free(sink);
+ serd_env_free(env);
+ serd_node_free(blank);
+ serd_node_free(uri);
+ serd_node_free(name);
+ serd_node_free(base);
+}
+
+static void
+test_free(void)
+{
+ // Free of null should (as always) not crash
+ serd_sink_free(NULL);
+
+ // 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);
+
+ // Free the sink, which should free the data (rely on valgrind or sanitizers)
+ serd_sink_free(sink);
+}
+
+int
+main(void)
+{
+ test_callbacks();
+ test_free();
+ return 0;
+}
diff --git a/test/test_writer.c b/test/test_writer.c
index 0c78495e..5a4e5ab0 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -7,6 +7,7 @@
#include "serd/env.h"
#include "serd/memory.h"
#include "serd/node.h"
+#include "serd/sink.h"
#include "serd/statement.h"
#include "serd/status.h"
#include "serd/string_view.h"
@@ -31,7 +32,8 @@ test_write_bad_prefix(void)
SerdNode* name = serd_new_string(serd_string("eg"));
SerdNode* uri = serd_new_uri(serd_string("rel"));
- assert(serd_writer_set_prefix(writer, name, uri) == SERD_BAD_ARG);
+ assert(serd_sink_write_prefix(serd_writer_sink(writer), name, uri) ==
+ SERD_BAD_ARG);
char* const out = serd_buffer_sink_finish(&buffer);
@@ -58,7 +60,7 @@ test_write_long_literal(void)
SerdNode* p = serd_new_uri(serd_string("http://example.org/p"));
SerdNode* o = serd_new_string(serd_string("hello \"\"\"world\"\"\"!"));
- assert(!serd_writer_write_statement(writer, 0, NULL, s, p, o));
+ assert(!serd_sink_write(serd_writer_sink(writer), 0, s, p, o, NULL));
serd_node_free(o);
serd_node_free(p);
@@ -95,11 +97,13 @@ test_writer_cleanup(void)
SerdEnv* env = serd_env_new(serd_empty_string());
SerdWriter* writer = serd_writer_new(SERD_TURTLE, 0U, env, null_sink, NULL);
+ const SerdSink* sink = serd_writer_sink(writer);
+
SerdNode* s = serd_new_uri(serd_string("http://example.org/s"));
SerdNode* p = serd_new_uri(serd_string("http://example.org/p"));
SerdNode* o = serd_new_blank(serd_string("start"));
- st = serd_writer_write_statement(writer, SERD_ANON_O_BEGIN, NULL, s, p, o);
+ st = serd_sink_write(sink, SERD_ANON_O_BEGIN, s, p, o, NULL);
assert(!st);
// Write the start of several nested anonymous objects
@@ -109,8 +113,7 @@ test_writer_cleanup(void)
SerdNode* next_o = serd_new_blank(serd_string(buf));
- st = serd_writer_write_statement(
- writer, SERD_ANON_O_BEGIN, NULL, o, p, next_o);
+ st = serd_sink_write(sink, SERD_ANON_O_BEGIN, o, p, next_o, NULL);
serd_node_free(o);
o = next_o;
@@ -121,7 +124,9 @@ test_writer_cleanup(void)
assert(!(st = serd_writer_set_base_uri(writer, NULL)));
// Set the base to an empty URI
- assert(!(st = serd_writer_set_base_uri(writer, NULL)));
+ SerdNode* empty_uri = serd_new_uri(serd_string(""));
+ assert(!(st = serd_sink_write_base(sink, empty_uri)));
+ serd_node_free(empty_uri);
// Free (which could leak if the writer doesn't clean up the stack properly)
serd_node_free(o);
@@ -144,6 +149,8 @@ test_strict_write(void)
assert(writer);
+ const SerdSink* const sink = serd_writer_sink(writer);
+
const uint8_t bad_str[] = {0xFF, 0x90, 'h', 'i', 0};
SerdNode* s = serd_new_uri(serd_string("http://example.org/s"));
@@ -152,11 +159,8 @@ test_strict_write(void)
SerdNode* bad_lit = serd_new_string(serd_string((const char*)bad_str));
SerdNode* bad_uri = serd_new_uri(serd_string((const char*)bad_str));
- assert(serd_writer_write_statement(writer, 0, NULL, s, p, bad_lit) ==
- SERD_BAD_TEXT);
-
- assert(serd_writer_write_statement(writer, 0, NULL, s, p, bad_uri) ==
- SERD_BAD_TEXT);
+ assert(serd_sink_write(sink, 0, s, p, bad_lit, NULL) == SERD_BAD_TEXT);
+ assert(serd_sink_write(sink, 0, s, p, bad_uri, NULL) == SERD_BAD_TEXT);
serd_node_free(bad_uri);
serd_node_free(bad_lit);
@@ -194,7 +198,10 @@ test_write_error(void)
writer =
serd_writer_new(SERD_TURTLE, (SerdWriterFlags)0, env, error_sink, NULL);
assert(writer);
- st = serd_writer_write_statement(writer, 0U, NULL, u, u, u);
+
+ const SerdSink* const sink = serd_writer_sink(writer);
+
+ st = serd_sink_write(sink, 0U, u, u, u, NULL);
assert(st == SERD_BAD_WRITE);
serd_writer_free(writer);