aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format3
-rw-r--r--NEWS1
-rw-r--r--include/serd/serd.h72
-rw-r--r--meson.build1
-rw-r--r--src/attributes.h4
-rw-r--r--src/reader.c17
-rw-r--r--src/reader.h1
-rw-r--r--src/serd_internal.h14
-rw-r--r--src/serdi.c17
-rw-r--r--src/system.h6
-rw-r--r--src/world.c40
-rw-r--r--src/world.h27
-rw-r--r--src/writer.c18
-rw-r--r--test/test_free_null.c1
-rw-r--r--test/test_node.c6
-rw-r--r--test/test_read_chunk.c6
-rw-r--r--test/test_reader_writer.c20
17 files changed, 182 insertions, 72 deletions
diff --git a/.clang-format b/.clang-format
index ced919a1..e0615abf 100644
--- a/.clang-format
+++ b/.clang-format
@@ -24,6 +24,9 @@ StatementMacros:
- SERD_CONST_API
- SERD_CONST_FUNC
- SERD_DEPRECATED_BY
+ - SERD_I_MALLOC_FUNC
+ - SERD_MALLOC_API
+ - SERD_MALLOC_FUNC
- SERD_PURE_API
- SERD_PURE_FUNC
...
diff --git a/NEWS b/NEWS
index 2b792055..99bbc724 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
serd (1.0.1) unstable;
* Add SerdBuffer for mutable buffers to keep SerdChunk const-correct
+ * Add SerdWorld for shared library state
* Add support for xsd:float and xsd:double literals
* Bring read/write interface closer to C standard
* Make nodes opaque
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 2f349e90..d57f1470 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -38,9 +38,11 @@
#ifdef __GNUC__
# define SERD_PURE_FUNC __attribute__((pure))
# define SERD_CONST_FUNC __attribute__((const))
+# define SERD_MALLOC_FUNC __attribute__((malloc))
#else
# define SERD_PURE_FUNC
# define SERD_CONST_FUNC
+# define SERD_MALLOC_FUNC
#endif
#if defined(__clang__) && __clang_major__ >= 7
@@ -61,6 +63,10 @@
SERD_API \
SERD_CONST_FUNC
+#define SERD_MALLOC_API \
+ SERD_API \
+ SERD_MALLOC_FUNC
+
#ifdef __cplusplus
extern "C" {
# if defined(__GNUC__)
@@ -75,6 +81,9 @@ extern "C" {
@{
*/
+/// Global library state
+typedef struct SerdWorldImpl SerdWorld;
+
/// Lexical environment for relative URIs or CURIEs (base URI and namespaces)
typedef struct SerdEnvImpl SerdEnv;
@@ -842,6 +851,39 @@ typedef SerdStatus (*SerdEndFunc)(void* SERD_NULLABLE handle,
/**
@}
+ @defgroup serd_world World
+ @{
+*/
+
+/**
+ Create a new Serd World.
+
+ It is safe to use multiple worlds in one process, though no objects can be
+ shared between worlds.
+*/
+SERD_MALLOC_API
+SerdWorld* SERD_ALLOCATED
+serd_world_new(void);
+
+/// Free `world`
+SERD_API
+void
+serd_world_free(SerdWorld* SERD_NULLABLE world);
+
+/**
+ Set a function to be called when errors occur.
+
+ The `error_func` will be called with `handle` as its first argument. If
+ no error function is set, errors are printed to stderr.
+*/
+SERD_API
+void
+serd_world_set_error_func(SerdWorld* SERD_NONNULL world,
+ SerdErrorFunc SERD_NULLABLE error_func,
+ void* SERD_NULLABLE handle);
+
+/**
+ @}
@defgroup serd_env Environment
@{
*/
@@ -1011,7 +1053,8 @@ serd_sink_write_end(const SerdSink* SERD_NONNULL sink,
/// Create a new RDF reader
SERD_API
SerdReader* SERD_ALLOCATED
-serd_reader_new(SerdSyntax syntax,
+serd_reader_new(SerdWorld* SERD_NONNULL world,
+ SerdSyntax syntax,
const SerdSink* SERD_NONNULL sink,
size_t stack_size);
@@ -1027,18 +1070,6 @@ void
serd_reader_set_strict(SerdReader* SERD_NONNULL reader, bool strict);
/**
- Set a function to be called when errors occur during reading.
-
- The `error_func` will be called with `handle` as its first argument. If
- no error function is set, errors are printed to stderr in GCC style.
-*/
-SERD_API
-void
-serd_reader_set_error_sink(SerdReader* SERD_NONNULL reader,
- SerdErrorFunc SERD_NULLABLE error_func,
- void* SERD_NULLABLE error_handle);
-
-/**
Set a prefix to be added to all blank node identifiers.
This is useful when multiple files are to be parsed into the same output (a
@@ -1142,7 +1173,8 @@ serd_reader_free(SerdReader* SERD_NULLABLE reader);
/// Create a new RDF writer
SERD_API
SerdWriter* SERD_ALLOCATED
-serd_writer_new(SerdSyntax syntax,
+serd_writer_new(SerdWorld* SERD_NONNULL world,
+ SerdSyntax syntax,
SerdWriterFlags flags,
SerdEnv* SERD_NONNULL env,
SerdWriteFunc SERD_NONNULL ssink,
@@ -1189,18 +1221,6 @@ char* SERD_NULLABLE
serd_buffer_sink_finish(SerdBuffer* SERD_NONNULL stream);
/**
- Set a function to be called when errors occur during writing.
-
- The `error_func` will be called with `handle` as its first argument. If
- no error function is set, errors are printed to stderr.
-*/
-SERD_API
-void
-serd_writer_set_error_sink(SerdWriter* SERD_NONNULL writer,
- SerdErrorFunc SERD_NONNULL error_func,
- void* SERD_NULLABLE error_handle);
-
-/**
Set a prefix to be removed from matching blank node identifiers
This is the counterpart to serd_reader_add_blank_prefix() which can be used
diff --git a/meson.build b/meson.build
index 4dd4b6e5..6b7818ff 100644
--- a/meson.build
+++ b/meson.build
@@ -102,6 +102,7 @@ sources = [
'src/syntax.c',
'src/system.c',
'src/uri.c',
+ 'src/world.c',
'src/writer.c',
]
diff --git a/src/attributes.h b/src/attributes.h
index 8628a868..25e5b505 100644
--- a/src/attributes.h
+++ b/src/attributes.h
@@ -18,9 +18,9 @@
#define SERD_ATTRIBUTES_H
#ifdef __GNUC__
-# define SERD_MALLOC_FUNC __attribute__((malloc))
+# define SERD_I_MALLOC_FUNC __attribute__((malloc))
#else
-# define SERD_MALLOC_FUNC
+# define SERD_I_MALLOC_FUNC
#endif
#endif // SERD_ATTRIBUTES_H
diff --git a/src/reader.c b/src/reader.c
index f375be42..640fb12b 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -36,7 +36,7 @@ r_err(SerdReader* reader, SerdStatus st, const char* fmt, ...)
va_start(args, fmt);
const Cursor* const cur = &reader->source.cur;
const SerdError e = {st, cur->filename, cur->line, cur->col, fmt, &args};
- serd_error(reader->error_func, reader->error_handle, &e);
+ serd_error(reader->world, &e);
va_end(args);
return st;
}
@@ -154,10 +154,14 @@ serd_reader_read_document(SerdReader* reader)
}
SerdReader*
-serd_reader_new(SerdSyntax syntax, const SerdSink* sink, size_t stack_size)
+serd_reader_new(SerdWorld* const world,
+ const SerdSyntax syntax,
+ const SerdSink* const sink,
+ const size_t stack_size)
{
SerdReader* me = (SerdReader*)calloc(1, sizeof(SerdReader));
+ me->world = world;
me->sink = sink;
me->default_graph = NULL;
me->stack = serd_stack_new(stack_size);
@@ -179,15 +183,6 @@ serd_reader_set_strict(SerdReader* reader, bool strict)
}
void
-serd_reader_set_error_sink(SerdReader* reader,
- SerdErrorFunc error_func,
- void* error_handle)
-{
- reader->error_func = error_func;
- reader->error_handle = error_handle;
-}
-
-void
serd_reader_free(SerdReader* reader)
{
if (!reader) {
diff --git a/src/reader.h b/src/reader.h
index 8c8d45d5..ce377fd3 100644
--- a/src/reader.h
+++ b/src/reader.h
@@ -50,6 +50,7 @@ typedef struct {
} ReadContext;
struct SerdReaderImpl {
+ SerdWorld* world;
const SerdSink* sink;
SerdErrorFunc error_func;
void* error_handle;
diff --git a/src/serd_internal.h b/src/serd_internal.h
index e16a2c12..1bb2a3bc 100644
--- a/src/serd_internal.h
+++ b/src/serd_internal.h
@@ -17,6 +17,8 @@
#ifndef SERD_INTERNAL_H
#define SERD_INTERNAL_H
+#include "world.h"
+
#include "serd/serd.h"
#include <stdio.h>
@@ -33,12 +35,16 @@
/* Error reporting */
static inline void
-serd_error(SerdErrorFunc error_func, void* handle, const SerdError* e)
+serd_error(const SerdWorld* world, const SerdError* e)
{
- if (error_func) {
- error_func(handle, e);
+ if (world->error_func) {
+ world->error_func(world->error_handle, e);
} else {
- fprintf(stderr, "error: %s:%u:%u: ", e->filename, e->line, e->col);
+ if (e->filename) {
+ fprintf(stderr, "error: %s:%u:%u: ", e->filename, e->line, e->col);
+ } else {
+ fprintf(stderr, "error: ");
+ }
vfprintf(stderr, e->fmt, *e->args);
}
}
diff --git a/src/serdi.c b/src/serdi.c
index 628ae004..3f0185ec 100644
--- a/src/serdi.c
+++ b/src/serdi.c
@@ -80,7 +80,7 @@ missing_arg(const char* name, char opt)
}
static SerdStatus
-quiet_error_sink(void* handle, const SerdError* e)
+quiet_error_func(void* const handle, const SerdError* const e)
{
(void)handle;
(void)e;
@@ -247,19 +247,19 @@ main(int argc, char** argv)
base = serd_new_file_uri(SERD_MEASURE_STRING(input), SERD_EMPTY_STRING());
}
- FILE* const out_fd = stdout;
- SerdEnv* const env = serd_env_new(serd_node_string_view(base));
+ FILE* const out_fd = stdout;
+ SerdWorld* const world = serd_world_new();
+ SerdEnv* const env = serd_env_new(serd_node_string_view(base));
- SerdWriter* writer = serd_writer_new(
- output_syntax, writer_flags, env, (SerdWriteFunc)fwrite, out_fd);
+ SerdWriter* const writer = serd_writer_new(
+ world, output_syntax, writer_flags, env, (SerdWriteFunc)fwrite, out_fd);
SerdReader* const reader =
- serd_reader_new(input_syntax, serd_writer_sink(writer), stack_size);
+ serd_reader_new(world, input_syntax, serd_writer_sink(writer), stack_size);
serd_reader_set_strict(reader, !lax);
if (quiet) {
- serd_reader_set_error_sink(reader, quiet_error_sink, NULL);
- serd_writer_set_error_sink(writer, quiet_error_sink, NULL);
+ serd_world_set_error_func(world, quiet_error_func, NULL);
}
SerdNode* root = serd_new_uri(SERD_MEASURE_STRING(root_uri));
@@ -292,6 +292,7 @@ main(int argc, char** argv)
serd_writer_free(writer);
serd_env_free(env);
serd_node_free(base);
+ serd_world_free(world);
if (fclose(stdout)) {
perror("serdi: write error");
diff --git a/src/system.h b/src/system.h
index df5827de..66546d6e 100644
--- a/src/system.h
+++ b/src/system.h
@@ -27,11 +27,13 @@ FILE*
serd_fopen(const char* path, const char* mode);
/// Allocate a buffer aligned to `alignment` bytes
-SERD_MALLOC_FUNC void*
+SERD_I_MALLOC_FUNC
+void*
serd_malloc_aligned(size_t alignment, size_t size);
/// Allocate an aligned buffer for I/O
-SERD_MALLOC_FUNC void*
+SERD_I_MALLOC_FUNC
+void*
serd_allocate_buffer(size_t size);
/// Free a buffer allocated with an aligned allocation function
diff --git a/src/world.c b/src/world.c
new file mode 100644
index 00000000..6f5a26c1
--- /dev/null
+++ b/src/world.c
@@ -0,0 +1,40 @@
+/*
+ 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 "world.h"
+
+#include <stdlib.h>
+
+SerdWorld*
+serd_world_new(void)
+{
+ return (SerdWorld*)calloc(1, sizeof(SerdWorld));
+}
+
+void
+serd_world_free(SerdWorld* world)
+{
+ free(world);
+}
+
+void
+serd_world_set_error_func(SerdWorld* world,
+ SerdErrorFunc error_func,
+ void* handle)
+{
+ world->error_func = error_func;
+ world->error_handle = handle;
+}
diff --git a/src/world.h b/src/world.h
new file mode 100644
index 00000000..3d994df9
--- /dev/null
+++ b/src/world.h
@@ -0,0 +1,27 @@
+/*
+ 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_WORLD_H
+#define SERD_WORLD_H
+
+#include "serd/serd.h"
+
+struct SerdWorldImpl {
+ SerdErrorFunc error_func;
+ void* error_handle;
+};
+
+#endif // SERD_WORLD_H
diff --git a/src/writer.c b/src/writer.c
index 5a934e41..de0e80a7 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -93,6 +93,7 @@ static const SepRule rules[] = {{NULL, 0, 0, 0, 0},
{"\n", 1, 0, 1, 0}};
struct SerdWriterImpl {
+ SerdWorld* world;
SerdSink iface;
SerdSyntax syntax;
SerdWriterFlags flags;
@@ -149,8 +150,8 @@ w_err(SerdWriter* writer, SerdStatus st, const char* fmt, ...)
va_list args;
va_start(args, fmt);
- const SerdError e = {st, "", 0, 0, fmt, &args};
- serd_error(writer->error_func, writer->error_handle, &e);
+ const SerdError e = {st, NULL, 0, 0, fmt, &args};
+ serd_error(writer->world, &e);
va_end(args);
}
@@ -966,7 +967,8 @@ serd_writer_finish(SerdWriter* writer)
}
SerdWriter*
-serd_writer_new(SerdSyntax syntax,
+serd_writer_new(SerdWorld* world,
+ SerdSyntax syntax,
SerdWriterFlags flags,
SerdEnv* env,
SerdWriteFunc ssink,
@@ -975,6 +977,7 @@ serd_writer_new(SerdSyntax syntax,
const WriteContext context = WRITE_CONTEXT_NULL;
SerdWriter* writer = (SerdWriter*)calloc(1, sizeof(SerdWriter));
+ writer->world = world;
writer->syntax = syntax;
writer->flags = flags;
writer->env = env;
@@ -997,15 +1000,6 @@ serd_writer_new(SerdSyntax syntax,
}
void
-serd_writer_set_error_sink(SerdWriter* writer,
- SerdErrorFunc error_func,
- void* error_handle)
-{
- writer->error_func = error_func;
- writer->error_handle = error_handle;
-}
-
-void
serd_writer_chop_blank_prefix(SerdWriter* writer, const char* prefix)
{
free(writer->bprefix);
diff --git a/test/test_free_null.c b/test/test_free_null.c
index af1ae149..3977507c 100644
--- a/test/test_free_null.c
+++ b/test/test_free_null.c
@@ -25,6 +25,7 @@ main(void)
{
serd_free(NULL);
serd_node_free(NULL);
+ serd_world_free(NULL);
serd_env_free(NULL);
serd_sink_free(NULL);
serd_reader_free(NULL);
diff --git a/test/test_node.c b/test/test_node.c
index 80c45b10..fc01803d 100644
--- a/test/test_node.c
+++ b/test/test_node.c
@@ -60,6 +60,8 @@ check_get_boolean(const char* string,
assert(node);
assert(serd_get_boolean(node) == expected);
+
+ serd_node_free(node);
}
static void
@@ -113,6 +115,8 @@ check_get_double(const char* string,
const double value = serd_get_double(node);
assert(!memcmp(&value, &expected, sizeof(value)));
+
+ serd_node_free(node);
}
static void
@@ -167,6 +171,8 @@ check_get_float(const char* string,
const float value = serd_get_float(node);
assert(!memcmp(&value, &expected, sizeof(value)));
+
+ serd_node_free(node);
}
static void
diff --git a/test/test_read_chunk.c b/test/test_read_chunk.c
index 9a482c02..e15d3c50 100644
--- a/test/test_read_chunk.c
+++ b/test/test_read_chunk.c
@@ -79,13 +79,14 @@ on_end(void* handle, const SerdNode* node)
int
main(void)
{
- SerdSink* sink = serd_sink_new(NULL, NULL);
+ 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);
- SerdReader* reader = serd_reader_new(SERD_TURTLE, sink, 4096);
+ SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
assert(reader);
assert(!serd_reader_start_string(reader,
@@ -112,5 +113,6 @@ main(void)
serd_reader_free(reader);
serd_sink_free(sink);
+ serd_world_free(world);
return 0;
}
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index 036e49d7..48f20247 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -79,11 +79,12 @@ eof_test_error(void* stream)
static void
test_read_chunks(void)
{
+ SerdWorld* world = serd_world_new();
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, 4096);
+ SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
assert(reader);
assert(sink);
@@ -136,14 +137,16 @@ test_read_chunks(void)
serd_sink_free(sink);
fclose(f);
free(rt);
+ serd_world_free(world);
}
static void
test_read_string(void)
{
+ SerdWorld* world = serd_world_new();
ReaderTest* rt = (ReaderTest*)calloc(1, sizeof(ReaderTest));
SerdSink* sink = serd_sink_new(rt, NULL);
- SerdReader* reader = serd_reader_new(SERD_TURTLE, sink, 4096);
+ SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
assert(reader);
assert(sink);
@@ -163,6 +166,7 @@ test_read_string(void)
serd_reader_free(reader);
serd_sink_free(sink);
free(rt);
+ serd_world_free(world);
}
static void
@@ -172,8 +176,10 @@ test_writer(const char* const path)
SerdEnv* env = serd_env_new(SERD_EMPTY_STRING());
assert(fd);
+ SerdWorld* world = serd_world_new();
+
SerdWriter* writer =
- serd_writer_new(SERD_TURTLE, 0, env, (SerdWriteFunc)fwrite, fd);
+ serd_writer_new(world, SERD_TURTLE, 0, env, (SerdWriteFunc)fwrite, fd);
assert(writer);
serd_writer_chop_blank_prefix(writer, "tmp");
@@ -242,7 +248,8 @@ test_writer(const char* const path)
// Test buffer sink
SerdBuffer buffer = {NULL, 0};
- writer = serd_writer_new(SERD_TURTLE, 0, env, serd_buffer_sink, &buffer);
+ writer =
+ serd_writer_new(world, SERD_TURTLE, 0, env, serd_buffer_sink, &buffer);
SerdNode* const base =
serd_new_uri(SERD_STATIC_STRING("http://example.org/base"));
@@ -260,15 +267,17 @@ test_writer(const char* const path)
serd_node_free(s);
serd_env_free(env);
+ serd_world_free(world);
fclose(fd);
}
static void
test_reader(const char* path)
{
+ SerdWorld* world = serd_world_new();
ReaderTest rt = {0, NULL};
SerdSink* const sink = serd_sink_new(&rt, NULL);
- SerdReader* reader = serd_reader_new(SERD_TURTLE, sink, 4096);
+ SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096);
assert(reader);
assert(sink);
@@ -341,6 +350,7 @@ test_reader(const char* path)
serd_reader_free(reader);
serd_sink_free(sink);
+ serd_world_free(world);
}
int