aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/serd/serd.h140
-rw-r--r--meson.build1
-rw-r--r--src/n3.c15
-rw-r--r--src/reader.c51
-rw-r--r--src/reader.h37
-rw-r--r--src/serdi.c8
-rw-r--r--src/sink.c112
-rw-r--r--src/sink.h34
-rw-r--r--src/writer.c23
-rw-r--r--test/meson.build1
-rw-r--r--test/test_free_null.c1
-rw-r--r--test/test_read_chunk.c11
-rw-r--r--test/test_reader_writer.c65
-rw-r--r--test/test_sink.c159
-rw-r--r--test/test_writer.c5
15 files changed, 500 insertions, 163 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 8b51e947..6b903b58 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -757,6 +757,90 @@ typedef SerdStatus (*SerdEndFunc)(void* SERD_NULLABLE handle,
/**
@}
+ @defgroup serd_sink Sink
+ @{
+*/
+
+/// 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);
+
+/**
+ @}
@defgroup serd_env Environment
@{
*/
@@ -847,13 +931,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.
@@ -878,11 +956,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.
@@ -1031,10 +1104,10 @@ 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);
/**
A convenience sink function for writing to a string.
@@ -1110,41 +1183,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 SerdPrefixSink.
-*/
-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 SerdStatementSink.
-*/
-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 SerdEndSink.
-*/
-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 7073a76c..0e1e9471 100644
--- a/meson.build
+++ b/meson.build
@@ -96,6 +96,7 @@ sources = [
'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 9fb256af..8e599458 100644
--- a/src/n3.c
+++ b/src/n3.c
@@ -1100,9 +1100,7 @@ read_anon(SerdReader* const reader,
return r_err(reader, SERD_ERR_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;
}
return (eat_byte_check(reader, ']') == ']') ? SERD_SUCCESS
@@ -1449,9 +1447,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);
@@ -1489,10 +1485,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);
@@ -1500,7 +1494,6 @@ read_prefixID(SerdReader* const reader, const bool sparql, const bool token)
read_ws_star(reader);
st = eat_byte_check(reader, '.') ? SERD_SUCCESS : SERD_ERR_BAD_SYNTAX;
}
-
return st;
}
diff --git a/src/reader.c b/src/reader.c
index 6bd458d4..5989ab93 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -153,14 +153,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;
@@ -180,26 +178,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);
@@ -240,18 +228,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 60a52e9d..d5c80373 100644
--- a/src/reader.h
+++ b/src/reader.h
@@ -57,27 +57,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 a62083af..0141b444 100644
--- a/src/serdi.c
+++ b/src/serdi.c
@@ -331,13 +331,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..959a92b3
--- /dev/null
+++ b/src/sink.c
@@ -0,0 +1,112 @@
+/*
+ Copyright 2011-2020 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 "sink.h"
+
+#include "serd/serd.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..e0f99a53
--- /dev/null
+++ b/src/sink.h
@@ -0,0 +1,34 @@
+/*
+ Copyright 2011-2020 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.
+*/
+
+#ifndef SERD_SINK_H
+#define SERD_SINK_H
+
+#include "serd/serd.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_SINK_H
diff --git a/src/writer.c b/src/writer.c
index 7c3cb563..a2ba0482 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -18,6 +18,7 @@
#include "env.h"
#include "node.h"
#include "serd_internal.h"
+#include "sink.h"
#include "stack.h"
#include "string_utils.h"
#include "uri_utils.h"
@@ -92,6 +93,7 @@ static const SepRule rules[] = {{NULL, 0, 0, 0, 0},
{"\n", 1, 0, 1, 0}};
struct SerdWriterImpl {
+ SerdSink iface;
SerdSyntax syntax;
SerdWriterFlags flags;
SerdEnv* env;
@@ -113,6 +115,11 @@ struct SerdWriterImpl {
typedef enum { WRITE_STRING, WRITE_LONG_STRING } TextContext;
+static SerdStatus
+serd_writer_set_prefix(SerdWriter* writer,
+ const SerdNode* name,
+ const SerdNode* uri);
+
static bool
write_node(SerdWriter* writer,
const SerdNode* node,
@@ -767,7 +774,7 @@ write_list_obj(SerdWriter* const writer,
return false;
}
-SerdStatus
+static SerdStatus
serd_writer_write_statement(SerdWriter* const writer,
const SerdStatementFlags flags,
const SerdNode* const graph,
@@ -897,7 +904,7 @@ serd_writer_write_statement(SerdWriter* const writer,
return SERD_SUCCESS;
}
-SerdStatus
+static SerdStatus
serd_writer_end_anon(SerdWriter* writer, const SerdNode* node)
{
if (writer->syntax == SERD_NTRIPLES || writer->syntax == SERD_NQUADS) {
@@ -958,6 +965,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;
}
@@ -1071,10 +1084,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 308803bb..794dec2c 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -10,6 +10,7 @@ unit_tests = [
'node',
'read_chunk',
'reader_writer',
+ 'sink',
'string',
'uri',
'writer',
diff --git a/test/test_free_null.c b/test/test_free_null.c
index 99e0be1d..af1ae149 100644
--- a/test/test_free_null.c
+++ b/test/test_free_null.c
@@ -26,6 +26,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_read_chunk.c b/test/test_read_chunk.c
index 009cbd14..8dd176e7 100644
--- a/test/test_read_chunk.c
+++ b/test/test_read_chunk.c
@@ -95,8 +95,14 @@ main(void)
fseek(file, 0, SEEK_SET);
- SerdReader* reader = serd_reader_new(
- SERD_TURTLE, NULL, NULL, on_base, on_prefix, on_statement, on_end);
+ 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);
+
+ SerdReader* reader = serd_reader_new(SERD_TURTLE, sink);
+ assert(reader);
assert(reader);
assert(!serd_reader_start_stream(reader, file, NULL, true));
@@ -112,6 +118,7 @@ main(void)
assert(!serd_reader_end_stream(reader));
serd_reader_free(reader);
+ serd_sink_free(sink);
fclose(file);
return 0;
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index 76ba87b7..ed1c87cb 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -96,15 +96,16 @@ eof_test_error(void* stream)
static void
test_read_chunks(void)
{
- ReaderTest* const rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
- FILE* const f = tmpfile();
- static const char null = 0;
- SerdReader* const reader =
- serd_reader_new(SERD_TURTLE, rt, free, NULL, NULL, test_sink, NULL);
+ ReaderTest* const rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
+ FILE* const f = tmpfile();
+ static const char null = 0;
+ 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);
assert(f);
+ serd_sink_set_statement_func(sink, test_sink);
SerdStatus st = serd_reader_start_stream(reader, f, NULL, false);
assert(st == SERD_SUCCESS);
@@ -148,18 +149,22 @@ test_read_chunks(void)
assert(rt->n_statements == 2);
serd_reader_free(reader);
+ serd_sink_free(sink);
fclose(f);
+ free(rt);
}
static void
test_read_string(void)
{
- ReaderTest* rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
- SerdReader* reader =
- serd_reader_new(SERD_TURTLE, rt, free, NULL, NULL, test_sink, NULL);
+ ReaderTest* rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
+ 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_statement_func(sink, test_sink);
// Test reading a string that ends exactly at the end of input (no newline)
const SerdStatus st =
@@ -171,6 +176,8 @@ test_read_string(void)
assert(rt->n_statements == 1);
serd_reader_free(reader);
+ serd_sink_free(sink);
+ free(rt);
}
static void
@@ -189,10 +196,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};
@@ -204,8 +211,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));
}
static const SerdStringView empty = SERD_EMPTY_STRING();
@@ -217,8 +223,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)
@@ -226,16 +232,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);
@@ -268,11 +273,13 @@ 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, NULL, NULL, test_sink, NULL);
+ 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_statement_func(sink, test_sink);
SerdNode* g = serd_new_uri(SERD_STRING("http://example.org/"));
serd_reader_set_default_graph(reader, g);
@@ -336,6 +343,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..982fc1c9
--- /dev/null
+++ b/test/test_sink.c
@@ -0,0 +1,159 @@
+/*
+ Copyright 2019-2020 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.
+*/
+
+#undef NDEBUG
+
+#include "serd/serd.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 756535ad..190995b6 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -34,7 +34,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_ERR_BAD_ARG);
+ assert(serd_sink_write_prefix(serd_writer_sink(writer), name, uri) ==
+ SERD_ERR_BAD_ARG);
char* const out = serd_buffer_sink_finish(&buffer);
@@ -63,7 +64,7 @@ test_write_long_literal(void)
SERD_EMPTY_STRING(),
SERD_EMPTY_STRING());
- 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);