diff options
author | David Robillard <d@drobilla.net> | 2023-12-02 01:20:48 -0500 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2023-12-02 18:49:07 -0500 |
commit | 930f28478ca2573b7f7baf29a57a03cfa95a841f (patch) | |
tree | ccd9fca0955c8c92e6f3d3cacd032024c1c3d4b7 | |
parent | 06bc73c6fdf986eb5d13943b497992a947661bb1 (diff) | |
download | serd-930f28478ca2573b7f7baf29a57a03cfa95a841f.tar.gz serd-930f28478ca2573b7f7baf29a57a03cfa95a841f.tar.bz2 serd-930f28478ca2573b7f7baf29a57a03cfa95a841f.zip |
Add a set of limits to the world
The idea here is to remove the burden of passing things around like stack
sizes (where most users don't care and will be happy with a reasonably large
default) and keeping the call sites to things like serd_reader_new() clean.
The cost is a bit more state, so it's both more powerful and more potentially
flaky, since changing the limits has action at a distance that isn't clear from
the call site. I think it's worth it for the cleaner API in the common case,
and the much better forward compatibility.
-rw-r--r-- | include/serd/reader.h | 3 | ||||
-rw-r--r-- | include/serd/world.h | 29 | ||||
-rw-r--r-- | src/reader.c | 4 | ||||
-rw-r--r-- | src/serdi.c | 5 | ||||
-rw-r--r-- | src/world.c | 19 | ||||
-rw-r--r-- | src/world.h | 1 | ||||
-rw-r--r-- | test/test_overflow.c | 6 | ||||
-rw-r--r-- | test/test_reader_writer.c | 22 |
8 files changed, 74 insertions, 15 deletions
diff --git a/include/serd/reader.h b/include/serd/reader.h index cc19a9b8..a2b2698d 100644 --- a/include/serd/reader.h +++ b/include/serd/reader.h @@ -31,8 +31,7 @@ typedef struct SerdReaderImpl SerdReader; SERD_API SerdReader* SERD_ALLOCATED serd_reader_new(SerdWorld* SERD_NONNULL world, SerdSyntax syntax, - const SerdSink* SERD_NONNULL sink, - size_t stack_size); + const SerdSink* SERD_NONNULL sink); /** Enable or disable strict parsing. diff --git a/include/serd/world.h b/include/serd/world.h index abf2999c..fe381628 100644 --- a/include/serd/world.h +++ b/include/serd/world.h @@ -7,6 +7,9 @@ #include "serd/attributes.h" #include "serd/error.h" #include "serd/node.h" +#include "serd/status.h" + +#include <stddef.h> SERD_BEGIN_DECLS @@ -19,6 +22,11 @@ SERD_BEGIN_DECLS /// Global library state typedef struct SerdWorldImpl SerdWorld; +/// Resource limits to control allocation +typedef struct { + size_t reader_stack_size; +} SerdLimits; + /** Create a new Serd World. @@ -33,6 +41,27 @@ SERD_API void serd_world_free(SerdWorld* SERD_NULLABLE world); /** + Return the current resource limits. + + These determine how much memory is allocated for reading and writing (where + the required stack space depends on the input data. The defaults use about + a megabyte and over 100 levels of nesting, which is more than enough for + most data. +*/ +SERD_API SerdLimits +serd_world_limits(const SerdWorld* SERD_NONNULL world); + +/** + Set the current resource limits. + + This updates the "current" limits, that is, those that will be used after + this call. It can be used to configure allocation sizes before calling some + other function like serd_reader_new() that uses the current limits. +*/ +SERD_API SerdStatus +serd_world_set_limits(SerdWorld* SERD_NONNULL world, SerdLimits limits); + +/** Return a unique blank node. The returned node is valid only until the next time serd_world_get_blank() diff --git a/src/reader.c b/src/reader.c index 61da89ff..4702131e 100644 --- a/src/reader.c +++ b/src/reader.c @@ -146,9 +146,9 @@ serd_reader_read_document(SerdReader* const reader) SerdReader* serd_reader_new(SerdWorld* const world, const SerdSyntax syntax, - const SerdSink* const sink, - const size_t stack_size) + const SerdSink* const sink) { + const size_t stack_size = world->limits.reader_stack_size; if (stack_size < 3 * sizeof(SerdNode) + 192 + serd_node_align) { return NULL; } diff --git a/src/serdi.c b/src/serdi.c index 0610069d..105bda46 100644 --- a/src/serdi.c +++ b/src/serdi.c @@ -245,8 +245,11 @@ main(int argc, char** argv) SerdWriter* const writer = serd_writer_new( world, output_syntax, writer_flags, env, (SerdWriteFunc)fwrite, out_fd); + const SerdLimits limits = {stack_size}; + serd_world_set_limits(world, limits); + SerdReader* const reader = - serd_reader_new(world, input_syntax, serd_writer_sink(writer), stack_size); + serd_reader_new(world, input_syntax, serd_writer_sink(writer)); serd_reader_set_strict(reader, !lax); if (quiet) { diff --git a/src/world.c b/src/world.c index 21cdffab..7fb5eea2 100644 --- a/src/world.c +++ b/src/world.c @@ -16,6 +16,7 @@ # include <fcntl.h> #endif +#include <assert.h> #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -102,7 +103,8 @@ serd_world_new(void) return NULL; } - world->blank_node = blank_node; + world->limits.reader_stack_size = 1048576U; + world->blank_node = blank_node; return world; } @@ -116,6 +118,21 @@ serd_world_free(SerdWorld* const world) } } +SerdLimits +serd_world_limits(const SerdWorld* const world) +{ + assert(world); + return world->limits; +} + +SerdStatus +serd_world_set_limits(SerdWorld* const world, const SerdLimits limits) +{ + assert(world); + world->limits = limits; + return SERD_SUCCESS; +} + const SerdNode* serd_world_get_blank(SerdWorld* const world) { diff --git a/src/world.h b/src/world.h index b330d4e4..96252a7a 100644 --- a/src/world.h +++ b/src/world.h @@ -13,6 +13,7 @@ #include <stdio.h> struct SerdWorldImpl { + SerdLimits limits; SerdErrorFunc error_func; void* error_handle; uint32_t next_blank_id; diff --git a/test/test_overflow.c b/test/test_overflow.c index 62154958..ac47d239 100644 --- a/test/test_overflow.c +++ b/test/test_overflow.c @@ -17,8 +17,12 @@ test_size(SerdWorld* const world, const SerdSyntax syntax, const size_t stack_size) { + SerdLimits limits = serd_world_limits(world); + limits.reader_stack_size = stack_size; + serd_world_set_limits(world, limits); + SerdSink* sink = serd_sink_new(NULL, NULL, NULL); - SerdReader* const reader = serd_reader_new(world, syntax, sink, stack_size); + SerdReader* const reader = serd_reader_new(world, syntax, sink); if (!reader) { return SERD_BAD_STACK; } diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c index 3cacd4be..fd9c0d28 100644 --- a/test/test_reader_writer.c +++ b/test/test_reader_writer.c @@ -162,7 +162,7 @@ test_read_nquads_chunks(const char* const path) SerdSink* const sink = serd_sink_new(&rt, test_sink, NULL); assert(sink); - SerdReader* const reader = serd_reader_new(world, SERD_NQUADS, sink, 4096); + SerdReader* const reader = serd_reader_new(world, SERD_NQUADS, sink); assert(reader); SerdStatus st = serd_reader_start_stream( @@ -241,7 +241,7 @@ test_read_turtle_chunks(const char* const path) SerdSink* sink = serd_sink_new(&rt, test_sink, NULL); assert(sink); - SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096); + SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink); assert(reader); SerdStatus st = serd_reader_start_stream( @@ -321,7 +321,7 @@ test_read_string(void) SerdSink* sink = serd_sink_new(&rt, test_sink, NULL); assert(sink); - SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096); + SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink); assert(reader); // Test reading a string that ends exactly at the end of input (no newline) @@ -386,8 +386,8 @@ test_write_errors(void) SerdWriter* const writer = serd_writer_new(world, syntax, 0U, env, faulty_sink, &ctx); - const SerdSink* const sink = serd_writer_sink(writer); - SerdReader* const reader = serd_reader_new(world, SERD_TRIG, sink, 4096U); + const SerdSink* const sink = serd_writer_sink(writer); + SerdReader* const reader = serd_reader_new(world, SERD_TRIG, sink); SerdStatus st = serd_reader_start_string(reader, doc_string, NULL); assert(!st); @@ -513,9 +513,15 @@ test_reader(const char* path) assert(sink); // Test that too little stack space fails gracefully - assert(!serd_reader_new(world, SERD_TURTLE, sink, 32)); - - SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink, 4096); + const SerdLimits old_limits = serd_world_limits(world); + SerdLimits limits = old_limits; + limits.reader_stack_size = 32U; + serd_world_set_limits(world, limits); + assert(!serd_reader_new(world, SERD_TURTLE, sink)); + + // Restore original limits and successfully create reader + serd_world_set_limits(world, old_limits); + SerdReader* reader = serd_reader_new(world, SERD_TURTLE, sink); assert(reader); assert(serd_reader_read_chunk(reader) == SERD_FAILURE); |