diff options
-rw-r--r-- | .clang-format | 3 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | include/serd/serd.h | 72 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/attributes.h | 4 | ||||
-rw-r--r-- | src/reader.c | 17 | ||||
-rw-r--r-- | src/reader.h | 1 | ||||
-rw-r--r-- | src/serd_internal.h | 14 | ||||
-rw-r--r-- | src/serdi.c | 17 | ||||
-rw-r--r-- | src/system.h | 6 | ||||
-rw-r--r-- | src/world.c | 40 | ||||
-rw-r--r-- | src/world.h | 27 | ||||
-rw-r--r-- | src/writer.c | 18 | ||||
-rw-r--r-- | test/test_free_null.c | 1 | ||||
-rw-r--r-- | test/test_node.c | 6 | ||||
-rw-r--r-- | test/test_read_chunk.c | 6 | ||||
-rw-r--r-- | test/test_reader_writer.c | 20 |
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 ... @@ -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 |