aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-10-27 14:15:31 -0400
committerDavid Robillard <d@drobilla.net>2022-01-28 21:57:24 -0500
commit30487c277ac5d4be5786733ca7b98adb4c810ae9 (patch)
treef1b00a7725d74a594fcd91de2aea924485356528
parent56cceb103dc633d6af957472945e792187a5dd4e (diff)
downloadserd-30487c277ac5d4be5786733ca7b98adb4c810ae9.tar.gz
serd-30487c277ac5d4be5786733ca7b98adb4c810ae9.tar.bz2
serd-30487c277ac5d4be5786733ca7b98adb4c810ae9.zip
Add custom allocator support
-rw-r--r--include/serd/serd.h262
-rw-r--r--meson.build2
-rw-r--r--src/block_dumper.c9
-rw-r--r--src/block_dumper.h5
-rw-r--r--src/buffer.c14
-rw-r--r--src/byte_source.c51
-rw-r--r--src/byte_source.h5
-rw-r--r--src/canon.c51
-rw-r--r--src/caret.c34
-rw-r--r--src/cursor.c17
-rw-r--r--src/describe.c17
-rw-r--r--src/env.c100
-rw-r--r--src/filter.c56
-rw-r--r--src/inserter.c42
-rw-r--r--src/memory.c30
-rw-r--r--src/memory.h148
-rw-r--r--src/model.c101
-rw-r--r--src/model.h1
-rw-r--r--src/node.c125
-rw-r--r--src/node.h10
-rw-r--r--src/node_syntax.c127
-rw-r--r--src/nodes.c103
-rw-r--r--src/reader.c31
-rw-r--r--src/sink.c16
-rw-r--r--src/sink.h7
-rw-r--r--src/stack.h12
-rw-r--r--src/statement.c55
-rw-r--r--src/string.c5
-rw-r--r--src/system.c96
-rw-r--r--src/system.h23
-rw-r--r--src/uri.c35
-rw-r--r--src/world.c70
-rw-r--r--src/world.h2
-rw-r--r--src/writer.c76
-rw-r--r--test/.clang-tidy1
-rw-r--r--test/failing_allocator.c121
-rw-r--r--test/failing_allocator.h34
-rw-r--r--test/meson.build4
-rw-r--r--test/test_canon.c108
-rw-r--r--test/test_caret.c83
-rw-r--r--test/test_cursor.c8
-rw-r--r--test/test_env.c201
-rw-r--r--test/test_filter.c68
-rw-r--r--test/test_free_null.c8
-rw-r--r--test/test_log.c18
-rw-r--r--test/test_model.c87
-rw-r--r--test/test_node.c238
-rw-r--r--test/test_node_syntax.c143
-rw-r--r--test/test_nodes.c131
-rw-r--r--test/test_overflow.c8
-rw-r--r--test/test_read_chunk.c2
-rw-r--r--test/test_reader.c106
-rw-r--r--test/test_reader_writer.c8
-rw-r--r--test/test_sink.c39
-rw-r--r--test/test_statement.c164
-rw-r--r--test/test_string.c4
-rw-r--r--test/test_terse_write.c9
-rw-r--r--test/test_uri.c86
-rw-r--r--test/test_world.c26
-rw-r--r--test/test_writer.c133
-rw-r--r--tools/console.c20
-rw-r--r--tools/serd-filter.c48
62 files changed, 2729 insertions, 915 deletions
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 9e322c0d..2300a1a4 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -197,7 +197,102 @@ typedef struct {
/**
@}
+ @defgroup serd_memory Memory Management
+ @{
+*/
+
+struct SerdAllocatorImpl;
+
+/**
+ A memory allocator.
+
+ This object-like structure provides an interface like the standard C
+ functions malloc(), calloc(), realloc(), free(), and aligned_alloc(). It
+ contains function pointers that differ from their standard counterparts by
+ taking a context parameter (a pointer to this struct), which allows the user
+ to implement custom stateful allocators.
+*/
+typedef struct SerdAllocatorImpl SerdAllocator;
+
+/**
+ General malloc-like memory allocation function.
+
+ This works like the standard C malloc(), except has an additional handle
+ parameter for implementing stateful allocators without static data.
+*/
+typedef void* SERD_ALLOCATED (*SerdAllocatorMallocFunc)( //
+ SerdAllocator* SERD_NULLABLE allocator,
+ size_t size);
+
+/**
+ General calloc-like memory allocation function.
+
+ This works like the standard C calloc(), except has an additional handle
+ parameter for implementing stateful allocators without static data.
+*/
+typedef void* SERD_ALLOCATED (*SerdAllocatorCallocFunc)( //
+ SerdAllocator* SERD_NULLABLE allocator,
+ size_t nmemb,
+ size_t size);
+
+/**
+ General realloc-like memory reallocation function.
+
+ This works like the standard C remalloc(), except has an additional handle
+ parameter for implementing stateful allocators without static data.
+*/
+typedef void* SERD_ALLOCATED (*SerdAllocatorReallocFunc)( //
+ SerdAllocator* SERD_NULLABLE allocator,
+ void* SERD_NULLABLE ptr,
+ size_t size);
+
+/**
+ General free-like memory deallocation function.
+
+ This works like the standard C remalloc(), except has an additional handle
+ parameter for implementing stateful allocators without static data.
*/
+typedef void (*SerdAllocatorFreeFunc)( //
+ SerdAllocator* SERD_NULLABLE allocator,
+ void* SERD_NULLABLE ptr);
+
+/**
+ General aligned_alloc-like memory deallocation function.
+
+ This works like the standard C aligned_alloc(), except has an additional
+ handle parameter for implementing stateful allocators without static data.
+*/
+typedef void* SERD_ALLOCATED (*SerdAllocatorAlignedAllocFunc)( //
+ SerdAllocator* SERD_NULLABLE allocator,
+ size_t alignment,
+ size_t size);
+
+/**
+ General aligned memory deallocation function.
+
+ This works like the standard C free(), but must be used to free memory
+ allocated with the aligned_alloc() method of the allocator. This allows
+ portability to systems (like Windows) that can not use the same free function
+ in these cases.
+*/
+typedef void (*SerdAllocatorAlignedFreeFunc)( //
+ SerdAllocator* SERD_NULLABLE allocator,
+ void* SERD_NULLABLE ptr);
+
+/// Definition of SerdAllocator
+struct SerdAllocatorImpl {
+ SerdAllocatorMallocFunc SERD_ALLOCATED malloc;
+ SerdAllocatorCallocFunc SERD_ALLOCATED calloc;
+ SerdAllocatorReallocFunc SERD_ALLOCATED realloc;
+ SerdAllocatorFreeFunc SERD_ALLOCATED free;
+ SerdAllocatorAlignedAllocFunc SERD_ALLOCATED aligned_alloc;
+ SerdAllocatorAlignedFreeFunc SERD_ALLOCATED aligned_free;
+};
+
+/// Return the default allocator which simply uses the system allocator
+SERD_CONST_API
+SerdAllocator* SERD_NONNULL
+serd_default_allocator(void);
/**
Free memory allocated by Serd.
@@ -205,12 +300,15 @@ typedef struct {
This function exists because some systems require memory allocated by a
library to be freed by code in the same library. It is otherwise equivalent
to the standard C free() function.
+
+ This may be used to free memory allocated using serd_default_allocator().
*/
SERD_API
void
-serd_free(void* SERD_NULLABLE ptr);
+serd_free(SerdAllocator* SERD_NULLABLE allocator, void* SERD_NULLABLE ptr);
/**
+ @}
@defgroup serd_status Status Codes
@{
*/
@@ -283,11 +381,13 @@ serd_strerror(SerdStatus status);
directory separators. Null is returned on error, including if the path does
not exist.
- @return A new string that must be freed with serd_free(), or null.
+ @return A newly allocated string that must be freed with serd_free() using
+ the world allocator, or null.
*/
SERD_API
-char* SERD_NULLABLE
-serd_canonical_path(const char* SERD_NONNULL path);
+char* SERD_ALLOCATED
+serd_canonical_path(SerdAllocator* SERD_NULLABLE allocator,
+ const char* SERD_NONNULL path);
/**
Compare two strings ignoring case.
@@ -462,13 +562,16 @@ serd_parse_uri(const char* SERD_NONNULL string);
The returned path and `*hostname` must be freed with serd_free().
+ @param allocator Allocator for the returned string.
@param uri A file URI.
@param hostname If non-NULL, set to the hostname, if present.
- @return A filesystem path.
+
+ @return A newly allocated path string that must be freed with serd_free().
*/
SERD_API
char* SERD_NULLABLE
-serd_parse_file_uri(const char* SERD_NONNULL uri,
+serd_parse_file_uri(SerdAllocator* SERD_NULLABLE allocator,
+ const char* SERD_NONNULL uri,
char* SERD_NONNULL* SERD_NULLABLE hostname);
/**
@@ -856,7 +959,8 @@ serd_node_construct_base64(size_t buf_size,
@defgroup serd_node_allocation Dynamic Allocation
This is a convenient higher-level node construction API which allocates
- nodes on the heap. The returned nodes must be freed with serd_node_free().
+ nodes with an allocator. The returned nodes must be freed with
+ serd_node_free() using the same allocator.
Note that in most cases it is better to use a #SerdNodes instead of managing
individual node allocations.
@@ -875,10 +979,11 @@ serd_node_construct_base64(size_t buf_size,
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_node_new(SerdNodeType type,
- SerdStringView string,
- SerdNodeFlags flags,
- SerdStringView meta);
+serd_node_new(SerdAllocator* SERD_NULLABLE allocator,
+ SerdNodeType type,
+ SerdStringView string,
+ SerdNodeFlags flags,
+ SerdStringView meta);
/**
Create a new simple "token" node.
@@ -891,7 +996,9 @@ serd_node_new(SerdNodeType type,
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_token(SerdNodeType type, SerdStringView string);
+serd_new_token(SerdAllocator* SERD_NULLABLE allocator,
+ SerdNodeType type,
+ SerdStringView string);
/**
Create a new string literal node.
@@ -904,7 +1011,7 @@ serd_new_token(SerdNodeType type, SerdStringView string);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_string(SerdStringView string);
+serd_new_string(SerdAllocator* SERD_NULLABLE allocator, SerdStringView string);
/**
Create a new URI node from a string.
@@ -917,7 +1024,7 @@ serd_new_string(SerdStringView string);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_uri(SerdStringView string);
+serd_new_uri(SerdAllocator* SERD_NULLABLE allocator, SerdStringView string);
/**
Create a new URI node from a parsed URI.
@@ -930,7 +1037,7 @@ serd_new_uri(SerdStringView string);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_parsed_uri(SerdURIView uri);
+serd_new_parsed_uri(SerdAllocator* SERD_NULLABLE allocator, SerdURIView uri);
/**
Create a new file URI node from a path and optional hostname.
@@ -943,7 +1050,9 @@ serd_new_parsed_uri(SerdURIView uri);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_file_uri(SerdStringView path, SerdStringView hostname);
+serd_new_file_uri(SerdAllocator* SERD_NULLABLE allocator,
+ SerdStringView path,
+ SerdStringView hostname);
/**
Create a new literal node.
@@ -956,9 +1065,10 @@ serd_new_file_uri(SerdStringView path, SerdStringView hostname);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_literal(SerdStringView string,
- SerdNodeFlags flags,
- SerdStringView meta);
+serd_new_literal(SerdAllocator* SERD_NULLABLE allocator,
+ SerdStringView string,
+ SerdNodeFlags flags,
+ SerdStringView meta);
/**
Create a new canonical xsd:boolean node.
@@ -971,7 +1081,7 @@ serd_new_literal(SerdStringView string,
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_boolean(bool b);
+serd_new_boolean(SerdAllocator* SERD_NULLABLE allocator, bool b);
/**
Create a new canonical xsd:decimal literal.
@@ -984,7 +1094,7 @@ serd_new_boolean(bool b);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_decimal(double d);
+serd_new_decimal(SerdAllocator* SERD_NULLABLE allocator, double d);
/**
Create a new canonical xsd:double literal.
@@ -997,7 +1107,7 @@ serd_new_decimal(double d);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_double(double d);
+serd_new_double(SerdAllocator* SERD_NULLABLE allocator, double d);
/**
Create a new canonical xsd:float literal.
@@ -1010,7 +1120,7 @@ serd_new_double(double d);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_float(float f);
+serd_new_float(SerdAllocator* SERD_NULLABLE allocator, float f);
/**
Create a new canonical xsd:integer literal.
@@ -1023,7 +1133,9 @@ serd_new_float(float f);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_integer(int64_t i, SerdStringView datatype);
+serd_new_integer(SerdAllocator* SERD_NULLABLE allocator,
+ int64_t i,
+ SerdStringView datatype);
/**
Create a new canonical xsd:base64Binary literal.
@@ -1036,9 +1148,10 @@ serd_new_integer(int64_t i, SerdStringView datatype);
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_new_base64(const void* SERD_NONNULL buf,
- size_t size,
- SerdStringView datatype);
+serd_new_base64(SerdAllocator* SERD_NULLABLE allocator,
+ const void* SERD_NONNULL buf,
+ size_t size,
+ SerdStringView datatype);
/**
@}
@@ -1125,12 +1238,14 @@ serd_get_base64(const SerdNode* SERD_NONNULL node,
/// Return a deep copy of `node`
SERD_API
SerdNode* SERD_ALLOCATED
-serd_node_copy(const SerdNode* SERD_NULLABLE node);
+serd_node_copy(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdNode* SERD_NULLABLE node);
/// Free any data owned by `node`
SERD_API
void
-serd_node_free(SerdNode* SERD_NULLABLE node);
+serd_node_free(SerdAllocator* SERD_NULLABLE allocator,
+ SerdNode* SERD_NULLABLE node);
/// Return the type of a node (SERD_URI, SERD_BLANK, or SERD_LITERAL)
SERD_PURE_API
@@ -1220,7 +1335,7 @@ typedef struct SerdNodesImpl SerdNodes;
/// Create a new node set
SERD_API
SerdNodes* SERD_ALLOCATED
-serd_nodes_new(void);
+serd_nodes_new(SerdAllocator* SERD_NULLABLE allocator);
/**
Free `nodes` and all nodes that are stored in it.
@@ -1307,9 +1422,6 @@ serd_nodes_parsed_uri(SerdNodes* SERD_NONNULL nodes, SerdURIView uri);
#SERD_HAS_DATATYPE is set, then this must be an absolute datatype URI. If
#SERD_HAS_LANGUAGE is set, then this must be an RFC 5646 language tag like
"en-ca". Otherwise, it is ignored.
-
- @return A newly allocated literal node that must be freed with
- serd_node_free(), or null if the arguments are invalid or allocation failed.
*/
SERD_API
const SerdNode* SERD_ALLOCATED
@@ -1422,6 +1534,7 @@ typedef struct SerdCaretImpl SerdCaret;
valid. That is, serd_caret_name() will return exactly the pointer
`name`, not a copy.
+ @param allocator Allocator to use for caret memory.
@param name The name of the document or stream (usually a file URI)
@param line The line number in the document (1-based)
@param col The column number in the document (1-based)
@@ -1429,17 +1542,22 @@ typedef struct SerdCaretImpl SerdCaret;
*/
SERD_API
SerdCaret* SERD_ALLOCATED
-serd_caret_new(const SerdNode* SERD_NONNULL name, unsigned line, unsigned col);
+serd_caret_new(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdNode* SERD_NONNULL name,
+ unsigned line,
+ unsigned col);
/// Return a copy of `caret`
SERD_API
SerdCaret* SERD_ALLOCATED
-serd_caret_copy(const SerdCaret* SERD_NULLABLE caret);
+serd_caret_copy(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdCaret* SERD_NULLABLE caret);
/// Free `caret`
SERD_API
void
-serd_caret_free(SerdCaret* SERD_NULLABLE caret);
+serd_caret_free(SerdAllocator* SERD_NULLABLE allocator,
+ SerdCaret* SERD_NULLABLE caret);
/// Return true iff `lhs` is equal to `rhs`
SERD_PURE_API
@@ -1492,6 +1610,7 @@ typedef enum {
statements in models, this is the lifetime of the model. For user-created
statements, the simplest way to handle this is to use `SerdNodes`.
+ @param allocator Allocator to use for statement memory.
@param s The subject
@param p The predicate ("key")
@param o The object ("value")
@@ -1501,7 +1620,8 @@ typedef enum {
*/
SERD_API
SerdStatement* SERD_ALLOCATED
-serd_statement_new(const SerdNode* SERD_NONNULL s,
+serd_statement_new(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdNode* SERD_NONNULL s,
const SerdNode* SERD_NONNULL p,
const SerdNode* SERD_NONNULL o,
const SerdNode* SERD_NULLABLE g,
@@ -1510,12 +1630,14 @@ serd_statement_new(const SerdNode* SERD_NONNULL s,
/// Return a copy of `statement`
SERD_API
SerdStatement* SERD_ALLOCATED
-serd_statement_copy(const SerdStatement* SERD_NULLABLE statement);
+serd_statement_copy(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdStatement* SERD_NULLABLE statement);
/// Free `statement`
SERD_API
void
-serd_statement_free(SerdStatement* SERD_NULLABLE statement);
+serd_statement_free(SerdAllocator* SERD_NULLABLE allocator,
+ SerdStatement* SERD_NULLABLE statement);
/// Return the given node of the statement
SERD_PURE_API
@@ -1590,13 +1712,18 @@ typedef struct SerdWorldImpl SerdWorld;
*/
SERD_MALLOC_API
SerdWorld* SERD_ALLOCATED
-serd_world_new(void);
+serd_world_new(SerdAllocator* SERD_NULLABLE allocator);
/// Free `world`
SERD_API
void
serd_world_free(SerdWorld* SERD_NULLABLE world);
+/// Return the allocator used by `world`
+SERD_PURE_API
+SerdAllocator* SERD_NONNULL
+serd_world_allocator(const SerdWorld* SERD_NONNULL world);
+
/**
Return the nodes cache in `world`.
@@ -2057,7 +2184,8 @@ serd_env_new(SerdWorld* SERD_NONNULL world, SerdStringView base_uri);
/// Copy an environment
SERD_API
SerdEnv* SERD_ALLOCATED
-serd_env_copy(const SerdEnv* SERD_NULLABLE env);
+serd_env_copy(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdEnv* SERD_NULLABLE env);
/// Return true iff `a` is equal to `b`
SERD_PURE_API
@@ -2152,6 +2280,9 @@ serd_env_write_prefixes(const SerdEnv* SERD_NONNULL env,
serd_node_to_syntax(). These two functions, when used with #SERD_TURTLE,
can be used to round-trip any node to a string and back.
+ @param allocator Allocator used for the returned node, and any temporary
+ objects if `env` is null.
+
@param str String representation of a node.
@param syntax Syntax to use. Should be either SERD_TURTLE or SERD_NTRIPLES
@@ -2161,13 +2292,15 @@ serd_env_write_prefixes(const SerdEnv* SERD_NONNULL env,
@param env Environment of `str`. This must define any abbreviations needed
to parse the string.
- @return A newly allocated node that must be freed with serd_node_free().
+ @return A newly allocated node that must be freed with serd_node_free()
+ using the world allocator.
*/
SERD_API
SerdNode* SERD_ALLOCATED
-serd_node_from_syntax(const char* SERD_NONNULL str,
- SerdSyntax syntax,
- SerdEnv* SERD_NULLABLE env);
+serd_node_from_syntax(SerdAllocator* SERD_NULLABLE allocator,
+ const char* SERD_NONNULL str,
+ SerdSyntax syntax,
+ SerdEnv* SERD_NULLABLE env);
/**
Return a string representation of `node` in `syntax`.
@@ -2175,6 +2308,9 @@ serd_node_from_syntax(const char* SERD_NONNULL str,
The returned string represents that node as if written as an object in the
given syntax, without any extra quoting or punctuation.
+ @param allocator Allocator used for the returned node, and any temporary
+ objects if `env` is null.
+
@param node Node to write as a string.
@param syntax Syntax to use. Should be either SERD_TURTLE or SERD_NTRIPLES
@@ -2184,11 +2320,13 @@ serd_node_from_syntax(const char* SERD_NONNULL str,
@param env Environment for the output string. This can be used to
abbreviate things nicely by setting namespace prefixes.
- @return A newly allocated string that must be freed with serd_free().
+ @return A newly allocated string that must be freed with serd_free() using
+ the world allocator.
*/
SERD_API
char* SERD_ALLOCATED
-serd_node_to_syntax(const SerdNode* SERD_NONNULL node,
+serd_node_to_syntax(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdNode* SERD_NONNULL node,
SerdSyntax syntax,
const SerdEnv* SERD_NULLABLE env);
@@ -2418,26 +2556,26 @@ serd_reader_free(SerdReader* SERD_NULLABLE reader);
@defgroup serd_buffer Buffer
The #SerdBuffer type represents a writable area of memory with a known size.
- An implementation of #SerdWriteFunc, #SerdErrorFunc, and
- #SerdCloseFunc are provided which allow output to be written to a
- buffer in memory instead of to a file as with `fwrite`, `ferror`, and
- `fclose`.
+ An implementation of #SerdWriteFunc, #SerdErrorFunc, and #SerdCloseFunc are
+ provided which allow output to be written to a buffer in memory instead of
+ to a file as with `fwrite`, `ferror`, and `fclose`.
@{
*/
-/// A mutable buffer in memory
+/// A dynamically resizable mutable buffer in memory
typedef struct {
- void* SERD_NULLABLE buf; ///< Buffer
- size_t len; ///< Size of buffer in bytes
+ SerdAllocator* SERD_NULLABLE allocator; ///< Allocator for buf
+ void* SERD_NULLABLE buf; ///< Buffer
+ size_t len; ///< Size of buffer in bytes
} SerdBuffer;
/**
A function for writing to a buffer, resizing it if necessary.
This function can be used as a #SerdWriteFunc to write to a #SerdBuffer
- which is resized as necessary with realloc(). The `stream` parameter must
- point to an initialized #SerdBuffer.
+ which is reallocated as necessary. The `stream` parameter must point to an
+ initialized #SerdBuffer.
Note that when writing a string, the string in the buffer will not be
null-terminated until serd_buffer_close() is called.
@@ -2498,9 +2636,9 @@ serd_open_output_stream(SerdWriteFunc SERD_NONNULL write_func,
/**
Open a stream that writes to a buffer.
- The `buffer` is owned by the caller, but will be expanded using `realloc` as
- necessary. Note that the string in the buffer will not be null terminated
- until the stream is closed.
+ The `buffer` is owned by the caller, but will be reallocated using the
+ buffer's allocator as necessary. Note that the string in the buffer will
+ not be null terminated until the stream is closed.
@param buffer Buffer to write output to.
@return An opened output stream, or all zeros on error.
@@ -2690,7 +2828,8 @@ typedef struct SerdCursorImpl SerdCursor;
/// Return a new copy of `cursor`
SERD_API
SerdCursor* SERD_ALLOCATED
-serd_cursor_copy(const SerdCursor* SERD_NULLABLE cursor);
+serd_cursor_copy(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdCursor* SERD_NULLABLE cursor);
/// Return the statement pointed to by `cursor`
SERD_API
@@ -2826,7 +2965,8 @@ serd_model_new(SerdWorld* SERD_NONNULL world,
/// Return a deep copy of `model`
SERD_API
SerdModel* SERD_ALLOCATED
-serd_model_copy(const SerdModel* SERD_NONNULL model);
+serd_model_copy(SerdAllocator* SERD_NULLABLE allocator,
+ const SerdModel* SERD_NONNULL model);
/// Return true iff `a` is equal to `b`, ignoring statement cursor metadata
SERD_API
diff --git a/meson.build b/meson.build
index 7d1d3c84..57547bfc 100644
--- a/meson.build
+++ b/meson.build
@@ -42,6 +42,7 @@ if get_option('strict')
'-Wno-float-conversion', # MinGW
'-Wno-format-nonliteral',
'-Wno-inline',
+ '-Wno-null-dereference', # ARM32
'-Wno-padded',
'-Wno-strict-overflow',
'-Wno-switch-default',
@@ -99,6 +100,7 @@ sources = [
'src/input_stream.c',
'src/inserter.c',
'src/log.c',
+ 'src/memory.c',
'src/model.c',
'src/n3.c',
'src/node.c',
diff --git a/src/block_dumper.c b/src/block_dumper.c
index 201b5dbf..6545d69c 100644
--- a/src/block_dumper.c
+++ b/src/block_dumper.c
@@ -15,6 +15,8 @@
*/
#include "block_dumper.h"
+
+#include "memory.h"
#include "system.h"
#include "serd/serd.h"
@@ -22,7 +24,8 @@
#include <stddef.h>
SerdStatus
-serd_block_dumper_open(SerdBlockDumper* const dumper,
+serd_block_dumper_open(const SerdWorld* const world,
+ SerdBlockDumper* const dumper,
SerdOutputStream* const output,
const size_t block_size)
{
@@ -39,7 +42,7 @@ serd_block_dumper_open(SerdBlockDumper* const dumper,
return SERD_SUCCESS;
}
- dumper->buf = (char*)serd_allocate_buffer(block_size);
+ dumper->buf = (char*)serd_waligned_alloc(world, SERD_PAGE_SIZE, block_size);
return dumper->buf ? SERD_SUCCESS : SERD_BAD_ALLOC;
}
@@ -55,5 +58,5 @@ serd_block_dumper_flush(SerdBlockDumper* const dumper)
void
serd_block_dumper_close(SerdBlockDumper* const dumper)
{
- serd_free_aligned(dumper->buf);
+ serd_aaligned_free(dumper->allocator, dumper->buf);
}
diff --git a/src/block_dumper.h b/src/block_dumper.h
index 7c718566..4581de42 100644
--- a/src/block_dumper.h
+++ b/src/block_dumper.h
@@ -23,6 +23,8 @@
#include <string.h>
typedef struct {
+ SerdAllocator* SERD_NONNULL allocator; ///< Buffer allocator
+
SerdOutputStream* SERD_ALLOCATED out; ///< Output stream to write to
char* SERD_ALLOCATED buf; ///< Local buffer if needed
size_t size; ///< Bytes pending for this block
@@ -36,7 +38,8 @@ typedef struct {
calling serd_block_dumper_close().
*/
SerdStatus
-serd_block_dumper_open(SerdBlockDumper* SERD_NONNULL dumper,
+serd_block_dumper_open(const SerdWorld* SERD_NONNULL world,
+ SerdBlockDumper* SERD_NONNULL dumper,
SerdOutputStream* SERD_NONNULL output,
size_t block_size);
diff --git a/src/buffer.c b/src/buffer.c
index 85ec09de..8d9eb95d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -14,11 +14,12 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "memory.h"
+
#include "serd/serd.h"
#include <assert.h>
#include <stddef.h>
-#include <stdlib.h>
#include <string.h>
size_t
@@ -33,20 +34,21 @@ serd_buffer_write(const void* const buf,
SerdBuffer* const buffer = (SerdBuffer*)stream;
const size_t n_bytes = size * nmemb;
- char* const new_buf = (char*)realloc(buffer->buf, buffer->len + n_bytes);
+ char* const new_buf =
+ (char*)serd_arealloc(buffer->allocator, buffer->buf, buffer->len + n_bytes);
+
if (new_buf) {
memcpy(new_buf + buffer->len, buf, n_bytes);
buffer->buf = new_buf;
buffer->len += nmemb;
+ return n_bytes;
}
- return new_buf ? nmemb : 0;
+ return 0;
}
int
serd_buffer_close(void* const stream)
{
- serd_buffer_write("", 1, 1, stream); // Write null terminator
-
- return 0;
+ return serd_buffer_write("", 1, 1, stream) != 1; // Write null terminator
}
diff --git a/src/byte_source.c b/src/byte_source.c
index 396e16d0..e4a82d6a 100644
--- a/src/byte_source.c
+++ b/src/byte_source.c
@@ -17,6 +17,7 @@
#include "byte_source.h"
#include "caret.h"
+#include "memory.h"
#include "system.h"
#include "serd/serd.h"
@@ -24,7 +25,6 @@
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdlib.h>
#include <string.h>
SerdStatus
@@ -53,33 +53,48 @@ serd_byte_source_page(SerdByteSource* const source)
}
static void
-serd_byte_source_init_buffer(SerdByteSource* const source)
+serd_byte_source_init_buffer(SerdAllocator* const allocator,
+ SerdByteSource* const source)
{
if (source->block_size > 1) {
- source->block = (uint8_t*)serd_allocate_buffer(source->block_size);
- source->read_buf = source->block;
- memset(source->block, '\0', source->block_size);
+ source->block = (uint8_t*)serd_aaligned_alloc(
+ allocator, SERD_PAGE_SIZE, source->block_size);
+
+ if ((source->read_buf = source->block)) {
+ memset(source->block, '\0', source->block_size);
+ }
} else {
source->read_buf = &source->read_byte;
}
}
SerdByteSource*
-serd_byte_source_new_input(SerdInputStream* const input,
+serd_byte_source_new_input(SerdAllocator* const allocator,
+ SerdInputStream* const input,
const SerdNode* const name,
const size_t block_size)
{
assert(input);
+ assert(block_size);
+ assert(input->stream);
- if (!block_size || !input->stream) {
+ SerdNode* const source_name =
+ name ? serd_node_copy(allocator, name)
+ : serd_new_string(allocator, SERD_STRING("input"));
+
+ if (!source_name) {
return NULL;
}
- SerdByteSource* source = (SerdByteSource*)calloc(1, sizeof(SerdByteSource));
+ SerdByteSource* source =
+ (SerdByteSource*)serd_acalloc(allocator, 1, sizeof(SerdByteSource));
- source->name =
- name ? serd_node_copy(name) : serd_new_string(SERD_STRING("input"));
+ if (!source) {
+ serd_node_free(allocator, source_name);
+ return NULL;
+ }
+ source->name = source_name;
source->in = input;
source->block_size = block_size;
source->buf_size = block_size;
@@ -87,21 +102,27 @@ serd_byte_source_new_input(SerdInputStream* const input,
source->caret.line = 1u;
source->caret.col = 1u;
- serd_byte_source_init_buffer(source);
+ serd_byte_source_init_buffer(allocator, source);
+ if (block_size > 1 && !source->block) {
+ serd_node_free(allocator, source_name);
+ serd_afree(allocator, source);
+ return NULL;
+ }
return source;
}
void
-serd_byte_source_free(SerdByteSource* const source)
+serd_byte_source_free(SerdAllocator* const allocator,
+ SerdByteSource* const source)
{
if (source) {
if (source->block_size > 1) {
- serd_free_aligned(source->block);
+ serd_aaligned_free(allocator, source->block);
}
- serd_node_free(source->name);
- free(source);
+ serd_node_free(allocator, source->name);
+ serd_afree(allocator, source);
}
}
diff --git a/src/byte_source.h b/src/byte_source.h
index 2bd06bdf..8246b581 100644
--- a/src/byte_source.h
+++ b/src/byte_source.h
@@ -41,12 +41,13 @@ typedef struct {
} SerdByteSource;
SerdByteSource*
-serd_byte_source_new_input(SerdInputStream* input,
+serd_byte_source_new_input(SerdAllocator* allocator,
+ SerdInputStream* input,
const SerdNode* name,
size_t block_size);
void
-serd_byte_source_free(SerdByteSource* source);
+serd_byte_source_free(SerdAllocator* allocator, SerdByteSource* source);
SerdStatus
serd_byte_source_prepare(SerdByteSource* source);
diff --git a/src/canon.c b/src/canon.c
index f8f1d8f0..a02d9a29 100644
--- a/src/canon.c
+++ b/src/canon.c
@@ -15,6 +15,7 @@
*/
#include "caret.h"
+#include "memory.h"
#include "namespaces.h"
#include "node.h"
#include "statement.h"
@@ -35,7 +36,8 @@ typedef struct {
} SerdCanonData;
static ExessResult
-build_typed(SerdNode** const out,
+build_typed(SerdAllocator* const SERD_NONNULL allocator,
+ SerdNode** const out,
const SerdNode* const SERD_NONNULL node,
const SerdNode* const SERD_NONNULL datatype)
{
@@ -46,7 +48,7 @@ build_typed(SerdNode** const out,
ExessResult r = {EXESS_SUCCESS, 0};
if (!strcmp(datatype_uri, NS_RDF "langString")) {
- *out = serd_new_string(serd_node_string_view(node));
+ *out = serd_new_string(allocator, serd_node_string_view(node));
return r;
}
@@ -65,7 +67,11 @@ build_typed(SerdNode** const out,
const size_t datatype_size = serd_node_total_size(datatype);
const size_t len = serd_node_pad_length(r.count);
const size_t total_len = sizeof(SerdNode) + len + datatype_size;
- SerdNode* const result = serd_node_malloc(total_len);
+ SerdNode* const result = serd_node_malloc(allocator, total_len);
+ if (!result) {
+ r.status = EXESS_NO_SPACE;
+ return r;
+ }
result->length = r.count;
result->flags = SERD_HAS_DATATYPE;
@@ -86,7 +92,8 @@ build_typed(SerdNode** const out,
}
static ExessResult
-build_tagged(SerdNode** const out,
+build_tagged(SerdAllocator* const SERD_NONNULL allocator,
+ SerdNode** const out,
const SerdNode* const SERD_NONNULL node,
const SerdNode* const SERD_NONNULL language)
{
@@ -107,7 +114,8 @@ build_tagged(SerdNode** const out,
}
// Make a new literal that is otherwise identical
- *out = serd_new_literal(serd_node_string_view(node),
+ *out = serd_new_literal(allocator,
+ serd_node_string_view(node),
serd_node_flags(node),
SERD_SUBSTRING(canonical_lang, lang_len));
@@ -122,16 +130,18 @@ serd_canon_on_statement(SerdCanonData* const data,
const SerdStatementFlags flags,
const SerdStatement* const statement)
{
- const SerdNode* const object = serd_statement_object(statement);
- const SerdNode* const datatype = serd_node_datatype(object);
- const SerdNode* const language = serd_node_language(object);
+ SerdAllocator* const allocator = serd_world_allocator(data->world);
+ const SerdNode* const object = serd_statement_object(statement);
+ const SerdNode* const datatype = serd_node_datatype(object);
+ const SerdNode* const language = serd_node_language(object);
if (!datatype && !language) {
return serd_sink_write_statement(data->target, flags, statement);
}
SerdNode* normo = NULL;
- const ExessResult r = datatype ? build_typed(&normo, object, datatype)
- : build_tagged(&normo, object, language);
+ const ExessResult r = datatype
+ ? build_typed(allocator, &normo, object, datatype)
+ : build_tagged(allocator, &normo, object, language);
if (r.status) {
SerdCaret caret = {NULL, 0u, 0u};
@@ -151,7 +161,7 @@ serd_canon_on_statement(SerdCanonData* const data,
exess_strerror(r.status));
if (!lax) {
- return SERD_BAD_LITERAL;
+ return r.status == EXESS_NO_SPACE ? SERD_BAD_ALLOC : SERD_BAD_LITERAL;
}
}
@@ -165,7 +175,7 @@ serd_canon_on_statement(SerdCanonData* const data,
statement->nodes[1],
normo,
statement->nodes[3]);
- serd_node_free(normo);
+ serd_node_free(allocator, normo);
return st;
}
@@ -183,13 +193,26 @@ serd_canon_new(const SerdWorld* const world,
const SerdSink* const target,
const SerdCanonFlags flags)
{
+ assert(world);
assert(target);
- SerdCanonData* const data = (SerdCanonData*)calloc(1, sizeof(SerdCanonData));
+ SerdCanonData* const data =
+ (SerdCanonData*)serd_wcalloc(world, 1, sizeof(SerdCanonData));
+
+ if (!data) {
+ return NULL;
+ }
data->world = world;
data->target = target;
data->flags = flags;
- return serd_sink_new(world, data, (SerdEventFunc)serd_canon_on_event, free);
+ SerdSink* const sink =
+ serd_sink_new(world, data, (SerdEventFunc)serd_canon_on_event, free);
+
+ if (!sink) {
+ serd_wfree(world, data);
+ }
+
+ return sink;
}
diff --git a/src/caret.c b/src/caret.c
index b6911468..67abbadb 100644
--- a/src/caret.c
+++ b/src/caret.c
@@ -16,40 +16,54 @@
#include "caret.h"
+#include "memory.h"
+
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
SerdCaret*
-serd_caret_new(const SerdNode* name, unsigned line, unsigned col)
+serd_caret_new(SerdAllocator* const allocator,
+ const SerdNode* const name,
+ const unsigned line,
+ const unsigned col)
{
assert(name);
- SerdCaret* caret = (SerdCaret*)malloc(sizeof(SerdCaret));
+ SerdCaret* const caret =
+ (SerdCaret*)serd_amalloc(allocator, sizeof(SerdCaret));
+
+ if (caret) {
+ caret->file = name;
+ caret->line = line;
+ caret->col = col;
+ }
- caret->file = name;
- caret->line = line;
- caret->col = col;
return caret;
}
SerdCaret*
-serd_caret_copy(const SerdCaret* caret)
+serd_caret_copy(SerdAllocator* const allocator, const SerdCaret* caret)
{
if (!caret) {
return NULL;
}
- SerdCaret* copy = (SerdCaret*)malloc(sizeof(SerdCaret));
- memcpy(copy, caret, sizeof(SerdCaret));
+ SerdCaret* const copy =
+ (SerdCaret*)serd_amalloc(allocator, sizeof(SerdCaret));
+
+ if (copy) {
+ memcpy(copy, caret, sizeof(SerdCaret));
+ }
+
return copy;
}
void
-serd_caret_free(SerdCaret* caret)
+serd_caret_free(SerdAllocator* const allocator, SerdCaret* caret)
{
- free(caret);
+ serd_afree(allocator, caret);
}
bool
diff --git a/src/cursor.c b/src/cursor.c
index 1a94142e..e3d7e35f 100644
--- a/src/cursor.c
+++ b/src/cursor.c
@@ -16,6 +16,7 @@
#include "cursor.h"
+#include "memory.h"
#include "model.h"
#include "node.h"
@@ -25,7 +26,6 @@
#include <assert.h>
#include <stdbool.h>
-#include <stdlib.h>
#include <string.h>
static inline bool
@@ -132,14 +132,19 @@ serd_cursor_make(const SerdModel* const model,
}
SerdCursor*
-serd_cursor_copy(const SerdCursor* const cursor)
+serd_cursor_copy(SerdAllocator* const allocator, const SerdCursor* const cursor)
{
if (!cursor) {
return NULL;
}
- SerdCursor* const copy = (SerdCursor* const)malloc(sizeof(SerdCursor));
- memcpy(copy, cursor, sizeof(SerdCursor));
+ SerdCursor* const copy =
+ (SerdCursor* const)serd_amalloc(allocator, sizeof(SerdCursor));
+
+ if (copy) {
+ memcpy(copy, cursor, sizeof(SerdCursor));
+ }
+
return copy;
}
@@ -223,5 +228,7 @@ serd_cursor_equals(const SerdCursor* const lhs, const SerdCursor* const rhs)
void
serd_cursor_free(SerdCursor* const cursor)
{
- free(cursor);
+ if (cursor) {
+ serd_afree(cursor->model->allocator, cursor);
+ }
}
diff --git a/src/describe.c b/src/describe.c
index 695217fd..92750363 100644
--- a/src/describe.c
+++ b/src/describe.c
@@ -23,6 +23,7 @@
#define ZIX_HASH_RECORD_TYPE const SerdNode
#include "serd/serd.h"
+#include "zix/allocator.h"
#include "zix/common.h"
#include "zix/digest.h"
#include "zix/hash.h"
@@ -295,16 +296,18 @@ serd_describe_range(const SerdCursor* const range,
assert(sink);
- SerdStatus st = SERD_SUCCESS;
- SerdCursor copy = *range;
- ZixHash* const list_subjects =
- zix_hash_new(NULL, identity, ptr_hash, ptr_equals);
+ SerdCursor copy = *range;
- DescribeContext ctx = {range->model, sink, list_subjects, flags};
+ ZixHash* const list_subjects = zix_hash_new(
+ (ZixAllocator*)range->model->allocator, identity, ptr_hash, ptr_equals);
- st = write_pretty_range(&ctx, 0, &copy, NULL, (flags & SERD_NO_TYPE_FIRST));
+ SerdStatus st = SERD_BAD_ALLOC;
+ if (list_subjects) {
+ DescribeContext ctx = {range->model, sink, list_subjects, flags};
- zix_hash_free(list_subjects);
+ st = write_pretty_range(&ctx, 0, &copy, NULL, (flags & SERD_NO_TYPE_FIRST));
+ }
+ zix_hash_free(list_subjects);
return st;
}
diff --git a/src/env.c b/src/env.c
index df82381c..67d77cab 100644
--- a/src/env.c
+++ b/src/env.c
@@ -16,14 +16,14 @@
#include "env.h"
+#include "memory.h"
#include "node.h"
+#include "world.h"
#include "serd/serd.h"
#include <assert.h>
#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
typedef struct {
@@ -46,32 +46,53 @@ serd_env_new(SerdWorld* const world, const SerdStringView base_uri)
assert(world);
(void)world;
- SerdEnv* env = (SerdEnv*)calloc(1, sizeof(struct SerdEnvImpl));
+ SerdEnv* env = (SerdEnv*)serd_wcalloc(world, 1, sizeof(struct SerdEnvImpl));
- env->world = world;
- env->nodes = serd_nodes_new();
+ if (env) {
+ env->world = world;
+ if (!(env->nodes = serd_nodes_new(world->allocator))) {
+ serd_wfree(world, env);
+ return NULL;
+ }
- if (env && base_uri.len) {
- serd_env_set_base_uri(env, base_uri);
+ if (base_uri.len) {
+ if (serd_env_set_base_uri(env, base_uri)) {
+ serd_nodes_free(env->nodes);
+ serd_wfree(world, env);
+ return NULL;
+ }
+ }
}
return env;
}
SerdEnv*
-serd_env_copy(const SerdEnv* const env)
+serd_env_copy(SerdAllocator* const allocator, const SerdEnv* const env)
{
if (!env) {
return NULL;
}
- SerdEnv* copy = (SerdEnv*)calloc(1, sizeof(struct SerdEnvImpl));
+ SerdEnv* copy =
+ (SerdEnv*)serd_acalloc(allocator, 1, sizeof(struct SerdEnvImpl));
+
if (copy) {
copy->world = env->world;
- copy->nodes = serd_nodes_new();
copy->n_prefixes = env->n_prefixes;
- copy->prefixes = (SerdPrefix*)malloc(copy->n_prefixes * sizeof(SerdPrefix));
+ if (!(copy->nodes = serd_nodes_new(allocator))) {
+ serd_wfree(env->world, copy);
+ return NULL;
+ }
+
+ if (!(copy->prefixes = (SerdPrefix*)serd_amalloc(
+ allocator, copy->n_prefixes * sizeof(SerdPrefix)))) {
+ serd_nodes_free(copy->nodes);
+ serd_wfree(env->world, copy);
+ return NULL;
+ }
+
for (size_t i = 0; i < copy->n_prefixes; ++i) {
copy->prefixes[i].name =
serd_nodes_intern(copy->nodes, env->prefixes[i].name);
@@ -93,9 +114,9 @@ void
serd_env_free(SerdEnv* const env)
{
if (env) {
- free(env->prefixes);
+ serd_wfree(env->world, env->prefixes);
serd_nodes_free(env->nodes);
- free(env);
+ serd_wfree(env->world, env);
}
}
@@ -158,8 +179,11 @@ serd_env_set_base_uri(SerdEnv* const env, const SerdStringView uri)
serd_resolve_uri(serd_parse_uri(uri.buf), env->base_uri);
// Replace the current base URI
- env->base_uri_node = serd_nodes_parsed_uri(env->nodes, new_base_uri);
- env->base_uri = serd_node_uri_view(env->base_uri_node);
+ if ((env->base_uri_node = serd_nodes_parsed_uri(env->nodes, new_base_uri))) {
+ env->base_uri = serd_node_uri_view(env->base_uri_node);
+ } else {
+ return SERD_BAD_ALLOC;
+ }
serd_nodes_deref(env->nodes, old_base_uri);
return SERD_SUCCESS;
@@ -182,7 +206,7 @@ serd_env_find(const SerdEnv* const env,
return NULL;
}
-static void
+static SerdStatus
serd_env_add(SerdEnv* const env,
const SerdStringView name,
const SerdNode* const uri)
@@ -194,14 +218,25 @@ serd_env_add(SerdEnv* const env,
prefix->uri = uri;
}
} else {
- env->prefixes = (SerdPrefix*)realloc(
- env->prefixes, (++env->n_prefixes) * sizeof(SerdPrefix));
+ const SerdNode* const name_node = serd_nodes_string(env->nodes, name);
+ if (!name_node) {
+ return SERD_BAD_ALLOC;
+ }
- env->prefixes[env->n_prefixes - 1].name =
- serd_nodes_string(env->nodes, name);
+ SerdPrefix* const new_prefixes = (SerdPrefix*)serd_wrealloc(
+ env->world, env->prefixes, (env->n_prefixes + 1) * sizeof(SerdPrefix));
- env->prefixes[env->n_prefixes - 1].uri = uri;
+ if (!new_prefixes) {
+ return SERD_BAD_ALLOC;
+ }
+
+ new_prefixes[env->n_prefixes].name = name_node;
+ new_prefixes[env->n_prefixes].uri = uri;
+ env->prefixes = new_prefixes;
+ ++env->n_prefixes;
}
+
+ return SERD_SUCCESS;
}
SerdStatus
@@ -213,8 +248,12 @@ serd_env_set_prefix(SerdEnv* const env,
if (serd_uri_string_has_scheme(uri.buf)) {
// Set prefix to absolute URI
- serd_env_add(env, name, serd_nodes_uri(env->nodes, uri));
- return SERD_SUCCESS;
+ const SerdNode* const abs_uri = serd_nodes_uri(env->nodes, uri);
+ if (!abs_uri) {
+ return SERD_BAD_ALLOC;
+ }
+
+ return serd_env_add(env, name, abs_uri);
}
if (!env->base_uri_node) {
@@ -230,11 +269,14 @@ serd_env_set_prefix(SerdEnv* const env,
const SerdNode* const abs_uri =
serd_nodes_parsed_uri(env->nodes, abs_uri_view);
+ if (!abs_uri) {
+ return SERD_BAD_ALLOC;
+ }
+
assert(serd_uri_string_has_scheme(serd_node_string(abs_uri)));
// Set prefix to resolved absolute URI
- serd_env_add(env, name, abs_uri);
- return SERD_SUCCESS;
+ return serd_env_add(env, name, abs_uri);
}
SerdStatus
@@ -303,7 +345,9 @@ serd_env_expand_curie(const SerdEnv* const env, const SerdStringView curie)
}
const size_t len = prefix.len + suffix.len;
- SerdNode* ret = serd_node_malloc(sizeof(SerdNode) + len + 1);
+ SerdNode* ret =
+ serd_node_malloc(env->world->allocator, sizeof(SerdNode) + len + 1);
+
if (ret) {
ret->length = len;
ret->flags = 0u;
@@ -331,8 +375,8 @@ serd_env_expand_node(const SerdEnv* const env, const SerdNode* const node)
return NULL;
}
- const SerdWriteResult r = serd_node_construct_uri(0u, NULL, abs_uri);
- SerdNode* const expanded = serd_node_try_malloc(r);
+ const SerdWriteResult r = serd_node_construct_uri(0u, NULL, abs_uri);
+ SerdNode* const expanded = serd_node_try_malloc(env->world->allocator, r);
if (expanded) {
serd_node_construct_uri(r.count, expanded, abs_uri);
}
diff --git a/src/filter.c b/src/filter.c
index 8efe1e91..8e0f5731 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -16,6 +16,9 @@
#include "serd/serd.h"
+#include "memory.h"
+#include "sink.h"
+
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
@@ -33,13 +36,15 @@ static void
free_data(void* const handle)
{
if (handle) {
- SerdFilterData* data = (SerdFilterData*)handle;
-
- serd_node_free(data->subject);
- serd_node_free(data->predicate);
- serd_node_free(data->object);
- serd_node_free(data->graph);
- free(data);
+ SerdFilterData* const data = (SerdFilterData*)handle;
+ const SerdWorld* const world = data->target->world;
+ SerdAllocator* const allocator = serd_world_allocator(world);
+
+ serd_node_free(allocator, data->subject);
+ serd_node_free(allocator, data->predicate);
+ serd_node_free(allocator, data->object);
+ serd_node_free(allocator, data->graph);
+ serd_wfree(data->target->world, data);
}
}
@@ -80,28 +85,53 @@ serd_filter_new(const SerdWorld* const world,
{
assert(world);
assert(target);
+ assert(target->world == world);
+ SerdAllocator* const allocator = serd_world_allocator(world);
SerdFilterData* const data =
- (SerdFilterData*)calloc(1, sizeof(SerdFilterData));
+ (SerdFilterData*)serd_wcalloc(world, 1, sizeof(SerdFilterData));
+
+ if (!data) {
+ return NULL;
+ }
data->target = target;
data->inclusive = inclusive;
if (subject && serd_node_type(subject) != SERD_VARIABLE) {
- data->subject = serd_node_copy(subject);
+ if (!(data->subject = serd_node_copy(allocator, subject))) {
+ free_data(data);
+ return NULL;
+ }
}
if (predicate && serd_node_type(predicate) != SERD_VARIABLE) {
- data->predicate = serd_node_copy(predicate);
+ if (!(data->predicate = serd_node_copy(allocator, predicate))) {
+ free_data(data);
+ return NULL;
+ }
}
if (object && serd_node_type(object) != SERD_VARIABLE) {
- data->object = serd_node_copy(object);
+ if (!(data->object = serd_node_copy(allocator, object))) {
+ free_data(data);
+ return NULL;
+ }
}
if (graph && serd_node_type(graph) != SERD_VARIABLE) {
- data->graph = serd_node_copy(graph);
+ if (!(data->graph = serd_node_copy(allocator, graph))) {
+ free_data(data);
+ return NULL;
+ }
+ }
+
+ SerdSink* const sink =
+ serd_sink_new(world, data, serd_filter_on_event, free_data);
+
+ if (!sink) {
+ free_data(data);
}
- return serd_sink_new(world, data, serd_filter_on_event, free_data);
+ return sink;
}
diff --git a/src/inserter.c b/src/inserter.c
index 9f388921..f5419b81 100644
--- a/src/inserter.c
+++ b/src/inserter.c
@@ -14,6 +14,7 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "memory.h"
#include "model.h"
#include "statement.h"
@@ -24,8 +25,8 @@
#include <stdlib.h>
typedef struct {
- SerdModel* model;
- const SerdNode* default_graph;
+ SerdModel* model;
+ SerdNode* default_graph;
} SerdInserterData;
static bool
@@ -107,19 +108,38 @@ serd_inserter_on_event(SerdInserterData* const data,
return SERD_SUCCESS;
}
+static SerdInserterData*
+serd_inserter_data_new(SerdModel* const model,
+ const SerdNode* const default_graph)
+{
+ SerdInserterData* const data =
+ (SerdInserterData*)serd_wcalloc(model->world, 1, sizeof(SerdInserterData));
+
+ if (data) {
+ data->model = model;
+ data->default_graph = serd_node_copy(model->allocator, default_graph);
+ }
+
+ return data;
+}
+
+static void
+serd_inserter_data_free(SerdInserterData* const data)
+{
+ serd_node_free(data->model->allocator, data->default_graph);
+ serd_wfree(data->model->world, data);
+}
+
SerdSink*
serd_inserter_new(SerdModel* const model, const SerdNode* const default_graph)
{
assert(model);
- SerdInserterData* const data =
- (SerdInserterData*)calloc(1, sizeof(SerdInserterData));
-
- data->model = model;
- data->default_graph = serd_node_copy(default_graph);
-
- SerdSink* const sink = serd_sink_new(
- model->world, data, (SerdEventFunc)serd_inserter_on_event, free);
+ SerdEventFunc func = (SerdEventFunc)serd_inserter_on_event;
+ SerdInserterData* const data = serd_inserter_data_new(model, default_graph);
- return sink;
+ return data
+ ? serd_sink_new(
+ model->world, data, func, (SerdFreeFunc)serd_inserter_data_free)
+ : NULL;
}
diff --git a/src/memory.c b/src/memory.c
new file mode 100644
index 00000000..a46319fb
--- /dev/null
+++ b/src/memory.c
@@ -0,0 +1,30 @@
+/*
+ Copyright 2011-2021 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 "memory.h"
+
+#include "serd/serd.h"
+#include "zix/allocator.h"
+
+SerdAllocator*
+serd_default_allocator(void)
+{
+ /* Note that SerdAllocator is intentionally the same as ZixAllocator. It
+ only exists to avoid exposing the zix API in the public serd API, which
+ I'm not sure would be appropriate. */
+
+ return (SerdAllocator*)zix_default_allocator();
+}
diff --git a/src/memory.h b/src/memory.h
new file mode 100644
index 00000000..e0cd4dff
--- /dev/null
+++ b/src/memory.h
@@ -0,0 +1,148 @@
+/*
+ Copyright 2011-2021 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_MEMORY_H
+#define SERD_MEMORY_H
+
+#include "serd/serd.h"
+
+#include <stddef.h>
+#include <string.h>
+
+// Allocator convenience wrappers that fall back to the default for NULL
+
+/// Convenience wrapper that defers to malloc() if allocator is null
+static inline void*
+serd_amalloc(SerdAllocator* const allocator, const size_t size)
+{
+ SerdAllocator* const actual =
+ allocator ? allocator : serd_default_allocator();
+
+ return actual->malloc(actual, size);
+}
+
+/// Convenience wrapper that defers to calloc() if allocator is null
+static inline void*
+serd_acalloc(SerdAllocator* const allocator,
+ const size_t nmemb,
+ const size_t size)
+{
+ SerdAllocator* const actual =
+ allocator ? allocator : serd_default_allocator();
+
+ return actual->calloc(actual, nmemb, size);
+}
+
+/// Convenience wrapper that defers to realloc() if allocator is null
+static inline void*
+serd_arealloc(SerdAllocator* const allocator,
+ void* const ptr,
+ const size_t size)
+{
+ SerdAllocator* const actual =
+ allocator ? allocator : serd_default_allocator();
+
+ return actual->realloc(actual, ptr, size);
+}
+
+/// Convenience wrapper that defers to free() if allocator is null
+static inline void
+serd_afree(SerdAllocator* const allocator, void* const ptr)
+{
+ SerdAllocator* const actual =
+ allocator ? allocator : serd_default_allocator();
+
+ actual->free(actual, ptr);
+}
+
+/// Convenience wrapper that defers to the system allocator if allocator is null
+static inline void*
+serd_aaligned_alloc(SerdAllocator* const allocator,
+ const size_t alignment,
+ const size_t size)
+{
+ SerdAllocator* const actual =
+ allocator ? allocator : serd_default_allocator();
+
+ return actual->aligned_alloc(actual, alignment, size);
+}
+
+/// Convenience wrapper for serd_aaligned_alloc that zeros memory
+static inline void*
+serd_aaligned_calloc(SerdAllocator* const allocator,
+ const size_t alignment,
+ const size_t size)
+{
+ void* const ptr = serd_aaligned_alloc(allocator, alignment, size);
+ if (ptr) {
+ memset(ptr, 0, size);
+ }
+ return ptr;
+}
+
+/// Convenience wrapper that defers to the system allocator if allocator is null
+static inline void
+serd_aaligned_free(SerdAllocator* const allocator, void* const ptr)
+{
+ SerdAllocator* const actual =
+ allocator ? allocator : serd_default_allocator();
+
+ actual->aligned_free(actual, ptr);
+}
+
+// World convenience wrappers
+
+static inline void*
+serd_wmalloc(const SerdWorld* const world, const size_t size)
+{
+ return serd_amalloc(serd_world_allocator(world), size);
+}
+
+static inline void*
+serd_wcalloc(const SerdWorld* const world,
+ const size_t nmemb,
+ const size_t size)
+{
+ return serd_acalloc(serd_world_allocator(world), nmemb, size);
+}
+
+static inline void*
+serd_wrealloc(const SerdWorld* const world, void* const ptr, const size_t size)
+{
+ return serd_arealloc(serd_world_allocator(world), ptr, size);
+}
+
+static inline void
+serd_wfree(const SerdWorld* const world, void* const ptr)
+{
+ serd_afree(serd_world_allocator(world), ptr);
+}
+
+static inline void*
+serd_waligned_alloc(const SerdWorld* const world,
+ const size_t alignment,
+ const size_t size)
+{
+ return serd_aaligned_alloc(serd_world_allocator(world), alignment, size);
+}
+
+static inline void
+serd_waligned_free(const SerdWorld* const world, void* const ptr)
+{
+ serd_aaligned_free(serd_world_allocator(world), ptr);
+}
+
+#endif // SERD_MEMORY_H
diff --git a/src/model.c b/src/model.c
index 4e79fa77..1b1c144a 100644
--- a/src/model.c
+++ b/src/model.c
@@ -19,15 +19,16 @@
#include "caret.h"
#include "compare.h"
#include "cursor.h"
+#include "memory.h"
#include "statement.h"
+#include "zix/allocator.h"
#include "zix/btree.h"
#include "zix/common.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
-#include <stdlib.h>
static const SerdQuad everything_pattern = {0, 0, 0, 0};
@@ -73,11 +74,15 @@ serd_model_add_index(SerdModel* const model, const SerdStatementOrder order)
const unsigned* const ordering = orderings[order];
const ZixComparator comparator = serd_model_index_comparator(model, order);
- model->indices[order] = zix_btree_new(NULL, comparator, ordering);
+ model->indices[order] =
+ zix_btree_new((ZixAllocator*)model->allocator, comparator, ordering);
- ZixStatus zst = model->indices[order] ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR;
+ if (!model->indices[order]) {
+ return SERD_BAD_ALLOC;
+ }
// Insert statements from the default index
+ ZixStatus zst = ZIX_STATUS_SUCCESS;
if (order != model->default_order) {
ZixBTree* const default_index = model->indices[model->default_order];
for (ZixBTreeIter i = zix_btree_begin(default_index);
@@ -108,21 +113,38 @@ serd_model_drop_index(SerdModel* const model, const SerdStatementOrder order)
return SERD_SUCCESS;
}
-SerdModel*
-serd_model_new(SerdWorld* const world,
- const SerdStatementOrder default_order,
- const SerdModelFlags flags)
+static SerdModel*
+serd_model_new_with_allocator(SerdAllocator* const allocator,
+ SerdWorld* const world,
+ const SerdStatementOrder default_order,
+ const SerdModelFlags flags)
{
assert(world);
- SerdModel* model = (SerdModel*)calloc(1, sizeof(struct SerdModelImpl));
+ SerdNodes* const nodes = serd_nodes_new(allocator);
+ if (!nodes) {
+ return NULL;
+ }
+
+ SerdModel* model =
+ (SerdModel*)serd_acalloc(allocator, 1, sizeof(struct SerdModelImpl));
+ if (!model) {
+ serd_nodes_free(nodes);
+ return NULL;
+ }
+
+ model->allocator = allocator;
model->world = world;
- model->nodes = serd_nodes_new();
+ model->nodes = nodes;
model->default_order = default_order;
model->flags = flags;
- serd_model_add_index(model, default_order);
+ if (serd_model_add_index(model, default_order)) {
+ serd_nodes_free(nodes);
+ serd_wfree(world, model);
+ return NULL;
+ }
const ScanStrategy end_strategy = {SCAN_EVERYTHING, 0u, default_order};
@@ -135,12 +157,21 @@ serd_model_new(SerdWorld* const world,
}
SerdModel*
-serd_model_copy(const SerdModel* const model)
+serd_model_new(SerdWorld* const world,
+ const SerdStatementOrder default_order,
+ const SerdModelFlags flags)
+{
+ return serd_model_new_with_allocator(
+ serd_world_allocator(world), world, default_order, flags);
+}
+
+SerdModel*
+serd_model_copy(SerdAllocator* const allocator, const SerdModel* const model)
{
assert(model);
- SerdModel* copy =
- serd_model_new(model->world, model->default_order, model->flags);
+ SerdModel* copy = serd_model_new_with_allocator(
+ allocator, model->world, model->default_order, model->flags);
SerdCursor* cursor = serd_model_begin(model);
serd_model_insert_statements(copy, cursor);
@@ -239,18 +270,23 @@ serd_model_drop_statement(SerdModel* const model,
}
}
- if (statement->caret && statement->caret->file) {
- serd_nodes_deref(model->nodes, statement->caret->file);
+ if (statement->caret && serd_caret_name(statement->caret)) {
+ serd_nodes_deref(model->nodes, serd_caret_name(statement->caret));
}
- serd_statement_free(statement);
+ serd_statement_free(model->allocator, statement);
}
+typedef struct {
+ SerdAllocator* allocator;
+} DestroyContext;
+
static void
destroy_tree_statement(void* ptr, const void* user_data)
{
- (void)user_data;
- serd_statement_free((SerdStatement*)ptr);
+ const DestroyContext* const ctx = (const DestroyContext*)user_data;
+
+ serd_statement_free(ctx->allocator, (SerdStatement*)ptr);
}
void
@@ -261,8 +297,9 @@ serd_model_free(SerdModel* const model)
}
// Free all statements (which are owned by the default index)
- ZixBTree* const default_index = model->indices[model->default_order];
- zix_btree_clear(default_index, destroy_tree_statement, NULL);
+ ZixBTree* const default_index = model->indices[model->default_order];
+ const DestroyContext ctx = {model->allocator};
+ zix_btree_clear(default_index, destroy_tree_statement, &ctx);
// Free indices themselves
for (unsigned i = 0u; i < N_STATEMENT_ORDERS; ++i) {
@@ -270,7 +307,7 @@ serd_model_free(SerdModel* const model)
}
serd_nodes_free(model->nodes);
- free(model);
+ serd_wfree(model->world, model);
}
SerdWorld*
@@ -323,7 +360,7 @@ serd_model_begin_ordered(const SerdModel* const model,
const SerdCursor cursor = make_begin_cursor(model, order);
- return serd_cursor_copy(&cursor);
+ return serd_cursor_copy(model->allocator, &cursor);
}
SerdCursor*
@@ -524,7 +561,9 @@ serd_model_find(const SerdModel* const model,
const SerdCursor cursor = serd_model_search(model, s, p, o, g);
- return zix_btree_iter_is_end(cursor.iter) ? NULL : serd_cursor_copy(&cursor);
+ return zix_btree_iter_is_end(cursor.iter)
+ ? NULL
+ : serd_cursor_copy(model->allocator, &cursor);
}
const SerdNode*
@@ -602,16 +641,20 @@ serd_model_ask(const SerdModel* const model,
static SerdCaret*
serd_model_intern_caret(SerdModel* const model, const SerdCaret* const caret)
{
- if (caret) {
- SerdCaret* copy = (SerdCaret*)calloc(1, sizeof(SerdCaret));
+ if (!caret) {
+ return NULL;
+ }
+ SerdCaret* const copy =
+ (SerdCaret*)serd_acalloc(model->allocator, 1, sizeof(SerdCaret));
+
+ if (copy) {
copy->file = serd_nodes_intern(model->nodes, caret->file);
copy->line = caret->line;
copy->col = caret->col;
- return copy;
}
- return NULL;
+ return copy;
}
SerdStatus
@@ -624,7 +667,9 @@ serd_model_add_with_caret(SerdModel* const model,
{
assert(model);
- SerdStatement* const statement = serd_statement_new(s, p, o, g, NULL);
+ SerdStatement* const statement =
+ serd_statement_new(model->allocator, s, p, o, g, NULL);
+
if (!statement) {
return SERD_BAD_ALLOC;
}
diff --git a/src/model.h b/src/model.h
index 5fbf6209..7c4816a4 100644
--- a/src/model.h
+++ b/src/model.h
@@ -25,6 +25,7 @@
#include <stddef.h>
struct SerdModelImpl {
+ SerdAllocator* allocator; ///< Allocator for everything in this model
SerdWorld* world; ///< World this model is a part of
SerdNodes* nodes; ///< Interned nodes in this model
ZixBTree* indices[12]; ///< Trees of SerdStatement pointers
diff --git a/src/node.c b/src/node.c
index 61a45a03..0436c4fb 100644
--- a/src/node.c
+++ b/src/node.c
@@ -16,9 +16,9 @@
#include "node.h"
+#include "memory.h"
#include "namespaces.h"
#include "string_utils.h"
-#include "system.h"
#include "exess/exess.h"
#include "serd/serd.h"
@@ -81,39 +81,46 @@ serd_node_total_size(const SerdNode* const node)
}
SerdNode*
-serd_node_malloc(const size_t size)
+serd_node_malloc(SerdAllocator* const allocator, const size_t size)
{
- SerdNode* const node =
- (SerdNode*)serd_calloc_aligned(serd_node_align, serd_node_pad_size(size));
+ SerdNode* const node = (SerdNode*)serd_aaligned_calloc(
+ allocator, serd_node_align, serd_node_pad_size(size));
assert((uintptr_t)node % serd_node_align == 0);
return node;
}
SerdNode*
-serd_node_try_malloc(const SerdWriteResult r)
+serd_node_try_malloc(SerdAllocator* const allocator, const SerdWriteResult r)
{
- return (r.status && r.status != SERD_OVERFLOW) ? NULL
- : serd_node_malloc(r.count);
+ return (r.status && r.status != SERD_OVERFLOW)
+ ? NULL
+ : serd_node_malloc(allocator, r.count);
}
-void
-serd_node_set(SerdNode** const dst, const SerdNode* const src)
+SerdStatus
+serd_node_set(SerdAllocator* const allocator,
+ SerdNode** const dst,
+ const SerdNode* const src)
{
if (!src) {
- serd_free_aligned(*dst);
+ serd_aaligned_free(allocator, *dst);
*dst = NULL;
- return;
+ return SERD_SUCCESS;
}
const size_t size = serd_node_total_size(src);
if (!*dst || serd_node_total_size(*dst) < size) {
- serd_free_aligned(*dst);
- *dst = (SerdNode*)serd_calloc_aligned(serd_node_align, size);
+ serd_aaligned_free(allocator, *dst);
+ if (!(*dst = (SerdNode*)serd_aaligned_calloc(
+ allocator, serd_node_align, size))) {
+ return SERD_BAD_ALLOC;
+ }
}
assert(*dst);
memcpy(*dst, src, size);
+ return SERD_SUCCESS;
}
/**
@@ -457,7 +464,8 @@ serd_node_construct_uri(const size_t buf_size,
}
SerdNode*
-serd_node_new(const SerdNodeType type,
+serd_node_new(SerdAllocator* const allocator,
+ const SerdNodeType type,
const SerdStringView string,
const SerdNodeFlags flags,
const SerdStringView meta)
@@ -469,7 +477,8 @@ serd_node_new(const SerdNodeType type,
assert(r.count % sizeof(SerdNode) == 0);
- SerdNode* const node = serd_node_malloc(sizeof(SerdNode) + r.count + 1);
+ SerdNode* const node =
+ serd_node_malloc(allocator, sizeof(SerdNode) + r.count + 1);
if (node) {
r = serd_node_construct(r.count, node, type, string, flags, meta);
@@ -480,23 +489,26 @@ serd_node_new(const SerdNodeType type,
}
SerdNode*
-serd_new_token(const SerdNodeType type, const SerdStringView string)
+serd_new_token(SerdAllocator* const allocator,
+ const SerdNodeType type,
+ const SerdStringView string)
{
- return serd_node_new(type, string, 0u, SERD_EMPTY_STRING());
+ return serd_node_new(allocator, type, string, 0u, SERD_EMPTY_STRING());
}
SerdNode*
-serd_new_string(const SerdStringView str)
+serd_new_string(SerdAllocator* const allocator, const SerdStringView str)
{
- return serd_node_new(SERD_LITERAL, str, 0u, SERD_EMPTY_STRING());
+ return serd_node_new(allocator, SERD_LITERAL, str, 0u, SERD_EMPTY_STRING());
}
SerdNode*
-serd_new_literal(const SerdStringView str,
+serd_new_literal(SerdAllocator* const allocator,
+ const SerdStringView str,
const SerdNodeFlags flags,
const SerdStringView meta)
{
- return serd_node_new(SERD_LITERAL, str, flags, meta);
+ return serd_node_new(allocator, SERD_LITERAL, str, flags, meta);
}
ExessResult
@@ -607,16 +619,20 @@ serd_get_base64(const SerdNode* const node,
}
SerdNode*
-serd_node_copy(const SerdNode* node)
+serd_node_copy(SerdAllocator* const allocator, const SerdNode* node)
{
if (!node) {
return NULL;
}
const size_t size = serd_node_total_size(node);
- SerdNode* copy = (SerdNode*)serd_calloc_aligned(serd_node_align, size);
+ SerdNode* copy =
+ (SerdNode*)serd_aaligned_alloc(allocator, serd_node_align, size);
+
+ if (copy) {
+ memcpy(copy, node, size);
+ }
- memcpy(copy, node, size);
return copy;
}
@@ -677,16 +693,16 @@ serd_node_compare(const SerdNode* const a, const SerdNode* const b)
}
SerdNode*
-serd_new_uri(const SerdStringView string)
+serd_new_uri(SerdAllocator* const allocator, const SerdStringView string)
{
- return serd_new_token(SERD_URI, string);
+ return serd_new_token(allocator, SERD_URI, string);
}
SerdNode*
-serd_new_parsed_uri(const SerdURIView uri)
+serd_new_parsed_uri(SerdAllocator* const allocator, const SerdURIView uri)
{
SerdWriteResult r = serd_node_construct_uri(0u, NULL, uri);
- SerdNode* const node = serd_node_try_malloc(r);
+ SerdNode* const node = serd_node_try_malloc(allocator, r);
if (node) {
r = serd_node_construct_uri(r.count, node, uri);
@@ -758,114 +774,121 @@ serd_node_construct_file_uri(const size_t buf_size,
}
SerdNode*
-serd_new_file_uri(const SerdStringView path, const SerdStringView hostname)
+serd_new_file_uri(SerdAllocator* const allocator,
+ const SerdStringView path,
+ const SerdStringView hostname)
{
SerdWriteResult r = serd_node_construct_file_uri(0, NULL, path, hostname);
- SerdNode* const node = serd_node_try_malloc(r);
+ SerdNode* const node = serd_node_try_malloc(allocator, r);
if (node) {
r = serd_node_construct_file_uri(r.count, node, path, hostname);
MUST_SUCCEED(r.status);
assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ serd_node_check_padding(node);
}
- serd_node_check_padding(node);
return node;
}
SerdNode*
-serd_new_double(const double d)
+serd_new_double(SerdAllocator* const allocator, const double d)
{
SerdWriteResult r = serd_node_construct_double(0, NULL, d);
- SerdNode* const node = serd_node_try_malloc(r);
+ SerdNode* const node = serd_node_try_malloc(allocator, r);
if (node) {
r = serd_node_construct_double(r.count, node, d);
MUST_SUCCEED(r.status);
assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ serd_node_check_padding(node);
}
- serd_node_check_padding(node);
return node;
}
SerdNode*
-serd_new_float(const float f)
+serd_new_float(SerdAllocator* const allocator, const float f)
{
SerdWriteResult r = serd_node_construct_float(0, NULL, f);
- SerdNode* const node = serd_node_try_malloc(r);
+ SerdNode* const node = serd_node_try_malloc(allocator, r);
if (node) {
r = serd_node_construct_float(r.count, node, f);
MUST_SUCCEED(r.status);
assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ serd_node_check_padding(node);
}
- serd_node_check_padding(node);
return node;
}
SerdNode*
-serd_new_boolean(bool b)
+serd_new_boolean(SerdAllocator* const allocator, bool b)
{
SerdWriteResult r = serd_node_construct_boolean(0, NULL, b);
- SerdNode* const node = serd_node_try_malloc(r);
+ SerdNode* const node = serd_node_try_malloc(allocator, r);
if (node) {
r = serd_node_construct_boolean(r.count, node, b);
MUST_SUCCEED(r.status);
assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ serd_node_check_padding(node);
}
- serd_node_check_padding(node);
return node;
}
SerdNode*
-serd_new_decimal(const double d)
+serd_new_decimal(SerdAllocator* const allocator, const double d)
{
SerdWriteResult r = serd_node_construct_decimal(0, NULL, d);
- SerdNode* const node = serd_node_try_malloc(r);
+ SerdNode* const node = serd_node_try_malloc(allocator, r);
if (node) {
r = serd_node_construct_decimal(r.count, node, d);
MUST_SUCCEED(r.status);
assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ serd_node_check_padding(node);
}
- serd_node_check_padding(node);
return node;
}
SerdNode*
-serd_new_integer(const int64_t i, const SerdStringView datatype)
+serd_new_integer(SerdAllocator* const allocator,
+ const int64_t i,
+ const SerdStringView datatype)
{
SerdWriteResult r = serd_node_construct_integer(0, NULL, i, datatype);
- SerdNode* const node = serd_node_try_malloc(r);
+ SerdNode* const node = serd_node_try_malloc(allocator, r);
if (node) {
r = serd_node_construct_integer(r.count, node, i, datatype);
MUST_SUCCEED(r.status);
assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ serd_node_check_padding(node);
}
- serd_node_check_padding(node);
return node;
}
SerdNode*
-serd_new_base64(const void* buf, size_t size, const SerdStringView datatype)
+serd_new_base64(SerdAllocator* const allocator,
+ const void* buf,
+ size_t size,
+ const SerdStringView datatype)
{
SerdWriteResult r = serd_node_construct_base64(0, NULL, size, buf, datatype);
- SerdNode* const node = serd_node_try_malloc(r);
+ SerdNode* const node = serd_node_try_malloc(allocator, r);
if (node) {
r = serd_node_construct_base64(r.count, node, size, buf, datatype);
MUST_SUCCEED(r.status);
assert(serd_node_length(node) == strlen(serd_node_string(node)));
+ serd_node_check_padding(node);
}
- serd_node_check_padding(node);
return node;
}
@@ -947,9 +970,9 @@ serd_node_flags(const SerdNode* const node)
}
void
-serd_node_free(SerdNode* const node)
+serd_node_free(SerdAllocator* const allocator, SerdNode* const node)
{
- serd_free_aligned(node);
+ serd_aaligned_free(allocator, node);
}
#undef MUST_SUCCEED
diff --git a/src/node.h b/src/node.h
index ab10a3e7..751b827a 100644
--- a/src/node.h
+++ b/src/node.h
@@ -102,14 +102,16 @@ is_langtag(SerdStringView string);
SERD_MALLOC_FUNC
SerdNode* SERD_ALLOCATED
-serd_node_malloc(size_t size);
+serd_node_malloc(SerdAllocator* SERD_NULLABLE allocator, size_t size);
SERD_MALLOC_FUNC
SerdNode* SERD_ALLOCATED
-serd_node_try_malloc(SerdWriteResult result);
+serd_node_try_malloc(SerdAllocator* SERD_NULLABLE allocator,
+ SerdWriteResult result);
-void
-serd_node_set(SerdNode* SERD_NULLABLE* SERD_NONNULL dst,
+SerdStatus
+serd_node_set(SerdAllocator* SERD_NULLABLE allocator,
+ SerdNode* SERD_NULLABLE* SERD_NONNULL dst,
const SerdNode* SERD_NULLABLE src);
SERD_PURE_FUNC
diff --git a/src/node_syntax.c b/src/node_syntax.c
index 261d4b03..166fad0e 100644
--- a/src/node_syntax.c
+++ b/src/node_syntax.c
@@ -15,21 +15,28 @@
*/
#include "env.h"
+#include "memory.h"
#include "writer.h"
#include "serd/serd.h"
#include <assert.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
+typedef struct {
+ SerdAllocator* allocator;
+ SerdNode* object;
+} NodeSyntaxContext;
+
static SerdStatus
-on_node_string_event(void* const handle, const SerdEvent* const event)
+on_syntax_event(void* const handle, const SerdEvent* const event)
{
+ NodeSyntaxContext* const ctx = (NodeSyntaxContext*)handle;
+
if (event->type == SERD_STATEMENT) {
- *(SerdNode**)handle =
- serd_node_copy(serd_statement_object(event->statement.statement));
+ ctx->object = serd_node_copy(
+ ctx->allocator, serd_statement_object(event->statement.statement));
}
return SERD_SUCCESS;
@@ -46,44 +53,46 @@ serd_node_from_syntax_in(SerdWorld* const world,
static const char* const prelude =
"_:s <http://www.w3.org/2000/01/rdf-schema#object>";
- const size_t str_len = strlen(str);
- const size_t doc_len = strlen(prelude) + str_len + 4;
- char* const doc = (char*)calloc(doc_len + 1, 1);
-
- snprintf(doc, doc_len + 1, "%s %s .", prelude, str);
-
- SerdNode* object = NULL;
- SerdSink* const sink =
- serd_sink_new(world, &object, on_node_string_event, NULL);
-
- SerdReader* const reader =
- serd_reader_new(world,
- syntax,
- SERD_READ_RELATIVE | SERD_READ_GLOBAL | SERD_READ_GENERATED,
- env,
- sink,
- 1024 + doc_len);
-
- const SerdNode* string_name =
- serd_nodes_string(serd_world_nodes(world), SERD_STRING("string"));
-
- const char* position = doc;
- SerdInputStream in = serd_open_input_string(&position);
- serd_reader_start(reader, &in, string_name, 1);
- serd_reader_read_document(reader);
- serd_reader_finish(reader);
- serd_close_input(&in);
- serd_reader_free(reader);
+ const size_t str_len = strlen(str);
+ const size_t doc_len = strlen(prelude) + str_len + 4;
+ NodeSyntaxContext ctx = {serd_world_allocator(world), NULL};
+ char* const doc = (char*)serd_wcalloc(world, doc_len + 1, 1);
+ SerdSink* const sink = serd_sink_new(world, &ctx, on_syntax_event, NULL);
+
+ if (doc && sink) {
+ snprintf(doc, doc_len + 1, "%s %s .", prelude, str);
+
+ SerdReader* const reader = serd_reader_new(
+ world,
+ syntax,
+ SERD_READ_RELATIVE | SERD_READ_GLOBAL | SERD_READ_GENERATED,
+ env,
+ sink,
+ 1024 + doc_len);
+
+ if (reader) {
+ const char* position = doc;
+ SerdInputStream in = serd_open_input_string(&position);
+ serd_reader_start(reader, &in, NULL, 1);
+ serd_reader_read_document(reader);
+ serd_reader_finish(reader);
+ serd_close_input(&in);
+ }
+
+ serd_reader_free(reader);
+ }
+
serd_sink_free(sink);
- free(doc);
+ serd_wfree(world, doc);
- return object;
+ return ctx.object;
}
SerdNode*
-serd_node_from_syntax(const char* const str,
- const SerdSyntax syntax,
- SerdEnv* const env)
+serd_node_from_syntax(SerdAllocator* const allocator,
+ const char* const str,
+ const SerdSyntax syntax,
+ SerdEnv* const env)
{
assert(str);
@@ -91,7 +100,7 @@ serd_node_from_syntax(const char* const str,
return serd_node_from_syntax_in(serd_env_world(env), str, syntax, env);
}
- SerdWorld* const temp_world = serd_world_new();
+ SerdWorld* const temp_world = serd_world_new(allocator);
if (!temp_world) {
return NULL;
}
@@ -116,27 +125,34 @@ serd_node_to_syntax_in(SerdWorld* const world,
const SerdSyntax syntax,
const SerdEnv* const env)
{
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {serd_world_allocator(world), NULL, 0};
SerdOutputStream out = serd_open_output_buffer(&buffer);
SerdWriter* const writer = serd_writer_new(world, syntax, 0, env, &out, 1);
+ if (!writer) {
+ return NULL;
+ }
char* result = NULL;
- if (!serd_writer_write_node(writer, node) && !serd_writer_finish(writer) &&
- !serd_close_output(&out)) {
- result = (char*)buffer.buf;
+ if (!serd_writer_write_node(writer, node) && !serd_writer_finish(writer)) {
+ if (!serd_close_output(&out)) {
+ result = (char*)buffer.buf;
+ }
} else {
serd_close_output(&out);
- free(buffer.buf);
}
serd_writer_free(writer);
- serd_close_output(&out);
+
+ if (!result) {
+ serd_wfree(world, buffer.buf);
+ }
return result;
}
char*
-serd_node_to_syntax(const SerdNode* const node,
+serd_node_to_syntax(SerdAllocator* const allocator,
+ const SerdNode* const node,
const SerdSyntax syntax,
const SerdEnv* const env)
{
@@ -146,16 +162,21 @@ serd_node_to_syntax(const SerdNode* const node,
return serd_node_to_syntax_in(serd_env_world(env), node, syntax, env);
}
- SerdWorld* const temp_world = serd_world_new();
- SerdEnv* const temp_env = serd_env_new(temp_world, SERD_EMPTY_STRING());
- if (temp_env) {
- char* const string =
- serd_node_to_syntax_in(temp_world, node, syntax, temp_env);
+ SerdWorld* const temp_world = serd_world_new(allocator);
+ if (!temp_world) {
+ return NULL;
+ }
- serd_env_free(temp_env);
+ SerdEnv* const temp_env = serd_env_new(temp_world, SERD_EMPTY_STRING());
+ if (!temp_env) {
serd_world_free(temp_world);
- return string;
+ return NULL;
}
- return NULL;
+ char* const string =
+ serd_node_to_syntax_in(temp_world, node, syntax, temp_env);
+
+ serd_env_free(temp_env);
+ serd_world_free(temp_world);
+ return string;
}
diff --git a/src/nodes.c b/src/nodes.c
index 529af688..09b63f2a 100644
--- a/src/nodes.c
+++ b/src/nodes.c
@@ -16,9 +16,9 @@
#include "nodes.h"
+#include "memory.h"
#include "node.h"
#include "node_spec.h"
-#include "system.h"
// Define the types used in the hash interface for more type safety
#define ZIX_HASH_KEY_TYPE SerdNode
@@ -26,13 +26,13 @@
#define ZIX_HASH_SEARCH_DATA_TYPE NodeSpec
#include "serd/serd.h"
+#include "zix/allocator.h"
#include "zix/digest.h"
#include "zix/hash.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdlib.h>
#include <string.h>
#if ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112l) || \
@@ -76,7 +76,8 @@ typedef struct {
} StaticNode;
struct SerdNodesImpl {
- ZixHash* hash;
+ SerdAllocator* allocator;
+ ZixHash* hash;
};
static const StaticNode empty_static_node = {{0u, 0u, SERD_LITERAL}, {'\0'}};
@@ -185,10 +186,10 @@ nodes_equal(const SerdNode* const a, const SerdNode* const b)
}
static NodesEntry*
-new_entry(const size_t node_size)
+new_entry(SerdAllocator* const allocator, const size_t node_size)
{
- NodesEntry* const entry = (NodesEntry*)serd_calloc_aligned(
- serd_node_align, sizeof(NodesEntryHead) + node_size);
+ NodesEntry* const entry = (NodesEntry*)serd_aaligned_calloc(
+ allocator, serd_node_align, sizeof(NodesEntryHead) + node_size);
if (entry) {
entry->head.refs = 1u;
@@ -198,11 +199,20 @@ new_entry(const size_t node_size)
}
SerdNodes*
-serd_nodes_new(void)
+serd_nodes_new(SerdAllocator* const allocator)
{
- SerdNodes* const nodes = (SerdNodes*)calloc(1, sizeof(SerdNodes));
+ SerdNodes* const nodes =
+ (SerdNodes*)serd_acalloc(allocator, 1, sizeof(SerdNodes));
- nodes->hash = zix_hash_new(NULL, nodes_key, nodes_hash, nodes_equal);
+ if (nodes) {
+ nodes->allocator = allocator;
+
+ if (!(nodes->hash = zix_hash_new(
+ (ZixAllocator*)allocator, nodes_key, nodes_hash, nodes_equal))) {
+ serd_afree(allocator, nodes);
+ return NULL;
+ }
+ }
return nodes;
}
@@ -214,11 +224,11 @@ serd_nodes_free(SerdNodes* nodes)
for (ZixHashIter i = zix_hash_begin(nodes->hash);
i != zix_hash_end(nodes->hash);
i = zix_hash_next(nodes->hash, i)) {
- serd_free_aligned(zix_hash_get(nodes->hash, i));
+ serd_aaligned_free(nodes->allocator, zix_hash_get(nodes->hash, i));
}
zix_hash_free(nodes->hash);
- free(nodes);
+ serd_afree(nodes->allocator, nodes);
}
}
@@ -247,7 +257,10 @@ serd_nodes_intern(SerdNodes* nodes, const SerdNode* node)
}
const size_t node_size = serd_node_total_size(node);
- NodesEntry* const entry = new_entry(node_size);
+ NodesEntry* const entry = new_entry(nodes->allocator, node_size);
+ if (!entry) {
+ return NULL;
+ }
memcpy(&entry->node, node, node_size);
@@ -273,6 +286,10 @@ serd_nodes_get(const SerdNodes* const nodes, const SerdNode* const node)
static const SerdNode*
serd_nodes_manage_entry(SerdNodes* const nodes, NodesEntry* const entry)
{
+ if (!entry) {
+ return NULL;
+ }
+
const SerdNode* const node = &entry->node;
const ZixHashInsertPlan plan = zix_hash_plan_insert(nodes->hash, node);
NodesEntry* const existing = zix_hash_record_at(nodes->hash, plan);
@@ -280,12 +297,16 @@ serd_nodes_manage_entry(SerdNodes* const nodes, NodesEntry* const entry)
assert(serd_node_equals(&existing->node, node));
assert(nodes_hash(&existing->node) == plan.code);
++existing->head.refs;
- serd_free_aligned(entry);
+ serd_aaligned_free(nodes->allocator, entry);
return &existing->node;
}
- // Insert the entry (blissfully ignoring a failed hash size increase)
- zix_hash_insert_at(nodes->hash, plan, entry);
+ // Insert the entry (or fail and free it on a failed hash size increase)
+ if (zix_hash_insert_at(nodes->hash, plan, entry)) {
+ serd_aaligned_free(nodes->allocator, entry);
+ return NULL;
+ }
+
assert(nodes_hash(&entry->node) == plan.code);
return &entry->node;
}
@@ -309,22 +330,27 @@ serd_nodes_token(SerdNodes* const nodes,
const size_t padded_length = serd_node_pad_length(string.len);
const size_t node_size = sizeof(SerdNode) + padded_length;
- NodesEntry* const entry = new_entry(node_size);
+ NodesEntry* const entry = new_entry(nodes->allocator, node_size);
SerdNode* const node = entry ? &entry->node : NULL;
+ if (!node) {
+ return NULL;
+ }
- if (node) {
- // Construct the token directly into the node in the new entry
- const SerdWriteResult r =
- serd_node_construct_token(node_size, &entry->node, type, string);
+ // Construct the token directly into the node in the new entry
+ const SerdWriteResult r =
+ serd_node_construct_token(node_size, &entry->node, type, string);
- assert(!r.status); // Never fails with sufficient space
- (void)r;
+ assert(!r.status); // Never fails with sufficient space
+ (void)r;
- // Insert the entry (blissfully ignoring a failed hash size increase)
- zix_hash_insert_at(nodes->hash, plan, entry);
- assert(nodes_hash(node) == code);
+ // Insert the entry (blissfully ignoring a failed hash size increase)
+ if (zix_hash_insert_at(nodes->hash, plan, entry)) {
+ serd_aaligned_free(nodes->allocator, entry);
+ return NULL;
}
+ assert(nodes_hash(node) == code);
+
return node;
}
@@ -357,7 +383,7 @@ serd_nodes_literal(SerdNodes* const nodes,
}
// Allocate a new entry with enough space for the node
- NodesEntry* const entry = new_entry(r.count);
+ NodesEntry* const entry = new_entry(nodes->allocator, r.count);
SerdNode* const node = entry ? &entry->node : NULL;
if (node) {
@@ -460,13 +486,15 @@ serd_nodes_base64(SerdNodes* const nodes,
serd_node_construct_base64(0, NULL, value_size, value, datatype);
// Allocate a new entry to and construct the node into it
- NodesEntry* const entry = new_entry(r.count);
+ NodesEntry* const entry = new_entry(nodes->allocator, r.count);
+ if (entry) {
+ r = serd_node_construct_base64(
+ r.count, &entry->node, value_size, value, datatype);
- r = serd_node_construct_base64(
- r.count, &entry->node, value_size, value, datatype);
+ assert(!r.status);
+ (void)r;
+ }
- assert(!r.status);
- (void)r;
return serd_nodes_manage_entry(nodes, entry);
}
@@ -490,11 +518,12 @@ serd_nodes_parsed_uri(SerdNodes* const nodes, const SerdURIView uri)
assert(r.status == SERD_OVERFLOW); // Currently no other errors
// Allocate a new entry to write the URI node into
- NodesEntry* const entry = new_entry(r.count);
-
- r = serd_node_construct_uri(r.count, &entry->node, uri);
- assert(!r.status);
- (void)r;
+ NodesEntry* const entry = new_entry(nodes->allocator, r.count);
+ if (entry) {
+ r = serd_node_construct_uri(r.count, &entry->node, uri);
+ assert(!r.status);
+ (void)r;
+ }
return serd_nodes_manage_entry(nodes, entry);
}
@@ -522,6 +551,6 @@ serd_nodes_deref(SerdNodes* const nodes, const SerdNode* const node)
NodesEntry* removed = NULL;
zix_hash_erase(nodes->hash, i, &removed);
assert(removed == entry);
- serd_free_aligned(removed);
+ serd_aaligned_free(nodes->allocator, removed);
}
}
diff --git a/src/reader.c b/src/reader.c
index 985a9fd2..46390192 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -17,19 +17,18 @@
#include "reader.h"
#include "byte_source.h"
+#include "memory.h"
#include "namespaces.h"
#include "node.h"
#include "read_nquads.h"
#include "read_ntriples.h"
#include "stack.h"
#include "statement.h"
-#include "system.h"
#include "world.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
static SerdStatus
@@ -247,17 +246,25 @@ serd_reader_new(SerdWorld* const world,
return NULL;
}
- SerdReader* me = (SerdReader*)calloc(1, sizeof(SerdReader));
+ SerdReader* me = (SerdReader*)serd_wcalloc(world, 1, sizeof(SerdReader));
+ if (!me) {
+ return NULL;
+ }
me->world = world;
me->sink = sink;
me->env = env;
- me->stack = serd_stack_new(stack_size, serd_node_align);
+ me->stack = serd_stack_new(world->allocator, stack_size, serd_node_align);
me->syntax = syntax;
me->flags = flags;
me->next_id = 1;
me->strict = !(flags & SERD_READ_LAX);
+ if (!me->stack.buf) {
+ serd_wfree(world, me);
+ return NULL;
+ }
+
// Reserve a bit of space at the end of the stack to zero pad nodes
me->stack.buf_size -= serd_node_align;
@@ -288,8 +295,8 @@ serd_reader_free(SerdReader* const reader)
serd_reader_finish(reader);
- serd_free_aligned(reader->stack.buf);
- free(reader);
+ serd_aaligned_free(reader->world->allocator, reader->stack.buf);
+ serd_wfree(reader->world, reader);
}
static SerdStatus
@@ -318,11 +325,17 @@ serd_reader_start(SerdReader* const reader,
assert(reader);
assert(input);
+ if (!block_size || !input->stream) {
+ return SERD_BAD_ARG;
+ }
+
serd_reader_finish(reader);
- reader->source = serd_byte_source_new_input(input, input_name, block_size);
+ assert(!reader->source);
+ reader->source = serd_byte_source_new_input(
+ reader->world->allocator, input, input_name, block_size);
- return reader->source ? SERD_SUCCESS : SERD_BAD_ARG;
+ return reader->source ? SERD_SUCCESS : SERD_BAD_ALLOC;
}
static SerdStatus
@@ -361,7 +374,7 @@ serd_reader_finish(SerdReader* const reader)
{
assert(reader);
- serd_byte_source_free(reader->source);
+ serd_byte_source_free(reader->world->allocator, reader->source);
reader->source = NULL;
return SERD_SUCCESS;
}
diff --git a/src/sink.c b/src/sink.c
index 3e22f11c..f9c46f09 100644
--- a/src/sink.c
+++ b/src/sink.c
@@ -16,6 +16,7 @@
#include "sink.h"
+#include "memory.h"
#include "statement.h"
#include "serd/serd.h"
@@ -29,13 +30,16 @@ serd_sink_new(const SerdWorld* const world,
SerdEventFunc event_func,
SerdFreeFunc free_handle)
{
- (void)world;
+ assert(world);
- SerdSink* sink = (SerdSink*)calloc(1, sizeof(SerdSink));
+ SerdSink* sink = (SerdSink*)serd_wcalloc(world, 1, sizeof(SerdSink));
- sink->handle = handle;
- sink->on_event = event_func;
- sink->free_handle = free_handle;
+ if (sink) {
+ sink->world = world;
+ sink->handle = handle;
+ sink->on_event = event_func;
+ sink->free_handle = free_handle;
+ }
return sink;
}
@@ -48,7 +52,7 @@ serd_sink_free(SerdSink* sink)
sink->free_handle(sink->handle);
}
- free(sink);
+ serd_wfree(sink->world, sink);
}
}
diff --git a/src/sink.h b/src/sink.h
index 6e8dffe3..1f7df534 100644
--- a/src/sink.h
+++ b/src/sink.h
@@ -23,9 +23,10 @@
An interface that receives a stream of RDF data.
*/
struct SerdSinkImpl {
- void* handle;
- SerdFreeFunc free_handle;
- SerdEventFunc on_event;
+ const SerdWorld* world;
+ void* handle;
+ SerdFreeFunc free_handle;
+ SerdEventFunc on_event;
};
#endif // SERD_SINK_H
diff --git a/src/stack.h b/src/stack.h
index f94730c3..3b53ac88 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -17,7 +17,9 @@
#ifndef SERD_STACK_H
#define SERD_STACK_H
-#include "system.h"
+#include "memory.h"
+
+#include "serd/serd.h"
#include <assert.h>
#include <stdbool.h>
@@ -38,12 +40,12 @@ typedef struct {
#define SERD_STACK_BOTTOM sizeof(void*)
static inline SerdStack
-serd_stack_new(size_t size, size_t align)
+serd_stack_new(SerdAllocator* const allocator, size_t size, size_t align)
{
const size_t aligned_size = (size + (align - 1)) / align * align;
SerdStack stack;
- stack.buf = (char*)serd_calloc_aligned(align, aligned_size);
+ stack.buf = (char*)serd_aaligned_calloc(allocator, align, aligned_size);
stack.buf_size = size;
stack.size = SERD_STACK_BOTTOM;
return stack;
@@ -62,9 +64,9 @@ serd_stack_is_empty(const SerdStack* stack)
}
static inline void
-serd_stack_free(SerdStack* stack)
+serd_stack_free(SerdAllocator* const allocator, SerdStack* stack)
{
- serd_free_aligned(stack->buf);
+ serd_aaligned_free(allocator, stack->buf);
stack->buf = NULL;
stack->buf_size = 0;
stack->size = 0;
diff --git a/src/statement.c b/src/statement.c
index 8ca0d8ee..d407ad76 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -17,6 +17,7 @@
#include "statement.h"
#include "caret.h"
+#include "memory.h"
#include "node.h"
#include <assert.h>
@@ -38,13 +39,16 @@ serd_statement_is_valid(const SerdNode* const subject,
const SerdNode* const object,
const SerdNode* const graph)
{
- return subject && predicate && object && is_resource(subject) &&
- is_resource(predicate) && serd_node_type(predicate) != SERD_BLANK &&
+ (void)object;
+
+ return is_resource(subject) && is_resource(predicate) &&
+ serd_node_type(predicate) != SERD_BLANK &&
(!graph || is_resource(graph));
}
SerdStatement*
-serd_statement_new(const SerdNode* const s,
+serd_statement_new(SerdAllocator* const allocator,
+ const SerdNode* const s,
const SerdNode* const p,
const SerdNode* const o,
const SerdNode* const g,
@@ -58,39 +62,62 @@ serd_statement_new(const SerdNode* const s,
return NULL;
}
- SerdStatement* statement = (SerdStatement*)malloc(sizeof(SerdStatement));
+ SerdStatement* statement =
+ (SerdStatement*)serd_amalloc(allocator, sizeof(SerdStatement));
+
if (statement) {
statement->nodes[0] = s;
statement->nodes[1] = p;
statement->nodes[2] = o;
statement->nodes[3] = g;
- statement->caret = serd_caret_copy(caret);
+ statement->caret = NULL;
+
+ if (caret) {
+ if (!(statement->caret = serd_caret_copy(allocator, caret))) {
+ serd_afree(allocator, statement);
+ return NULL;
+ }
+ }
}
+
return statement;
}
SerdStatement*
-serd_statement_copy(const SerdStatement* const statement)
+serd_statement_copy(SerdAllocator* const allocator,
+ const SerdStatement* const statement)
{
if (!statement) {
return NULL;
}
- SerdStatement* copy = (SerdStatement*)malloc(sizeof(SerdStatement));
- memcpy(copy, statement, sizeof(SerdStatement));
- if (statement->caret) {
- copy->caret = (SerdCaret*)malloc(sizeof(SerdCaret));
- memcpy(copy->caret, statement->caret, sizeof(SerdCaret));
+ SerdStatement* copy =
+ (SerdStatement*)serd_amalloc(allocator, sizeof(SerdStatement));
+
+ if (copy) {
+ memcpy(copy, statement, sizeof(SerdStatement));
+
+ if (statement->caret) {
+ if (!(copy->caret =
+ (SerdCaret*)serd_amalloc(allocator, sizeof(SerdCaret)))) {
+ serd_afree(allocator, copy);
+ return NULL;
+ }
+
+ memcpy(copy->caret, statement->caret, sizeof(SerdCaret));
+ }
}
+
return copy;
}
void
-serd_statement_free(SerdStatement* const statement)
+serd_statement_free(SerdAllocator* const allocator,
+ SerdStatement* const statement)
{
if (statement) {
- free(statement->caret);
- free(statement);
+ serd_afree(allocator, statement->caret);
+ serd_afree(allocator, statement);
}
}
diff --git a/src/string.c b/src/string.c
index 136cb09c..1b9bb64a 100644
--- a/src/string.c
+++ b/src/string.c
@@ -14,6 +14,7 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "memory.h"
#include "string_utils.h"
#include "serd/serd.h"
@@ -22,9 +23,9 @@
#include <stdlib.h>
void
-serd_free(void* const ptr)
+serd_free(SerdAllocator* const allocator, void* const ptr)
{
- free(ptr);
+ serd_afree(allocator, ptr);
}
const char*
diff --git a/src/system.c b/src/system.c
index cb6ceb6d..2ebca97c 100644
--- a/src/system.c
+++ b/src/system.c
@@ -16,17 +16,23 @@
#include "system.h"
-#include "serd/serd.h"
+#include "memory.h"
#include "serd_config.h"
+#include "serd/serd.h"
+
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN 1
# include <malloc.h>
# include <windows.h>
+#else
+# include <limits.h>
+# ifndef PATH_MAX
+# include <unistd.h>
+# endif
#endif
#include <assert.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -44,72 +50,52 @@ serd_system_strerror(const int errnum, char* const buf, const size_t buflen)
#endif
}
-void*
-serd_malloc_aligned(const size_t alignment, const size_t size)
-{
-#if defined(_WIN32)
- return _aligned_malloc(size, alignment);
-#elif USE_POSIX_MEMALIGN
- void* ptr = NULL;
- const int ret = posix_memalign(&ptr, alignment, size);
- return ret ? NULL : ptr;
-#else
- (void)alignment;
- return malloc(size);
-#endif
-}
-
-void*
-serd_calloc_aligned(const size_t alignment, const size_t size)
-{
-#if defined(_WIN32) || defined(USE_POSIX_MEMALIGN)
- void* const ptr = serd_malloc_aligned(alignment, size);
- if (ptr) {
- memset(ptr, 0, size);
- }
- return ptr;
-#else
- (void)alignment;
- return calloc(1, size);
-#endif
-}
-
-void*
-serd_allocate_buffer(const size_t size)
-{
- return serd_malloc_aligned(SERD_PAGE_SIZE, size);
-}
-
-void
-serd_free_aligned(void* const ptr)
-{
-#ifdef _WIN32
- _aligned_free(ptr);
-#else
- free(ptr);
-#endif
-}
-
char*
-serd_canonical_path(const char* const path)
+serd_canonical_path(SerdAllocator* const allocator, const char* const path)
{
assert(path);
-#ifdef _WIN32
+#if defined(_WIN32)
+ // Microsoft got this one right: measure, allocate, resolve
const DWORD size = GetFullPathName(path, 0, NULL, NULL);
if (size == 0) {
return NULL;
}
- char* const out = (char*)calloc(size, 1);
- const DWORD ret = GetFullPathName(path, MAX_PATH, out, NULL);
- if (ret == 0 || ret >= size) {
- free(out);
+ char* const out = (char*)serd_acalloc(allocator, size, 1);
+ if (out) {
+ const DWORD ret = GetFullPathName(path, MAX_PATH, out, NULL);
+ if (ret == 0 || ret >= size) {
+ serd_afree(allocator, out);
+ return NULL;
+ }
+ }
+
+ return out;
+
+#elif defined(PATH_MAX)
+ // Some POSIX systems have a static PATH_MAX so we can resolve on the stack
+ char result[PATH_MAX] = {0};
+ char* resolved_path = realpath(path, result);
+ if (!resolved_path) {
return NULL;
}
+ const size_t len = strlen(resolved_path);
+ char* const out = (char*)serd_acalloc(allocator, len + 1, 1);
+ if (out) {
+ memcpy(out, resolved_path, len + 1);
+ }
return out;
+
#else
- return path ? realpath(path, NULL) : NULL;
+ // Others don't so we have to query PATH_MAX at runtime to allocate the result
+ long path_max = pathconf(path, _PC_PATH_MAX);
+ if (path_max <= 0) {
+ path_max = SERD_PAGE_SIZE;
+ }
+
+ char* const out = (char*)serd_acalloc(allocator, path_max, 1);
+ return out ? realpath(path, out) : NULL;
#endif
}
diff --git a/src/system.h b/src/system.h
index 184e1aae..9ef0b142 100644
--- a/src/system.h
+++ b/src/system.h
@@ -17,9 +17,7 @@
#ifndef SERD_SYSTEM_H
#define SERD_SYSTEM_H
-#include "attributes.h"
-
-#include <stdio.h>
+#include <stddef.h>
#define SERD_PAGE_SIZE 4096
@@ -27,23 +25,4 @@
int
serd_system_strerror(int errnum, char* buf, size_t buflen);
-/// Allocate a buffer aligned to `alignment` bytes
-SERD_I_MALLOC_FUNC
-void*
-serd_malloc_aligned(size_t alignment, size_t size);
-
-/// Allocate a zeroed buffer aligned to `alignment` bytes
-SERD_I_MALLOC_FUNC
-void*
-serd_calloc_aligned(size_t alignment, size_t size);
-
-/// Allocate an aligned buffer for I/O
-SERD_I_MALLOC_FUNC
-void*
-serd_allocate_buffer(size_t size);
-
-/// Free a buffer allocated with an aligned allocation function
-void
-serd_free_aligned(void* ptr);
-
#endif // SERD_SYSTEM_H
diff --git a/src/uri.c b/src/uri.c
index bbd53ca3..fad1d188 100644
--- a/src/uri.c
+++ b/src/uri.c
@@ -14,6 +14,7 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "memory.h"
#include "string_utils.h"
#include "uri_utils.h"
@@ -26,7 +27,9 @@
#include <string.h>
char*
-serd_parse_file_uri(const char* const uri, char** const hostname)
+serd_parse_file_uri(SerdAllocator* const allocator,
+ const char* const uri,
+ char** const hostname)
{
assert(uri);
@@ -46,7 +49,10 @@ serd_parse_file_uri(const char* const uri, char** const hostname)
if (hostname) {
const size_t len = (size_t)(path - auth);
- *hostname = (char*)calloc(len + 1, 1);
+ if (!(*hostname = (char*)serd_acalloc(allocator, len + 1, 1))) {
+ return NULL;
+ }
+
memcpy(*hostname, auth, len);
}
}
@@ -56,26 +62,39 @@ serd_parse_file_uri(const char* const uri, char** const hostname)
++path;
}
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {allocator, NULL, 0};
for (const char* s = path; *s; ++s) {
if (*s == '%') {
if (*(s + 1) == '%') {
- serd_buffer_write("%", 1, 1, &buffer);
+ if (serd_buffer_write("%", 1, 1, &buffer) != 1) {
+ serd_afree(allocator, buffer.buf);
+ return NULL;
+ }
+
++s;
} else if (is_hexdig(*(s + 1)) && is_hexdig(*(s + 2))) {
const char code[3] = {*(s + 1), *(s + 2), 0};
const char c = (char)strtoul(code, NULL, 16);
- serd_buffer_write(&c, 1, 1, &buffer);
+ if (serd_buffer_write(&c, 1, 1, &buffer) != 1) {
+ serd_afree(allocator, buffer.buf);
+ return NULL;
+ }
+
s += 2;
} else {
s += 2; // Junk escape, ignore
}
- } else {
- serd_buffer_write(s, 1, 1, &buffer);
+ } else if (serd_buffer_write(s, 1, 1, &buffer) != 1) {
+ serd_afree(allocator, buffer.buf);
+ return NULL;
}
}
- serd_buffer_close(&buffer);
+ if (serd_buffer_close(&buffer)) {
+ serd_afree(allocator, buffer.buf);
+ return NULL;
+ }
+
return (char*)buffer.buf;
}
diff --git a/src/world.c b/src/world.c
index 39eeaec3..5b5cb900 100644
--- a/src/world.c
+++ b/src/world.c
@@ -16,6 +16,7 @@
#include "world.h"
+#include "memory.h"
#include "namespaces.h"
#include "node.h"
#include "serd_config.h"
@@ -67,27 +68,46 @@ terminal_supports_color(FILE* const stream)
}
SerdWorld*
-serd_world_new(void)
+serd_world_new(SerdAllocator* const allocator)
{
- SerdWorld* world = (SerdWorld*)calloc(1, sizeof(SerdWorld));
- SerdNodes* nodes = serd_nodes_new();
-
- const SerdStringView rdf_first = SERD_STRING(NS_RDF "first");
- const SerdStringView rdf_nil = SERD_STRING(NS_RDF "nil");
- const SerdStringView rdf_rest = SERD_STRING(NS_RDF "rest");
- const SerdStringView rdf_type = SERD_STRING(NS_RDF "type");
- const SerdStringView xsd_boolean = SERD_STRING(NS_XSD "boolean");
- const SerdStringView xsd_decimal = SERD_STRING(NS_XSD "decimal");
- const SerdStringView xsd_integer = SERD_STRING(NS_XSD "integer");
-
- world->nodes = nodes;
- world->rdf_first = serd_nodes_uri(nodes, rdf_first);
- world->rdf_nil = serd_nodes_uri(nodes, rdf_nil);
- world->rdf_rest = serd_nodes_uri(nodes, rdf_rest);
- world->rdf_type = serd_nodes_uri(nodes, rdf_type);
- world->xsd_boolean = serd_nodes_uri(nodes, xsd_boolean);
- world->xsd_decimal = serd_nodes_uri(nodes, xsd_decimal);
- world->xsd_integer = serd_nodes_uri(nodes, xsd_integer);
+ SerdAllocator* const actual =
+ allocator ? allocator : serd_default_allocator();
+
+ SerdWorld* const world =
+ (SerdWorld*)serd_acalloc(actual, 1, sizeof(SerdWorld));
+
+ if (!world) {
+ return NULL;
+ }
+
+ SerdNodes* const nodes = serd_nodes_new(actual);
+ if (!nodes) {
+ serd_afree(actual, world);
+ return NULL;
+ }
+
+ static const SerdStringView rdf_first = SERD_STRING(NS_RDF "first");
+ static const SerdStringView rdf_nil = SERD_STRING(NS_RDF "nil");
+ static const SerdStringView rdf_rest = SERD_STRING(NS_RDF "rest");
+ static const SerdStringView rdf_type = SERD_STRING(NS_RDF "type");
+ static const SerdStringView xsd_boolean = SERD_STRING(NS_XSD "boolean");
+ static const SerdStringView xsd_decimal = SERD_STRING(NS_XSD "decimal");
+ static const SerdStringView xsd_integer = SERD_STRING(NS_XSD "integer");
+
+ world->allocator = actual;
+ world->nodes = nodes;
+
+ if (!(world->rdf_first = serd_nodes_uri(nodes, rdf_first)) ||
+ !(world->rdf_nil = serd_nodes_uri(nodes, rdf_nil)) ||
+ !(world->rdf_rest = serd_nodes_uri(nodes, rdf_rest)) ||
+ !(world->rdf_type = serd_nodes_uri(nodes, rdf_type)) ||
+ !(world->xsd_boolean = serd_nodes_uri(nodes, xsd_boolean)) ||
+ !(world->xsd_decimal = serd_nodes_uri(nodes, xsd_decimal)) ||
+ !(world->xsd_integer = serd_nodes_uri(nodes, xsd_integer))) {
+ serd_nodes_free(nodes);
+ serd_afree(actual, world);
+ return NULL;
+ }
serd_node_construct_token(sizeof(world->blank),
&world->blank,
@@ -104,10 +124,18 @@ serd_world_free(SerdWorld* const world)
{
if (world) {
serd_nodes_free(world->nodes);
- free(world);
+ serd_afree(world->allocator, world);
}
}
+SerdAllocator*
+serd_world_allocator(const SerdWorld* const world)
+{
+ assert(world);
+ assert(world->allocator);
+ return world->allocator;
+}
+
SerdNodes*
serd_world_nodes(SerdWorld* const world)
{
diff --git a/src/world.h b/src/world.h
index f1124023..5e8adaeb 100644
--- a/src/world.h
+++ b/src/world.h
@@ -43,6 +43,8 @@ struct SerdWorldImpl {
uint32_t next_blank_id;
uint32_t next_document_id;
+ SerdAllocator* allocator;
+
bool stderr_color;
};
diff --git a/src/writer.c b/src/writer.c
index e19518e4..db69b9ed 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -18,6 +18,7 @@
#include "block_dumper.h"
#include "env.h"
+#include "memory.h"
#include "node.h"
#include "sink.h"
#include "string_utils.h"
@@ -35,7 +36,6 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#ifndef _MSC_VER
@@ -176,9 +176,9 @@ supports_uriref(const SerdWriter* writer)
static SerdStatus
free_context(SerdWriter* writer)
{
- serd_node_free(writer->context.graph);
- serd_node_free(writer->context.subject);
- serd_node_free(writer->context.predicate);
+ serd_node_free(writer->world->allocator, writer->context.graph);
+ serd_node_free(writer->world->allocator, writer->context.subject);
+ serd_node_free(writer->world->allocator, writer->context.predicate);
return SERD_SUCCESS;
}
@@ -196,9 +196,9 @@ push_context(SerdWriter* const writer,
const WriteContext new_context = {type,
flags,
- serd_node_copy(g),
- serd_node_copy(s),
- serd_node_copy(p),
+ serd_node_copy(writer->world->allocator, g),
+ serd_node_copy(writer->world->allocator, s),
+ serd_node_copy(writer->world->allocator, p),
false};
writer->anon_stack[writer->anon_stack_size++] = writer->context;
@@ -951,8 +951,9 @@ write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred)
SerdStatus st = SERD_SUCCESS;
TRY(st, write_node(writer, pred, SERD_PREDICATE, flags));
TRY(st, write_sep(writer, flags, SEP_P_O));
- serd_node_set(&writer->context.predicate, pred);
- return st;
+
+ return serd_node_set(
+ writer->world->allocator, &writer->context.predicate, pred);
}
SERD_WARN_UNUSED_RESULT static SerdStatus
@@ -1028,12 +1029,10 @@ update_abbreviation_context(SerdWriter* const writer,
SerdStatus st = SERD_SUCCESS;
// Push context for list or anonymous subject if necessary
- if (!st) {
- if (flags & SERD_ANON_S) {
- st = push_context(writer, CTX_BLANK, flags, graph, subject, predicate);
- } else if (flags & SERD_LIST_S) {
- st = push_context(writer, CTX_LIST, flags, graph, subject, NULL);
- }
+ if (flags & SERD_ANON_S) {
+ st = push_context(writer, CTX_BLANK, flags, graph, subject, predicate);
+ } else if (flags & SERD_LIST_S) {
+ st = push_context(writer, CTX_LIST, flags, graph, subject, NULL);
}
// Push context for list or anonymous object if necessary
@@ -1048,9 +1047,10 @@ update_abbreviation_context(SerdWriter* const writer,
// Update current context to this statement if this isn't a new context
if (!st) {
if (!(flags & (SERD_ANON_S | SERD_LIST_S | SERD_ANON_O | SERD_LIST_O))) {
- serd_node_set(&writer->context.graph, graph);
- serd_node_set(&writer->context.subject, subject);
- serd_node_set(&writer->context.predicate, predicate);
+ SerdAllocator* const allocator = writer->world->allocator;
+ TRY(st, serd_node_set(allocator, &writer->context.graph, graph));
+ TRY(st, serd_node_set(allocator, &writer->context.subject, subject));
+ TRY(st, serd_node_set(allocator, &writer->context.predicate, predicate));
}
}
@@ -1126,11 +1126,16 @@ write_turtle_trig_statement(SerdWriter* const writer,
}
// Write new subject
+
if (!ctx(writer, SERD_GRAPH)) {
TRY(st, write_top_level_sep(writer));
}
+
reset_context(writer, false);
- serd_node_set(&writer->context.subject, subject);
+ TRY(st,
+ serd_node_set(
+ writer->world->allocator, &writer->context.subject, subject));
+
TRY(st, write_node(writer, subject, SERD_SUBJECT, flags));
// Write appropriate S,P separator based on the context
@@ -1187,7 +1192,7 @@ write_trig_statement(SerdWriter* const writer,
if (graph) {
TRY(st, write_node(writer, graph, SERD_GRAPH, flags));
TRY(st, write_sep(writer, flags, SEP_GRAPH_BEGIN));
- serd_node_set(&writer->context.graph, graph);
+ serd_node_set(writer->world->allocator, &writer->context.graph, graph);
}
}
@@ -1327,13 +1332,18 @@ serd_writer_new(SerdWorld* world,
assert(env);
assert(output);
- SerdBlockDumper dumper = {NULL, NULL, 0u, 0u};
- if (serd_block_dumper_open(&dumper, output, block_size)) {
+ SerdBlockDumper dumper = {world->allocator, NULL, NULL, 0u, 0u};
+ if (serd_block_dumper_open(world, &dumper, output, block_size)) {
return NULL;
}
const WriteContext context = WRITE_CONTEXT_NULL;
- SerdWriter* writer = (SerdWriter*)calloc(1, sizeof(SerdWriter));
+
+ SerdWriter* writer = (SerdWriter*)serd_wcalloc(world, 1, sizeof(SerdWriter));
+ if (!writer) {
+ serd_block_dumper_close(&dumper);
+ return NULL;
+ }
writer->world = world;
writer->syntax = syntax;
@@ -1345,9 +1355,15 @@ serd_writer_new(SerdWorld* world,
writer->context = context;
writer->empty = true;
- writer->anon_stack =
- (WriteContext*)calloc(anon_stack_capacity, sizeof(WriteContext));
+ writer->anon_stack = (WriteContext*)serd_wcalloc(
+ world, anon_stack_capacity, sizeof(WriteContext));
+
+ if (!writer->anon_stack) {
+ serd_wfree(world, writer);
+ return NULL;
+ }
+ writer->iface.world = world;
writer->iface.handle = writer;
writer->iface.on_event = (SerdEventFunc)serd_writer_on_event;
@@ -1386,12 +1402,12 @@ serd_writer_set_root_uri(SerdWriter* writer, const SerdStringView uri)
{
assert(writer);
- serd_node_free(writer->root_node);
+ serd_node_free(writer->world->allocator, writer->root_node);
writer->root_node = NULL;
writer->root_uri = SERD_URI_NULL;
if (uri.len) {
- writer->root_node = serd_new_uri(uri);
+ writer->root_node = serd_new_uri(writer->world->allocator, uri);
writer->root_uri = serd_node_uri_view(writer->root_node);
}
@@ -1439,9 +1455,9 @@ serd_writer_free(SerdWriter* writer)
serd_writer_finish(writer);
serd_block_dumper_close(&writer->output);
- free(writer->anon_stack);
- serd_node_free(writer->root_node);
- free(writer);
+ serd_wfree(writer->world, writer->anon_stack);
+ serd_node_free(writer->world->allocator, writer->root_node);
+ serd_wfree(writer->world, writer);
}
const SerdSink*
diff --git a/test/.clang-tidy b/test/.clang-tidy
index d8601571..fa6f671c 100644
--- a/test/.clang-tidy
+++ b/test/.clang-tidy
@@ -13,6 +13,7 @@ Checks: >
-clang-analyzer-valist.Uninitialized,
-cppcoreguidelines-avoid-non-const-global-variables,
-hicpp-signed-bitwise,
+ -llvm-header-guard,
-llvmlibc-*,
-readability-function-cognitive-complexity,
WarningsAsErrors: '*'
diff --git a/test/failing_allocator.c b/test/failing_allocator.c
new file mode 100644
index 00000000..e04f3eaf
--- /dev/null
+++ b/test/failing_allocator.c
@@ -0,0 +1,121 @@
+/*
+ Copyright 2021 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 "failing_allocator.h"
+
+#include "serd/serd.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+static bool
+attempt(SerdFailingAllocator* const allocator)
+{
+ ++allocator->n_allocations;
+
+ if (!allocator->n_remaining) {
+ return false;
+ }
+
+ --allocator->n_remaining;
+ return true;
+}
+
+SERD_MALLOC_FUNC
+static void*
+serd_failing_malloc(SerdAllocator* const allocator, const size_t size)
+{
+ SerdFailingAllocator* const state = (SerdFailingAllocator*)allocator;
+ SerdAllocator* const base = serd_default_allocator();
+
+ return attempt(state) ? base->malloc(base, size) : NULL;
+}
+
+SERD_MALLOC_FUNC
+static void*
+serd_failing_calloc(SerdAllocator* const allocator,
+ const size_t nmemb,
+ const size_t size)
+{
+ SerdFailingAllocator* const state = (SerdFailingAllocator*)allocator;
+ SerdAllocator* const base = serd_default_allocator();
+
+ return attempt(state) ? base->calloc(base, nmemb, size) : NULL;
+}
+
+static void*
+serd_failing_realloc(SerdAllocator* const allocator,
+ void* const ptr,
+ const size_t size)
+{
+ SerdFailingAllocator* const state = (SerdFailingAllocator*)allocator;
+ SerdAllocator* const base = serd_default_allocator();
+
+ return attempt(state) ? base->realloc(base, ptr, size) : NULL;
+}
+
+static void
+serd_failing_free(SerdAllocator* const allocator, void* const ptr)
+{
+ (void)allocator;
+
+ SerdAllocator* const base = serd_default_allocator();
+
+ base->free(base, ptr);
+}
+
+SERD_MALLOC_FUNC
+static void*
+serd_failing_aligned_alloc(SerdAllocator* const allocator,
+ const size_t alignment,
+ const size_t size)
+{
+ SerdFailingAllocator* const state = (SerdFailingAllocator*)allocator;
+ SerdAllocator* const base = serd_default_allocator();
+
+ return attempt(state) ? base->aligned_alloc(base, alignment, size) : NULL;
+}
+
+static void
+serd_failing_aligned_free(SerdAllocator* const allocator, void* const ptr)
+{
+ (void)allocator;
+
+ SerdAllocator* const base = serd_default_allocator();
+
+ base->aligned_free(base, ptr);
+}
+
+SERD_CONST_FUNC
+SerdFailingAllocator
+serd_failing_allocator(void)
+{
+ SerdFailingAllocator failing_allocator = {
+ {
+ serd_failing_malloc,
+ serd_failing_calloc,
+ serd_failing_realloc,
+ serd_failing_free,
+ serd_failing_aligned_alloc,
+ serd_failing_aligned_free,
+ },
+ 0,
+ SIZE_MAX,
+ };
+
+ return failing_allocator;
+}
diff --git a/test/failing_allocator.h b/test/failing_allocator.h
new file mode 100644
index 00000000..4539ede8
--- /dev/null
+++ b/test/failing_allocator.h
@@ -0,0 +1,34 @@
+/*
+ Copyright 2021 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_FAILING_ALLOCATOR_H
+#define SERD_FAILING_ALLOCATOR_H
+
+#include "serd/serd.h"
+
+#include <stddef.h>
+
+/// An allocator that fails after some number of successes for testing
+typedef struct {
+ SerdAllocator base; ///< Base allocator instance
+ size_t n_allocations; ///< Number of attempted allocations
+ size_t n_remaining; ///< Number of remaining successful allocations
+} SerdFailingAllocator;
+
+SerdFailingAllocator
+serd_failing_allocator(void);
+
+#endif // SERD_FAILING_ALLOCATOR_H
diff --git a/test/meson.build b/test/meson.build
index a25ac6de..eae5d42e 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -8,9 +8,11 @@ run_sort_suite = find_program('run_sort_suite.py')
wrapper = meson.get_cross_property('exe_wrapper', '')
unit_tests = [
+ 'canon',
'caret',
'cursor',
'env',
+ 'filter',
'free_null',
'log',
'model',
@@ -40,7 +42,7 @@ endif
foreach unit : unit_tests
test(unit,
executable('test_@0@'.format(unit),
- 'test_@0@.c'.format(unit),
+ files('test_@0@.c'.format(unit), 'failing_allocator.c'),
c_args: c_warnings + platform_args + prog_args,
dependencies: serd_dep),
env: test_env,
diff --git a/test/test_canon.c b/test/test_canon.c
new file mode 100644
index 00000000..1e4dc24c
--- /dev/null
+++ b/test/test_canon.c
@@ -0,0 +1,108 @@
+/*
+ Copyright 2021 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 "failing_allocator.h"
+#include "serd/serd.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+static SerdStatus
+ignore_event(void* handle, const SerdEvent* event)
+{
+ (void)handle;
+ (void)event;
+ return SERD_SUCCESS;
+}
+
+static void
+test_new_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ SerdNodes* const nodes = serd_nodes_new(&allocator.base);
+
+ SerdSink* target = serd_sink_new(world, NULL, ignore_event, NULL);
+ const size_t n_setup_allocs = allocator.n_allocations;
+
+ // Successfully allocate a canon to count the number of allocations
+ SerdSink* canon = serd_canon_new(world, target, 0u);
+ assert(canon);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_setup_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_canon_new(world, target, 0u));
+ }
+
+ serd_sink_free(canon);
+ serd_sink_free(target);
+ serd_nodes_free(nodes);
+ serd_world_free(world);
+}
+
+static void
+test_write_failed_alloc(void)
+{
+ static const SerdStringView s_string = SERD_STRING("http://example.org/s");
+ static const SerdStringView p_string = SERD_STRING("http://example.org/p");
+ static const SerdStringView o_string = SERD_STRING("012.340");
+ static const SerdStringView xsd_float =
+ SERD_STRING("http://www.w3.org/2001/XMLSchema#float");
+
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ SerdNodes* const nodes = serd_nodes_new(&allocator.base);
+ const SerdNode* const s = serd_nodes_uri(nodes, s_string);
+ const SerdNode* const p = serd_nodes_uri(nodes, p_string);
+ const SerdNode* const o =
+ serd_nodes_literal(nodes, o_string, SERD_HAS_DATATYPE, xsd_float);
+
+ SerdSink* target = serd_sink_new(world, NULL, ignore_event, NULL);
+ SerdSink* canon = serd_canon_new(world, target, 0u);
+ const size_t n_setup_allocs = allocator.n_allocations;
+
+ // Successfully write statement to count the number of allocations
+ assert(canon);
+ assert(!serd_sink_write(canon, 0u, s, p, o, NULL));
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_setup_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+
+ const SerdStatus st = serd_sink_write(canon, 0u, s, p, o, NULL);
+ assert(st == SERD_BAD_ALLOC);
+ }
+
+ serd_sink_free(canon);
+ serd_sink_free(target);
+ serd_nodes_free(nodes);
+ serd_world_free(world);
+}
+
+int
+main(void)
+{
+ test_new_failed_alloc();
+ test_write_failed_alloc();
+ return 0;
+}
diff --git a/test/test_caret.c b/test/test_caret.c
index 54037a30..5fb6d18d 100644
--- a/test/test_caret.c
+++ b/test/test_caret.c
@@ -1,5 +1,5 @@
/*
- Copyright 2019-2020 David Robillard <d@drobilla.net>
+ Copyright 2019-2021 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
@@ -16,34 +16,39 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
#include <stddef.h>
+#include <stdint.h>
-int
-main(void)
+static int
+test_caret(void)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const node = serd_nodes_string(nodes, SERD_STRING("node"));
- SerdCaret* const caret = serd_caret_new(node, 46, 2);
+ SerdCaret* const caret = serd_caret_new(allocator, node, 46, 2);
assert(serd_caret_equals(caret, caret));
assert(serd_caret_name(caret) == node);
assert(serd_caret_line(caret) == 46);
assert(serd_caret_column(caret) == 2);
- SerdCaret* const copy = serd_caret_copy(caret);
+ SerdCaret* const copy = serd_caret_copy(allocator, caret);
assert(serd_caret_equals(caret, copy));
- assert(!serd_caret_copy(NULL));
+ assert(!serd_caret_copy(allocator, NULL));
const SerdNode* const other_node =
serd_nodes_string(nodes, SERD_STRING("other"));
- SerdCaret* const other_file = serd_caret_new(other_node, 46, 2);
- SerdCaret* const other_line = serd_caret_new(node, 47, 2);
- SerdCaret* const other_col = serd_caret_new(node, 46, 3);
+ SerdCaret* const other_file = serd_caret_new(allocator, other_node, 46, 2);
+ SerdCaret* const other_line = serd_caret_new(allocator, node, 47, 2);
+ SerdCaret* const other_col = serd_caret_new(allocator, node, 46, 3);
assert(!serd_caret_equals(caret, other_file));
assert(!serd_caret_equals(caret, other_line));
@@ -51,12 +56,60 @@ main(void)
assert(!serd_caret_equals(caret, NULL));
assert(!serd_caret_equals(NULL, caret));
- serd_caret_free(other_col);
- serd_caret_free(other_line);
- serd_caret_free(other_file);
- serd_caret_free(copy);
- serd_caret_free(caret);
+ serd_caret_free(allocator, other_col);
+ serd_caret_free(allocator, other_line);
+ serd_caret_free(allocator, other_file);
+ serd_caret_free(allocator, copy);
+ serd_caret_free(allocator, caret);
serd_nodes_free(nodes);
return 0;
}
+
+static void
+test_failed_alloc(void)
+{
+ char node_buf[32];
+
+ assert(!serd_node_construct_token(
+ sizeof(node_buf), node_buf, SERD_LITERAL, SERD_STRING("node"))
+ .status);
+
+ const SerdNode* node = (const SerdNode*)node_buf;
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ // Successfully allocate a new caret to count the number of allocations
+ SerdCaret* const caret = serd_caret_new(&allocator.base, node, 46, 2);
+ assert(caret);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations;
+ for (size_t i = 0u; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_caret_new(&allocator.base, node, 46, 2));
+ }
+
+ // Successfully copy the caret to count the number of allocations
+ allocator.n_allocations = 0;
+ allocator.n_remaining = SIZE_MAX;
+ SerdCaret* const copy = serd_caret_copy(&allocator.base, caret);
+ assert(copy);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_copy_allocs = allocator.n_allocations;
+ for (size_t i = 0u; i < n_copy_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_caret_copy(&allocator.base, caret));
+ }
+
+ serd_caret_free(&allocator.base, copy);
+ serd_caret_free(&allocator.base, caret);
+}
+
+int
+main(void)
+{
+ test_caret();
+ test_failed_alloc();
+ return 0;
+}
diff --git a/test/test_cursor.c b/test/test_cursor.c
index cade0621..23f4aefd 100644
--- a/test/test_cursor.c
+++ b/test/test_cursor.c
@@ -24,12 +24,12 @@
static void
test_copy(void)
{
- assert(!serd_cursor_copy(NULL));
+ assert(!serd_cursor_copy(NULL, NULL));
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdModel* const model = serd_model_new(world, SERD_ORDER_SPO, 0u);
SerdCursor* const begin = serd_model_begin(model);
- SerdCursor* const copy = serd_cursor_copy(begin);
+ SerdCursor* const copy = serd_cursor_copy(NULL, begin);
assert(serd_cursor_equals(copy, begin));
@@ -42,7 +42,7 @@ test_copy(void)
static void
test_comparison(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdNodes* const nodes = serd_world_nodes(world);
SerdModel* const model = serd_model_new(world, SERD_ORDER_SPO, 0u);
diff --git a/test/test_env.c b/test/test_env.c
index e2d53f6c..e6d6cbe5 100644
--- a/test/test_env.c
+++ b/test/test_env.c
@@ -16,19 +16,144 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
#include <string.h>
#define NS_EG "http://example.org/"
static void
+test_new_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ const size_t n_world_allocs = allocator.n_allocations;
+
+ // Successfully allocate a env to count the number of allocations
+ SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
+ assert(env);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_world_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_env_new(world, SERD_EMPTY_STRING()));
+ }
+
+ serd_env_free(env);
+ serd_world_free(world);
+}
+
+static void
+test_copy_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
+ const size_t n_world_allocs = allocator.n_allocations;
+
+ // Successfully copy an env to count the number of allocations
+ SerdEnv* copy = serd_env_copy(&allocator.base, env);
+ assert(copy);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_copy_allocs = allocator.n_allocations - n_world_allocs;
+ for (size_t i = 0; i < n_copy_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_env_copy(&allocator.base, env));
+ }
+
+ serd_env_free(copy);
+ serd_env_free(env);
+ serd_world_free(world);
+}
+
+static void
+test_set_prefix_absolute_failed_alloc(void)
+{
+ static const SerdStringView base_uri = SERD_STRING("http://example.org/");
+
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ SerdEnv* const env = serd_env_new(world, base_uri);
+ const size_t n_world_allocs = allocator.n_allocations;
+
+ char name[64] = "eg";
+ char uri[64] = "http://example.org/";
+
+ // Successfully set an absolute prefix to count the number of allocations
+ SerdStatus st = serd_env_set_prefix(env, SERD_STRING(name), SERD_STRING(uri));
+ assert(st == SERD_SUCCESS);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_set_prefix_allocs = allocator.n_allocations - n_world_allocs;
+ for (size_t i = 0; i < n_set_prefix_allocs; ++i) {
+ allocator.n_remaining = i;
+
+ snprintf(name, sizeof(name), "eg%" PRIuPTR, i);
+ snprintf(uri, sizeof(name), "http://example.org/%" PRIuPTR, i);
+
+ st = serd_env_set_prefix(env, SERD_STRING(name), SERD_STRING(uri));
+ assert(st == SERD_BAD_ALLOC);
+ }
+
+ serd_env_free(env);
+ serd_world_free(world);
+}
+
+static void
+test_set_prefix_relative_failed_alloc(void)
+{
+ static const SerdStringView base_uri = SERD_STRING("http://example.org/");
+
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ const size_t n_setup_allocs = allocator.n_allocations;
+
+ char name[64] = "egX";
+ char uri[64] = "relativeX";
+
+ // Successfully set an absolute prefix to count the number of allocations
+ SerdEnv* env = serd_env_new(world, base_uri);
+ SerdStatus st = serd_env_set_prefix(env, SERD_STRING(name), SERD_STRING(uri));
+ assert(st == SERD_SUCCESS);
+ serd_env_free(env);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_set_prefix_allocs = allocator.n_allocations - n_setup_allocs;
+ for (size_t i = 0; i < n_set_prefix_allocs; ++i) {
+ allocator.n_remaining = i;
+
+ snprintf(name, sizeof(name), "eg%" PRIuPTR, i);
+ snprintf(uri, sizeof(uri), "relative%" PRIuPTR, i);
+
+ env = serd_env_new(world, base_uri);
+ if (env) {
+ st = serd_env_set_prefix(env, SERD_STRING(name), SERD_STRING(uri));
+ assert(st == SERD_BAD_ALLOC);
+ }
+
+ serd_env_free(env);
+ }
+
+ serd_world_free(world);
+}
+
+static void
test_copy(void)
{
- assert(!serd_env_copy(NULL));
+ assert(!serd_env_copy(NULL, NULL));
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdEnv* const env =
serd_env_new(world, SERD_STRING("http://example.org/base/"));
@@ -36,7 +161,7 @@ test_copy(void)
serd_env_set_prefix(
env, SERD_STRING("eg"), SERD_STRING("http://example.org/"));
- SerdEnv* const env_copy = serd_env_copy(env);
+ SerdEnv* const env_copy = serd_env_copy(serd_world_allocator(world), env);
assert(serd_env_equals(env, env_copy));
@@ -58,7 +183,7 @@ test_copy(void)
static void
test_comparison(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
assert(!serd_env_equals(env, NULL));
@@ -74,7 +199,7 @@ static void
test_null(void)
{
// "Copying" NULL returns null
- assert(!serd_env_copy(NULL));
+ assert(!serd_env_copy(NULL, NULL));
// Accessors are tolerant to a NULL env for convenience
assert(!serd_env_base_uri(NULL));
@@ -95,9 +220,9 @@ count_prefixes(void* handle, const SerdEvent* event)
static void
test_base_uri(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
- SerdNode* const eg = serd_new_uri(SERD_STRING(NS_EG));
+ SerdNode* const eg = serd_new_uri(NULL, SERD_STRING(NS_EG));
// Test that invalid calls work as expected
assert(!serd_env_base_uri(env));
@@ -116,7 +241,7 @@ test_base_uri(void)
assert(!serd_env_set_base_uri(env, SERD_EMPTY_STRING()));
assert(!serd_env_base_uri(env));
- serd_node_free(eg);
+ serd_node_free(NULL, eg);
serd_env_free(env);
serd_world_free(world);
}
@@ -130,7 +255,7 @@ test_set_prefix(void)
static const SerdStringView rel = SERD_STRING("rel");
static const SerdStringView base = SERD_STRING("http://example.org/");
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
// Set a valid prefix
@@ -160,14 +285,14 @@ test_set_prefix(void)
static void
test_expand_untyped_literal(void)
{
- SerdWorld* const world = serd_world_new();
- SerdNode* const untyped = serd_new_string(SERD_STRING("data"));
+ SerdWorld* const world = serd_world_new(NULL);
+ SerdNode* const untyped = serd_new_string(NULL, SERD_STRING("data"));
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
assert(!serd_env_expand_node(env, untyped));
serd_env_free(env);
- serd_node_free(untyped);
+ serd_node_free(NULL, untyped);
serd_world_free(world);
}
@@ -176,8 +301,8 @@ test_expand_bad_uri_datatype(void)
{
static const SerdStringView type = SERD_STRING("Type");
- SerdWorld* world = serd_world_new();
- SerdNodes* nodes = serd_nodes_new();
+ SerdWorld* world = serd_world_new(NULL);
+ SerdNodes* nodes = serd_nodes_new(serd_world_allocator(world));
const SerdNode* const typed =
serd_nodes_literal(nodes, SERD_STRING("data"), SERD_HAS_DATATYPE, type);
@@ -196,20 +321,20 @@ test_expand_uri(void)
{
static const SerdStringView base = SERD_STRING("http://example.org/b/");
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdEnv* const env = serd_env_new(world, base);
- SerdNode* const rel = serd_new_uri(SERD_STRING("rel"));
+ SerdNode* const rel = serd_new_uri(NULL, SERD_STRING("rel"));
SerdNode* const rel_out = serd_env_expand_node(env, rel);
- SerdNode* const empty = serd_new_uri(SERD_EMPTY_STRING());
+ SerdNode* const empty = serd_new_uri(NULL, SERD_EMPTY_STRING());
SerdNode* const empty_out = serd_env_expand_node(env, empty);
assert(!strcmp(serd_node_string(rel_out), "http://example.org/b/rel"));
assert(!strcmp(serd_node_string(empty_out), "http://example.org/b/"));
- serd_node_free(empty_out);
- serd_node_free(empty);
- serd_node_free(rel_out);
- serd_node_free(rel);
+ serd_node_free(serd_world_allocator(world), empty_out);
+ serd_node_free(NULL, empty);
+ serd_node_free(serd_world_allocator(world), rel_out);
+ serd_node_free(NULL, rel);
serd_env_free(env);
serd_world_free(world);
}
@@ -219,30 +344,30 @@ test_expand_empty_uri_ref(void)
{
static const SerdStringView base = SERD_STRING("http://example.org/b/");
- SerdWorld* const world = serd_world_new();
- SerdNode* const rel = serd_new_uri(SERD_STRING("rel"));
+ SerdWorld* const world = serd_world_new(NULL);
+ SerdNode* const rel = serd_new_uri(NULL, SERD_STRING("rel"));
SerdEnv* const env = serd_env_new(world, base);
SerdNode* const rel_out = serd_env_expand_node(env, rel);
assert(!strcmp(serd_node_string(rel_out), "http://example.org/b/rel"));
- serd_node_free(rel_out);
+ serd_node_free(serd_world_allocator(world), rel_out);
serd_env_free(env);
- serd_node_free(rel);
+ serd_node_free(NULL, rel);
serd_world_free(world);
}
static void
test_expand_bad_uri(void)
{
- SerdWorld* const world = serd_world_new();
- SerdNode* const bad_uri = serd_new_uri(SERD_STRING("rel"));
+ SerdWorld* const world = serd_world_new(NULL);
+ SerdNode* const bad_uri = serd_new_uri(NULL, SERD_STRING("rel"));
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
assert(!serd_env_expand_node(env, bad_uri));
serd_env_free(env);
- serd_node_free(bad_uri);
+ serd_node_free(NULL, bad_uri);
serd_world_free(world);
}
@@ -252,7 +377,7 @@ test_expand_curie(void)
static const SerdStringView name = SERD_STRING("eg.1");
static const SerdStringView eg = SERD_STRING(NS_EG);
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
assert(!serd_env_set_prefix(env, name, eg));
@@ -262,7 +387,7 @@ test_expand_curie(void)
assert(expanded);
assert(!strcmp(serd_node_string(expanded), "http://example.org/foo"));
- serd_node_free(expanded);
+ serd_node_free(serd_world_allocator(world), expanded);
serd_env_free(env);
serd_world_free(world);
@@ -271,7 +396,7 @@ test_expand_curie(void)
static void
test_expand_bad_curie(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
assert(!serd_env_expand_curie(NULL, SERD_EMPTY_STRING()));
@@ -286,14 +411,14 @@ test_expand_bad_curie(void)
static void
test_expand_blank(void)
{
- SerdWorld* const world = serd_world_new();
- SerdNode* const blank = serd_new_token(SERD_BLANK, SERD_STRING("b1"));
+ SerdWorld* const world = serd_world_new(NULL);
+ SerdNode* const blank = serd_new_token(NULL, SERD_BLANK, SERD_STRING("b1"));
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
assert(!serd_env_expand_node(env, blank));
serd_env_free(env);
- serd_node_free(blank);
+ serd_node_free(NULL, blank);
serd_world_free(world);
}
@@ -304,7 +429,7 @@ test_equals(void)
static const SerdStringView base1 = SERD_STRING(NS_EG "b1/");
static const SerdStringView base2 = SERD_STRING(NS_EG "b2/");
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdEnv* const env1 = serd_env_new(world, base1);
SerdEnv* const env2 = serd_env_new(world, base2);
@@ -326,7 +451,7 @@ test_equals(void)
serd_env_set_base_uri(env2, base2);
assert(!serd_env_equals(env1, env2));
- SerdEnv* const env3 = serd_env_copy(env2);
+ SerdEnv* const env3 = serd_env_copy(NULL, env2);
assert(serd_env_equals(env3, env2));
serd_env_free(env3);
@@ -338,6 +463,10 @@ test_equals(void)
int
main(void)
{
+ test_new_failed_alloc();
+ test_copy_failed_alloc();
+ test_set_prefix_absolute_failed_alloc();
+ test_set_prefix_relative_failed_alloc();
test_copy();
test_comparison();
test_null();
diff --git a/test/test_filter.c b/test/test_filter.c
new file mode 100644
index 00000000..e326f9fb
--- /dev/null
+++ b/test/test_filter.c
@@ -0,0 +1,68 @@
+/*
+ Copyright 2021 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 "failing_allocator.h"
+#include "serd/serd.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+static void
+test_new_failed_alloc(void)
+{
+ static const SerdStringView s_string = SERD_STRING("http://example.org/s");
+ static const SerdStringView p_string = SERD_STRING("http://example.org/p");
+ static const SerdStringView o_string = SERD_STRING("http://example.org/o");
+ static const SerdStringView g_string = SERD_STRING("http://example.org/g");
+
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ SerdNodes* const nodes = serd_nodes_new(&allocator.base);
+ const SerdNode* const s = serd_nodes_uri(nodes, s_string);
+ const SerdNode* const p = serd_nodes_uri(nodes, p_string);
+ const SerdNode* const o = serd_nodes_uri(nodes, o_string);
+ const SerdNode* const g = serd_nodes_uri(nodes, g_string);
+
+ SerdSink* target = serd_sink_new(world, NULL, NULL, NULL);
+ const size_t n_setup_allocs = allocator.n_allocations;
+
+ // Successfully allocate a filter to count the number of allocations
+ SerdSink* filter = serd_filter_new(world, target, s, p, o, g, true);
+ assert(filter);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_setup_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_filter_new(world, target, s, p, o, g, true));
+ }
+
+ serd_sink_free(filter);
+ serd_sink_free(target);
+ serd_nodes_free(nodes);
+ serd_world_free(world);
+}
+
+int
+main(void)
+{
+ test_new_failed_alloc();
+ return 0;
+}
diff --git a/test/test_free_null.c b/test/test_free_null.c
index 85529a2b..4494368b 100644
--- a/test/test_free_null.c
+++ b/test/test_free_null.c
@@ -23,8 +23,8 @@
int
main(void)
{
- serd_free(NULL);
- serd_node_free(NULL);
+ serd_free(NULL, NULL);
+ serd_node_free(NULL, NULL);
serd_world_free(NULL);
serd_env_free(NULL);
serd_sink_free(NULL);
@@ -32,9 +32,9 @@ main(void)
serd_writer_free(NULL);
serd_nodes_free(NULL);
serd_model_free(NULL);
- serd_statement_free(NULL);
+ serd_statement_free(NULL, NULL);
serd_cursor_free(NULL);
- serd_caret_free(NULL);
+ serd_caret_free(NULL, NULL);
return 0;
}
diff --git a/test/test_log.c b/test/test_log.c
index 707a00f2..c9031ff3 100644
--- a/test/test_log.c
+++ b/test/test_log.c
@@ -47,7 +47,7 @@ custom_log_func(void* const handle,
static void
test_bad_arg(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
bool called = false;
serd_set_log_func(world, custom_log_func, &called);
@@ -60,7 +60,7 @@ test_bad_arg(void)
static void
test_default_log(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
for (unsigned i = 0; i <= SERD_LOG_LEVEL_DEBUG; ++i) {
const SerdLogLevel level = (SerdLogLevel)i;
@@ -74,7 +74,7 @@ test_default_log(void)
static void
test_custom_log(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
bool called = false;
serd_set_log_func(world, custom_log_func, &called);
@@ -90,22 +90,22 @@ test_custom_log(void)
static void
test_caret(void)
{
- SerdWorld* const world = serd_world_new();
- SerdNode* const name = serd_new_string(SERD_STRING("filename"));
- SerdCaret* const caret = serd_caret_new(name, 46, 2);
+ SerdWorld* const world = serd_world_new(NULL);
+ SerdNode* const name = serd_new_string(NULL, SERD_STRING("filename"));
+ SerdCaret* const caret = serd_caret_new(NULL, name, 46, 2);
serd_logf_at(world, SERD_LOG_LEVEL_NOTICE, caret, "is just ahead of me");
serd_logf_at(world, SERD_LOG_LEVEL_NOTICE, NULL, "isn't");
- serd_caret_free(caret);
- serd_node_free(name);
+ serd_caret_free(NULL, caret);
+ serd_node_free(NULL, name);
serd_world_free(world);
}
static void
test_filename_only(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
const SerdLogField fields[2] = {{"TEST_KEY", "TEST VALUE"},
{"SERD_FILE", "somename"}};
diff --git a/test/test_model.c b/test/test_model.c
index 72db88ae..827bd638 100644
--- a/test/test_model.c
+++ b/test/test_model.c
@@ -16,14 +16,16 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
+#include "serd/serd.h"
+
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "serd/serd.h"
-
#define WILDCARD_NODE NULL
#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
@@ -147,7 +149,9 @@ test_read(SerdWorld* world,
const SerdNode* g,
const unsigned n_quads)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
SerdCursor* cursor = serd_model_begin(model);
const SerdStatement* prev = NULL;
@@ -345,6 +349,32 @@ ignore_only_index_error(void* const handle,
}
static int
+test_failed_new_alloc(SerdWorld* ignored_world, const unsigned n_quads)
+{
+ (void)ignored_world;
+ (void)n_quads;
+
+ SerdFailingAllocator allocator = serd_failing_allocator();
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ const size_t n_world_allocs = allocator.n_allocations;
+
+ // Successfully allocate a model to count the number of allocations
+ SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, 0u);
+ assert(model);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_world_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_model_new(world, SERD_ORDER_SPO, 0u));
+ }
+
+ serd_model_free(model);
+ serd_world_free(world);
+ return 0;
+}
+
+static int
test_free_null(SerdWorld* world, const unsigned n_quads)
{
(void)world;
@@ -564,7 +594,9 @@ test_inserter(SerdWorld* world, const unsigned n_quads)
{
(void)n_quads;
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, 0u);
SerdSink* inserter = serd_inserter_new(model, NULL);
@@ -615,7 +647,9 @@ test_erase_with_iterator(SerdWorld* world, const unsigned n_quads)
assert(serd_cursor_advance(iter2) == SERD_BAD_CURSOR);
// Check that erasing the end iterator does nothing
- SerdCursor* const end = serd_cursor_copy(serd_model_end(model));
+ SerdCursor* const end =
+ serd_cursor_copy(serd_world_allocator(world), serd_model_end(model));
+
assert(serd_model_erase(model, end) == SERD_FAILURE);
serd_cursor_free(end);
@@ -630,7 +664,9 @@ test_add_erase(SerdWorld* world, const unsigned n_quads)
{
(void)n_quads;
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
SerdModel* const model = serd_model_new(world, SERD_ORDER_SPO, 0u);
// Add (s p "hello")
@@ -667,7 +703,9 @@ test_add_bad_statement(SerdWorld* world, const unsigned n_quads)
{
(void)n_quads;
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_world_allocator(world);
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* s = serd_nodes_uri(nodes, SERD_STRING("urn:s"));
const SerdNode* p = serd_nodes_uri(nodes, SERD_STRING("urn:p"));
const SerdNode* o = serd_nodes_uri(nodes, SERD_STRING("urn:o"));
@@ -675,7 +713,7 @@ test_add_bad_statement(SerdWorld* world, const unsigned n_quads)
const SerdNode* f =
serd_nodes_uri(nodes, SERD_STRING("file:///tmp/file.ttl"));
- SerdCaret* caret = serd_caret_new(f, 16, 18);
+ SerdCaret* caret = serd_caret_new(allocator, f, 16, 18);
SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, 0u);
assert(!serd_model_add_with_caret(model, s, p, o, NULL, caret));
@@ -699,7 +737,7 @@ test_add_bad_statement(SerdWorld* world, const unsigned n_quads)
serd_cursor_free(begin);
serd_model_free(model);
- serd_caret_free(caret);
+ serd_caret_free(allocator, caret);
serd_nodes_free(nodes);
return 0;
}
@@ -709,7 +747,7 @@ test_add_with_caret(SerdWorld* world, const unsigned n_quads)
{
(void)n_quads;
- SerdNodes* const nodes = serd_nodes_new();
+ SerdNodes* const nodes = serd_nodes_new(serd_world_allocator(world));
const SerdNode* lit = serd_nodes_string(nodes, SERD_STRING("string"));
const SerdNode* uri = serd_nodes_uri(nodes, SERD_STRING("urn:uri"));
const SerdNode* blank = serd_nodes_blank(nodes, SERD_STRING("b1"));
@@ -762,7 +800,7 @@ test_copy(SerdWorld* world, const unsigned n_quads)
SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, 0u);
generate(world, model, n_quads, NULL);
- SerdModel* copy = serd_model_copy(model);
+ SerdModel* copy = serd_model_copy(serd_world_allocator(world), model);
assert(serd_model_equals(model, copy));
serd_model_free(model);
@@ -1039,7 +1077,7 @@ test_write_flat_range(SerdWorld* world, const unsigned n_quads)
(void)n_quads;
SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, SERD_STORE_GRAPHS);
- SerdNodes* nodes = serd_nodes_new();
+ SerdNodes* nodes = serd_nodes_new(serd_world_allocator(world));
const SerdNode* s = serd_nodes_uri(nodes, SERD_STRING("urn:s"));
const SerdNode* p = serd_nodes_uri(nodes, SERD_STRING("urn:p"));
@@ -1052,7 +1090,7 @@ test_write_flat_range(SerdWorld* world, const unsigned n_quads)
serd_model_add(model, s, p, b2, NULL);
serd_model_add(model, b2, p, o, NULL);
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
SerdOutputStream out = serd_open_output_buffer(&buffer);
@@ -1083,7 +1121,7 @@ test_write_flat_range(SerdWorld* world, const unsigned n_quads)
assert(str);
assert(!strcmp(str, expected));
- serd_free(buffer.buf);
+ serd_free(NULL, buffer.buf);
serd_writer_free(writer);
serd_model_free(model);
serd_env_free(env);
@@ -1097,7 +1135,7 @@ test_write_bad_list(SerdWorld* world, const unsigned n_quads)
(void)n_quads;
SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, SERD_STORE_GRAPHS);
- SerdNodes* nodes = serd_nodes_new();
+ SerdNodes* nodes = serd_nodes_new(serd_world_allocator(world));
serd_model_add_index(model, SERD_ORDER_OPS);
@@ -1123,7 +1161,7 @@ test_write_bad_list(SerdWorld* world, const unsigned n_quads)
serd_model_add(model, list2, prest, norest, NULL);
serd_model_add(model, norest, pfirst, val2, NULL);
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
SerdOutputStream out = serd_open_output_buffer(&buffer);
@@ -1149,7 +1187,7 @@ test_write_bad_list(SerdWorld* world, const unsigned n_quads)
assert(str);
assert(!strcmp(str, expected));
- free(buffer.buf);
+ serd_free(NULL, buffer.buf);
serd_writer_free(writer);
serd_close_output(&out);
serd_model_free(model);
@@ -1164,7 +1202,7 @@ test_write_infinite_list(SerdWorld* world, const unsigned n_quads)
(void)n_quads;
SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, SERD_STORE_GRAPHS);
- SerdNodes* nodes = serd_nodes_new();
+ SerdNodes* nodes = serd_nodes_new(serd_world_allocator(world));
serd_model_add_index(model, SERD_ORDER_OPS);
@@ -1185,7 +1223,7 @@ test_write_infinite_list(SerdWorld* world, const unsigned n_quads)
serd_model_add(model, list2, pfirst, val2, NULL);
serd_model_add(model, list2, prest, list1, NULL);
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
SerdOutputStream out = serd_open_output_buffer(&buffer);
@@ -1217,7 +1255,7 @@ test_write_infinite_list(SerdWorld* world, const unsigned n_quads)
assert(str);
assert(!strcmp(str, expected));
- free(buffer.buf);
+ serd_free(NULL, buffer.buf);
serd_writer_free(writer);
serd_close_output(&out);
serd_model_free(model);
@@ -1253,7 +1291,7 @@ test_write_error_in_list_subject(SerdWorld* world, const unsigned n_quads)
serd_set_log_func(world, expected_error, NULL);
SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, 0u);
- SerdNodes* nodes = serd_nodes_new();
+ SerdNodes* nodes = serd_nodes_new(serd_world_allocator(world));
serd_model_add_index(model, SERD_ORDER_OPS);
@@ -1310,7 +1348,7 @@ test_write_error_in_list_object(SerdWorld* world, const unsigned n_quads)
serd_set_log_func(world, expected_error, NULL);
SerdModel* model = serd_model_new(world, SERD_ORDER_SPO, 0u);
- SerdNodes* nodes = serd_nodes_new();
+ SerdNodes* nodes = serd_nodes_new(serd_world_allocator(world));
serd_model_add_index(model, SERD_ORDER_OPS);
@@ -1367,7 +1405,8 @@ main(void)
typedef int (*TestFunc)(SerdWorld*, unsigned);
- const TestFunc tests[] = {test_free_null,
+ const TestFunc tests[] = {test_failed_new_alloc,
+ test_free_null,
test_get_world,
test_get_default_order,
test_get_flags,
@@ -1401,7 +1440,7 @@ main(void)
test_write_error_in_list_object,
NULL};
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
int ret = 0;
for (const TestFunc* t = tests; *t; ++t) {
diff --git a/test/test_node.c b/test/test_node.c
index 41ee6209..5c053294 100644
--- a/test/test_node.c
+++ b/test/test_node.c
@@ -59,23 +59,23 @@
static void
test_boolean(void)
{
- SerdNode* const true_node = serd_new_boolean(true);
+ SerdNode* const true_node = serd_new_boolean(NULL, true);
assert(!strcmp(serd_node_string(true_node), "true"));
assert(serd_get_boolean(true_node));
const SerdNode* const true_datatype = serd_node_datatype(true_node);
assert(true_datatype);
assert(!strcmp(serd_node_string(true_datatype), NS_XSD "boolean"));
- serd_node_free(true_node);
+ serd_node_free(NULL, true_node);
- SerdNode* const false_node = serd_new_boolean(false);
+ SerdNode* const false_node = serd_new_boolean(NULL, false);
assert(!strcmp(serd_node_string(false_node), "false"));
assert(!serd_get_boolean(false_node));
const SerdNode* const false_datatype = serd_node_datatype(false_node);
assert(false_datatype);
assert(!strcmp(serd_node_string(false_datatype), NS_XSD "boolean"));
- serd_node_free(false_node);
+ serd_node_free(NULL, false_node);
}
static void
@@ -84,12 +84,12 @@ check_get_boolean(const char* string,
const bool expected)
{
SerdNode* const node = serd_new_literal(
- SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
+ NULL, SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
assert(node);
assert(serd_get_boolean(node) == expected);
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
static void
@@ -123,7 +123,7 @@ test_decimal(void)
"0.0000000001"};
for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) {
- SerdNode* node = serd_new_decimal(test_values[i]);
+ SerdNode* node = serd_new_decimal(NULL, test_values[i]);
const char* node_str = serd_node_string(node);
assert(!strcmp(node_str, test_strings[i]));
@@ -136,7 +136,7 @@ test_decimal(void)
const double value = serd_get_double(node);
assert(!memcmp(&value, &test_values[i], sizeof(value)));
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
}
@@ -148,7 +148,7 @@ test_double(void)
"0.0E0", "-0.0E0", "1.2E0", "-2.3E0", "4.56789E6"};
for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) {
- SerdNode* node = serd_new_double(test_values[i]);
+ SerdNode* node = serd_new_double(NULL, test_values[i]);
const char* node_str = serd_node_string(node);
assert(!strcmp(node_str, test_strings[i]));
@@ -161,7 +161,7 @@ test_double(void)
const double value = serd_get_double(node);
assert(!memcmp(&value, &test_values[i], sizeof(value)));
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
}
@@ -171,14 +171,14 @@ check_get_double(const char* string,
const double expected)
{
SerdNode* const node = serd_new_literal(
- SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
+ NULL, SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
assert(node);
const double value = serd_get_double(node);
assert(!memcmp(&value, &expected, sizeof(value)));
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
static void
@@ -195,21 +195,23 @@ test_get_double(void)
SERD_DISABLE_CONVERSION_WARNINGS
- SerdNode* const nan = serd_new_string(SERD_STRING("unknown"));
+ SerdNode* const nan = serd_new_string(NULL, SERD_STRING("unknown"));
assert(isnan(serd_get_double(nan)));
- serd_node_free(nan);
+ serd_node_free(NULL, nan);
- SerdNode* const invalid = serd_new_literal(
- SERD_STRING("!invalid"), SERD_HAS_DATATYPE, SERD_STRING(NS_XSD "long"));
+ SerdNode* const invalid = serd_new_literal(NULL,
+ SERD_STRING("!invalid"),
+ SERD_HAS_DATATYPE,
+ SERD_STRING(NS_XSD "long"));
assert(isnan(serd_get_double(invalid)));
- serd_node_free(invalid);
+ serd_node_free(NULL, invalid);
SerdNode* const base64 =
- serd_new_base64(blob, sizeof(blob), SERD_EMPTY_STRING());
+ serd_new_base64(NULL, blob, sizeof(blob), SERD_EMPTY_STRING());
assert(isnan(serd_get_double(base64)));
- serd_node_free(base64);
+ serd_node_free(NULL, base64);
SERD_RESTORE_WARNINGS
}
@@ -222,7 +224,7 @@ test_float(void)
"0.0E0", "-0.0E0", "1.5E0", "-2.5E0", "4.56789E6"};
for (size_t i = 0; i < sizeof(test_values) / sizeof(float); ++i) {
- SerdNode* node = serd_new_float(test_values[i]);
+ SerdNode* node = serd_new_float(NULL, test_values[i]);
const char* node_str = serd_node_string(node);
assert(!strcmp(node_str, test_strings[i]));
@@ -235,7 +237,7 @@ test_float(void)
const float value = serd_get_float(node);
assert(!memcmp(&value, &test_values[i], sizeof(value)));
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
}
@@ -245,14 +247,14 @@ check_get_float(const char* string,
const float expected)
{
SerdNode* const node = serd_new_literal(
- SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
+ NULL, SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
assert(node);
const float value = serd_get_float(node);
assert(!memcmp(&value, &expected, sizeof(value)));
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
static void
@@ -267,31 +269,34 @@ test_get_float(void)
SERD_DISABLE_CONVERSION_WARNINGS
- SerdNode* const nan = serd_new_string(SERD_STRING("unknown"));
+ SerdNode* const nan = serd_new_string(NULL, SERD_STRING("unknown"));
assert(isnan(serd_get_float(nan)));
- serd_node_free(nan);
+ serd_node_free(NULL, nan);
- SerdNode* const invalid = serd_new_literal(
- SERD_STRING("!invalid"), SERD_HAS_DATATYPE, SERD_STRING(NS_XSD "long"));
+ SerdNode* const invalid = serd_new_literal(NULL,
+ SERD_STRING("!invalid"),
+ SERD_HAS_DATATYPE,
+ SERD_STRING(NS_XSD "long"));
assert(isnan(serd_get_double(invalid)));
SERD_RESTORE_WARNINGS
- serd_node_free(invalid);
+ serd_node_free(NULL, invalid);
}
static void
test_integer(void)
{
- assert(!serd_new_integer(42, SERD_STRING("notauri")));
+ assert(!serd_new_integer(NULL, 42, SERD_STRING("notauri")));
const int64_t test_values[] = {0, -0, -23, 23, -12340, 1000, -1000};
const char* test_strings[] = {
"0", "0", "-23", "23", "-12340", "1000", "-1000"};
for (size_t i = 0; i < sizeof(test_values) / sizeof(double); ++i) {
- SerdNode* node = serd_new_integer(test_values[i], SERD_EMPTY_STRING());
+ SerdNode* node =
+ serd_new_integer(NULL, test_values[i], SERD_EMPTY_STRING());
const char* node_str = serd_node_string(node);
assert(!strcmp(node_str, test_strings[i]));
const size_t len = strlen(node_str);
@@ -302,7 +307,7 @@ test_integer(void)
assert(!strcmp(serd_node_string(datatype), NS_XSD "integer"));
assert(serd_get_integer(node) == test_values[i]);
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
}
@@ -312,12 +317,12 @@ check_get_integer(const char* string,
const int64_t expected)
{
SerdNode* const node = serd_new_literal(
- SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
+ NULL, SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
assert(node);
assert(serd_get_integer(node) == expected);
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
static void
@@ -336,8 +341,8 @@ test_get_integer(void)
static void
test_base64(void)
{
- assert(!serd_new_base64(&SERD_URI_NULL, 1, SERD_STRING("notauri")));
- assert(!serd_new_base64(&SERD_URI_NULL, 0, SERD_EMPTY_STRING()));
+ assert(!serd_new_base64(NULL, &SERD_URI_NULL, 1, SERD_STRING("notauri")));
+ assert(!serd_new_base64(NULL, &SERD_URI_NULL, 0, SERD_EMPTY_STRING()));
// Test valid base64 blobs with a range of sizes
for (size_t size = 1; size < 256; ++size) {
@@ -346,7 +351,7 @@ test_base64(void)
data[i] = (uint8_t)((size + i) % 256);
}
- SerdNode* blob = serd_new_base64(data, size, SERD_EMPTY_STRING());
+ SerdNode* blob = serd_new_base64(NULL, data, size, SERD_EMPTY_STRING());
const char* blob_str = serd_node_string(blob);
const size_t max_size = serd_get_base64_size(blob);
uint8_t* out = (uint8_t*)calloc(1, max_size);
@@ -365,8 +370,8 @@ test_base64(void)
assert(datatype);
assert(!strcmp(serd_node_string(datatype), NS_XSD "base64Binary"));
- serd_node_free(blob);
- serd_free(out);
+ serd_node_free(NULL, blob);
+ free(out);
free(data);
}
}
@@ -377,7 +382,7 @@ check_get_base64(const char* string,
const char* expected)
{
SerdNode* const node = serd_new_literal(
- SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
+ NULL, SERD_STRING(string), SERD_HAS_DATATYPE, SERD_STRING(datatype_uri));
assert(node);
@@ -392,7 +397,7 @@ check_get_base64(const char* string,
assert(strlen(decoded) <= max_size);
free(decoded);
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
static void
@@ -402,14 +407,16 @@ test_get_base64(void)
check_get_base64("Zm9vYg==", NS_XSD "base64Binary", "foob");
check_get_base64(" \f\n\r\t\vZm9v \f\n\r\t\v", NS_XSD "base64Binary", "foo");
- SerdNode* const node = serd_new_literal(
- SERD_STRING("Zm9v"), SERD_HAS_DATATYPE, SERD_STRING(NS_XSD "base64Binary"));
+ SerdNode* const node = serd_new_literal(NULL,
+ SERD_STRING("Zm9v"),
+ SERD_HAS_DATATYPE,
+ SERD_STRING(NS_XSD "base64Binary"));
char small[2] = {0};
const SerdWriteResult r = serd_get_base64(node, sizeof(small), small);
assert(r.status == SERD_OVERFLOW);
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
static void
@@ -420,69 +427,75 @@ test_node_equals(void)
static const SerdStringView replacement_char = {
(const char*)replacement_char_str, 3};
- SerdNode* lhs = serd_new_string(replacement_char);
- SerdNode* rhs = serd_new_string(SERD_STRING("123"));
+ SerdNode* lhs = serd_new_string(NULL, replacement_char);
+ SerdNode* rhs = serd_new_string(NULL, SERD_STRING("123"));
assert(serd_node_equals(lhs, lhs));
assert(!serd_node_equals(lhs, rhs));
- assert(!serd_node_copy(NULL));
+ assert(!serd_node_copy(NULL, NULL));
- serd_node_free(lhs);
- serd_node_free(rhs);
+ serd_node_free(NULL, lhs);
+ serd_node_free(NULL, rhs);
}
static void
test_node_from_syntax(void)
{
- SerdNode* const hello = serd_new_string(SERD_STRING("hello\""));
+ SerdNode* const hello = serd_new_string(NULL, SERD_STRING("hello\""));
assert(serd_node_length(hello) == 6);
assert(!serd_node_flags(hello));
assert(!strncmp(serd_node_string(hello), "hello\"", 6));
- serd_node_free(hello);
+ serd_node_free(NULL, hello);
}
static void
test_node_from_substring(void)
{
- SerdNode* const a_b = serd_new_string(SERD_SUBSTRING("a\"bc", 3));
+ SerdNode* const a_b = serd_new_string(NULL, SERD_SUBSTRING("a\"bc", 3));
assert(serd_node_length(a_b) == 3);
assert(!serd_node_flags(a_b));
assert(strlen(serd_node_string(a_b)) == 3);
assert(!strncmp(serd_node_string(a_b), "a\"b", 3));
- serd_node_free(a_b);
+ serd_node_free(NULL, a_b);
}
static void
check_copy_equals(const SerdNode* const node)
{
- SerdNode* const copy = serd_node_copy(node);
+ SerdNode* const copy = serd_node_copy(NULL, node);
assert(serd_node_equals(node, copy));
- serd_node_free(copy);
+ serd_node_free(NULL, copy);
}
static void
test_new(void)
{
- assert(!serd_node_new(SERD_URI,
+ assert(!serd_node_new(NULL,
+ SERD_URI,
SERD_STRING("http://example.org/"),
SERD_HAS_DATATYPE,
SERD_STRING("http://example.org/uris/cant/have/me")));
- assert(!serd_node_new(SERD_URI,
+ assert(!serd_node_new(NULL,
+ SERD_URI,
SERD_STRING("http://example.org/"),
SERD_HAS_LANGUAGE,
SERD_STRING("in-valid")));
- assert(!serd_node_new(SERD_BLANK,
+ assert(!serd_node_new(NULL,
+ SERD_BLANK,
SERD_STRING("b0"),
SERD_HAS_DATATYPE,
SERD_STRING("http://example.org/uris/cant/have/me")));
- assert(!serd_node_new(
- SERD_BLANK, SERD_STRING("b0"), SERD_HAS_LANGUAGE, SERD_STRING("in-valid")));
+ assert(!serd_node_new(NULL,
+ SERD_BLANK,
+ SERD_STRING("b0"),
+ SERD_HAS_LANGUAGE,
+ SERD_STRING("in-valid")));
}
static void
@@ -491,35 +504,46 @@ test_literal(void)
static const SerdStringView hello_str = SERD_STRING("hello");
static const SerdStringView empty_str = SERD_EMPTY_STRING();
- assert(!serd_new_literal(
- hello_str, SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE, SERD_STRING("whatever")));
+ assert(!serd_new_literal(NULL,
+ hello_str,
+ SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE,
+ SERD_STRING("whatever")));
- assert(!serd_new_literal(hello_str, SERD_HAS_DATATYPE, empty_str));
- assert(!serd_new_literal(hello_str, SERD_HAS_LANGUAGE, empty_str));
+ assert(!serd_new_literal(NULL, hello_str, SERD_HAS_DATATYPE, empty_str));
+ assert(!serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, empty_str));
- assert(!serd_new_literal(hello_str, SERD_HAS_DATATYPE, SERD_STRING("Type")));
- assert(!serd_new_literal(hello_str, SERD_HAS_DATATYPE, SERD_STRING("de")));
+ assert(
+ !serd_new_literal(NULL, hello_str, SERD_HAS_DATATYPE, SERD_STRING("Type")));
+ assert(
+ !serd_new_literal(NULL, hello_str, SERD_HAS_DATATYPE, SERD_STRING("de")));
- assert(!serd_new_literal(hello_str, SERD_HAS_LANGUAGE, SERD_STRING("3n")));
- assert(!serd_new_literal(hello_str, SERD_HAS_LANGUAGE, SERD_STRING("d3")));
- assert(!serd_new_literal(hello_str, SERD_HAS_LANGUAGE, SERD_STRING("d3")));
- assert(!serd_new_literal(hello_str, SERD_HAS_LANGUAGE, SERD_STRING("en-!")));
+ assert(
+ !serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, SERD_STRING("3n")));
+ assert(
+ !serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, SERD_STRING("d3")));
+ assert(
+ !serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, SERD_STRING("d3")));
+ assert(
+ !serd_new_literal(NULL, hello_str, SERD_HAS_LANGUAGE, SERD_STRING("en-!")));
- SerdNode* hello2 = serd_new_string(SERD_STRING("hello\""));
+ SerdNode* hello2 = serd_new_string(NULL, SERD_STRING("hello\""));
assert(serd_node_length(hello2) == 6 &&
!strcmp(serd_node_string(hello2), "hello\""));
check_copy_equals(hello2);
- assert(!serd_new_literal(
- SERD_STRING("plain"), SERD_HAS_DATATYPE, SERD_STRING(NS_RDF "langString")));
+ assert(!serd_new_literal(NULL,
+ SERD_STRING("plain"),
+ SERD_HAS_DATATYPE,
+ SERD_STRING(NS_RDF "langString")));
- serd_node_free(hello2);
+ serd_node_free(NULL, hello2);
const char* lang_lit_str = "\"Hello\"@en-ca";
SerdNode* sliced_lang_lit =
- serd_new_literal(SERD_SUBSTRING(lang_lit_str + 1, 5),
+ serd_new_literal(NULL,
+ SERD_SUBSTRING(lang_lit_str + 1, 5),
SERD_HAS_LANGUAGE,
SERD_SUBSTRING(lang_lit_str + 8, 5));
@@ -529,11 +553,12 @@ test_literal(void)
assert(lang);
assert(!strcmp(serd_node_string(lang), "en-ca"));
check_copy_equals(sliced_lang_lit);
- serd_node_free(sliced_lang_lit);
+ serd_node_free(NULL, sliced_lang_lit);
const char* type_lit_str = "\"Hallo\"^^<http://example.org/Greeting>";
SerdNode* sliced_type_lit =
- serd_new_literal(SERD_SUBSTRING(type_lit_str + 1, 5),
+ serd_new_literal(NULL,
+ SERD_SUBSTRING(type_lit_str + 1, 5),
SERD_HAS_DATATYPE,
SERD_SUBSTRING(type_lit_str + 10, 27));
@@ -542,50 +567,53 @@ test_literal(void)
const SerdNode* const datatype = serd_node_datatype(sliced_type_lit);
assert(datatype);
assert(!strcmp(serd_node_string(datatype), "http://example.org/Greeting"));
- serd_node_free(sliced_type_lit);
+ serd_node_free(NULL, sliced_type_lit);
}
static void
test_blank(void)
{
- SerdNode* blank = serd_new_token(SERD_BLANK, SERD_STRING("b0"));
+ SerdNode* blank = serd_new_token(NULL, SERD_BLANK, SERD_STRING("b0"));
assert(serd_node_length(blank) == 2);
assert(serd_node_flags(blank) == 0);
assert(!strcmp(serd_node_string(blank), "b0"));
- serd_node_free(blank);
+ serd_node_free(NULL, blank);
}
static void
test_compare(void)
{
SerdNode* xsd_short = serd_new_token(
- SERD_URI, SERD_STRING("http://www.w3.org/2001/XMLSchema#short"));
+ NULL, SERD_URI, SERD_STRING("http://www.w3.org/2001/XMLSchema#short"));
- SerdNode* angst = serd_new_string(SERD_STRING("angst"));
+ SerdNode* angst = serd_new_string(NULL, SERD_STRING("angst"));
SerdNode* angst_de = serd_new_literal(
- SERD_STRING("angst"), SERD_HAS_LANGUAGE, SERD_STRING("de"));
+ NULL, SERD_STRING("angst"), SERD_HAS_LANGUAGE, SERD_STRING("de"));
SerdNode* angst_en = serd_new_literal(
- SERD_STRING("angst"), SERD_HAS_LANGUAGE, SERD_STRING("en"));
+ NULL, SERD_STRING("angst"), SERD_HAS_LANGUAGE, SERD_STRING("en"));
SerdNode* hallo = serd_new_literal(
- SERD_STRING("Hallo"), SERD_HAS_LANGUAGE, SERD_STRING("de"));
+ NULL, SERD_STRING("Hallo"), SERD_HAS_LANGUAGE, SERD_STRING("de"));
- SerdNode* hello = serd_new_string(SERD_STRING("Hello"));
- SerdNode* universe = serd_new_string(SERD_STRING("Universe"));
- SerdNode* integer = serd_new_integer(4, SERD_EMPTY_STRING());
- SerdNode* short_integer = serd_new_integer(4, SERD_STRING(NS_XSD "short"));
- SerdNode* blank = serd_new_token(SERD_BLANK, SERD_STRING("b1"));
+ SerdNode* hello = serd_new_string(NULL, SERD_STRING("Hello"));
+ SerdNode* universe = serd_new_string(NULL, SERD_STRING("Universe"));
+ SerdNode* integer = serd_new_integer(NULL, 4, SERD_EMPTY_STRING());
+ SerdNode* short_integer =
+ serd_new_integer(NULL, 4, SERD_STRING(NS_XSD "short"));
+ SerdNode* blank = serd_new_token(NULL, SERD_BLANK, SERD_STRING("b1"));
- SerdNode* uri = serd_new_uri(SERD_STRING("http://example.org/"));
+ SerdNode* uri = serd_new_uri(NULL, SERD_STRING("http://example.org/"));
SerdNode* aardvark =
- serd_new_literal(SERD_STRING("alex"),
+ serd_new_literal(NULL,
+ SERD_STRING("alex"),
SERD_HAS_DATATYPE,
SERD_STRING("http://example.org/Aardvark"));
- SerdNode* badger = serd_new_literal(SERD_STRING("bobby"),
+ SerdNode* badger = serd_new_literal(NULL,
+ SERD_STRING("bobby"),
SERD_HAS_DATATYPE,
SERD_STRING("http://example.org/Badger"));
@@ -602,19 +630,19 @@ test_compare(void)
assert(serd_node_compare(integer, short_integer) < 0);
assert(serd_node_compare(aardvark, badger) < 0);
- serd_node_free(uri);
- serd_node_free(blank);
- serd_node_free(short_integer);
- serd_node_free(integer);
- serd_node_free(badger);
- serd_node_free(aardvark);
- serd_node_free(universe);
- serd_node_free(hello);
- serd_node_free(hallo);
- serd_node_free(angst_en);
- serd_node_free(angst_de);
- serd_node_free(angst);
- serd_node_free(xsd_short);
+ serd_node_free(NULL, uri);
+ serd_node_free(NULL, blank);
+ serd_node_free(NULL, short_integer);
+ serd_node_free(NULL, integer);
+ serd_node_free(NULL, badger);
+ serd_node_free(NULL, aardvark);
+ serd_node_free(NULL, universe);
+ serd_node_free(NULL, hello);
+ serd_node_free(NULL, hallo);
+ serd_node_free(NULL, angst_en);
+ serd_node_free(NULL, angst_de);
+ serd_node_free(NULL, angst);
+ serd_node_free(NULL, xsd_short);
}
int
diff --git a/test/test_node_syntax.c b/test/test_node_syntax.c
index d9b0afbb..582ba8aa 100644
--- a/test/test_node_syntax.c
+++ b/test/test_node_syntax.c
@@ -16,12 +16,53 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
#include <stdbool.h>
#include <string.h>
+static void
+test_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdNode* const node = serd_new_string(&allocator.base, SERD_STRING("node"));
+
+ // Successfully convert a node to count the number of allocations
+
+ const size_t n_setup_allocs = allocator.n_allocations;
+
+ char* const str =
+ serd_node_to_syntax(&allocator.base, node, SERD_TURTLE, NULL);
+
+ SerdNode* const copy =
+ serd_node_from_syntax(&allocator.base, str, SERD_TURTLE, NULL);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_setup_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+
+ char* const s =
+ serd_node_to_syntax(&allocator.base, node, SERD_TURTLE, NULL);
+
+ SerdNode* const c =
+ serd_node_from_syntax(&allocator.base, str, SERD_TURTLE, NULL);
+
+ assert(!s || !c);
+
+ serd_free(&allocator.base, s);
+ serd_node_free(&allocator.base, c);
+ }
+
+ serd_node_free(&allocator.base, copy);
+ serd_free(&allocator.base, str);
+ serd_node_free(&allocator.base, node);
+}
+
static bool
check(SerdWorld* const world,
const SerdSyntax syntax,
@@ -31,14 +72,14 @@ check(SerdWorld* const world,
SerdEnv* const env =
serd_env_new(world, SERD_STRING("http://example.org/base/"));
- char* const str = serd_node_to_syntax(node, syntax, env);
- SerdNode* const copy = serd_node_from_syntax(str, syntax, env);
+ char* const str = serd_node_to_syntax(NULL, node, syntax, env);
+ SerdNode* const copy = serd_node_from_syntax(NULL, str, syntax, env);
const bool success = !strcmp(str, expected) && serd_node_equals(copy, node);
- serd_node_free(copy);
- serd_free(str);
- serd_node_free(node);
+ serd_node_free(serd_world_allocator(world), copy);
+ serd_free(serd_world_allocator(world), str);
+ serd_node_free(NULL, node);
serd_env_free(env);
return success;
}
@@ -54,107 +95,111 @@ test_common(SerdWorld* const world, const SerdSyntax syntax)
static const SerdStringView num_type =
SERD_STRING("http://example.org/Decimal");
+ assert(check(
+ world, syntax, serd_new_string(NULL, SERD_STRING("node")), "\"node\""));
+
assert(
- check(world, syntax, serd_new_string(SERD_STRING("node")), "\"node\""));
+ check(world,
+ syntax,
+ serd_new_literal(
+ NULL, SERD_STRING("hallo"), SERD_HAS_LANGUAGE, SERD_STRING("de")),
+ "\"hallo\"@de"));
- assert(check(world,
- syntax,
- serd_new_literal(
- SERD_STRING("hallo"), SERD_HAS_LANGUAGE, SERD_STRING("de")),
- "\"hallo\"@de"));
+ assert(
+ check(world,
+ syntax,
+ serd_new_literal(NULL, SERD_STRING("X"), SERD_HAS_DATATYPE, datatype),
+ "\"X\"^^<http://example.org/Datatype>"));
assert(check(world,
syntax,
- serd_new_literal(SERD_STRING("X"), SERD_HAS_DATATYPE, datatype),
- "\"X\"^^<http://example.org/Datatype>"));
+ serd_new_token(NULL, SERD_BLANK, SERD_STRING("blank")),
+ "_:blank"));
assert(check(world,
syntax,
- serd_new_token(SERD_BLANK, SERD_STRING("blank")),
- "_:blank"));
-
- assert(check(
- world, syntax, serd_new_token(SERD_BLANK, SERD_STRING("b0")), "_:b0"));
+ serd_new_token(NULL, SERD_BLANK, SERD_STRING("b0")),
+ "_:b0"));
assert(check(world,
syntax,
- serd_new_token(SERD_BLANK, SERD_STRING("named1")),
+ serd_new_token(NULL, SERD_BLANK, SERD_STRING("named1")),
"_:named1"));
assert(check(world,
syntax,
- serd_new_uri(SERD_STRING("http://example.org/")),
+ serd_new_uri(NULL, SERD_STRING("http://example.org/")),
"<http://example.org/>"));
assert(check(world,
syntax,
- serd_new_double(1.25),
+ serd_new_double(NULL, 1.25),
"\"1.25E0\"^^<http://www.w3.org/2001/XMLSchema#double>"));
assert(check(world,
syntax,
- serd_new_float(1.25),
+ serd_new_float(NULL, 1.25),
"\"1.25E0\"^^<http://www.w3.org/2001/XMLSchema#float>"));
assert(check(world,
syntax,
- serd_new_integer(1234, num_type),
+ serd_new_integer(NULL, 1234, num_type),
"\"1234\"^^<http://example.org/Decimal>"));
assert(
check(world,
syntax,
- serd_new_base64(data, sizeof(data), SERD_EMPTY_STRING()),
+ serd_new_base64(NULL, data, sizeof(data), SERD_EMPTY_STRING()),
"\"BAAAAAIAAAA=\"^^<http://www.w3.org/2001/XMLSchema#base64Binary>"));
}
static void
test_ntriples(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
test_common(world, SERD_NTRIPLES);
{
// No relative URIs in NTriples, so converting one fails without an env
- SerdNode* const rel = serd_new_uri(SERD_STRING("rel/uri"));
- assert(!serd_node_to_syntax(rel, SERD_NTRIPLES, NULL));
- assert(!serd_node_from_syntax("<rel/uri>", SERD_NTRIPLES, NULL));
+ SerdNode* const rel = serd_new_uri(NULL, SERD_STRING("rel/uri"));
+ assert(!serd_node_to_syntax(NULL, rel, SERD_NTRIPLES, NULL));
+ assert(!serd_node_from_syntax(NULL, "<rel/uri>", SERD_NTRIPLES, NULL));
// If a relative URI can be expanded then all's well
SerdEnv* const env =
serd_env_new(world, SERD_STRING("http://example.org/base/"));
- char* const str = serd_node_to_syntax(rel, SERD_NTRIPLES, env);
+ char* const str = serd_node_to_syntax(NULL, rel, SERD_NTRIPLES, env);
assert(!strcmp(str, "<http://example.org/base/rel/uri>"));
- SerdNode* const copy = serd_node_from_syntax(str, SERD_NTRIPLES, env);
+ SerdNode* const copy = serd_node_from_syntax(NULL, str, SERD_NTRIPLES, env);
assert(!strcmp(serd_node_string(copy), "http://example.org/base/rel/uri"));
- serd_node_free(copy);
+ serd_node_free(serd_world_allocator(world), copy);
serd_env_free(env);
- serd_free(str);
- serd_node_free(rel);
+ serd_free(serd_world_allocator(world), str);
+ serd_node_free(NULL, rel);
}
assert(check(world,
SERD_NTRIPLES,
- serd_new_decimal(1.25),
+ serd_new_decimal(NULL, 1.25),
"\"1.25\"^^<http://www.w3.org/2001/XMLSchema#decimal>"));
assert(check(world,
SERD_NTRIPLES,
- serd_new_integer(1234, SERD_EMPTY_STRING()),
+ serd_new_integer(NULL, 1234, SERD_EMPTY_STRING()),
"\"1234\"^^<http://www.w3.org/2001/XMLSchema#integer>"));
assert(check(world,
SERD_NTRIPLES,
- serd_new_boolean(true),
+ serd_new_boolean(NULL, true),
"\"true\"^^<http://www.w3.org/2001/XMLSchema#boolean>"));
assert(check(world,
SERD_NTRIPLES,
- serd_new_boolean(false),
+ serd_new_boolean(NULL, false),
"\"false\"^^<http://www.w3.org/2001/XMLSchema#boolean>"));
serd_world_free(world);
@@ -166,22 +211,27 @@ test_turtle(void)
static const SerdStringView xsd_integer =
SERD_STRING("http://www.w3.org/2001/XMLSchema#integer");
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
test_common(world, SERD_TURTLE);
- check(world, SERD_TURTLE, serd_new_uri(SERD_STRING("rel/uri")), "<rel/uri>");
+ check(world,
+ SERD_TURTLE,
+ serd_new_uri(NULL, SERD_STRING("rel/uri")),
+ "<rel/uri>");
- assert(check(world, SERD_TURTLE, serd_new_decimal(1.25), "1.25"));
+ assert(check(world, SERD_TURTLE, serd_new_decimal(NULL, 1.25), "1.25"));
- assert(check(
- world, SERD_TURTLE, serd_new_integer(1234, SERD_EMPTY_STRING()), "1234"));
+ assert(check(world,
+ SERD_TURTLE,
+ serd_new_integer(NULL, 1234, SERD_EMPTY_STRING()),
+ "1234"));
- assert(
- check(world, SERD_TURTLE, serd_new_integer(1234, xsd_integer), "1234"));
+ assert(check(
+ world, SERD_TURTLE, serd_new_integer(NULL, 1234, xsd_integer), "1234"));
- assert(check(world, SERD_TURTLE, serd_new_boolean(true), "true"));
- assert(check(world, SERD_TURTLE, serd_new_boolean(false), "false"));
+ assert(check(world, SERD_TURTLE, serd_new_boolean(NULL, true), "true"));
+ assert(check(world, SERD_TURTLE, serd_new_boolean(NULL, false), "false"));
serd_world_free(world);
}
@@ -189,6 +239,7 @@ test_turtle(void)
int
main(void)
{
+ test_failed_alloc();
test_ntriples();
test_turtle();
diff --git a/test/test_nodes.c b/test/test_nodes.c
index c841acc4..2cdfbc3c 100644
--- a/test/test_nodes.c
+++ b/test/test_nodes.c
@@ -16,6 +16,7 @@
#undef NDEBUG
+#include "failing_allocator.h"
#include "serd/serd.h"
#include <assert.h>
@@ -26,10 +27,64 @@
#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
static void
+test_new_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ // Successfully allocate a node set to count the number of allocations
+ SerdNodes* nodes = serd_nodes_new(&allocator.base);
+ assert(nodes);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_nodes_new(&allocator.base));
+ }
+
+ serd_nodes_free(nodes);
+}
+
+static void
+test_intern_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdNode* const node = serd_new_string(&allocator.base, SERD_STRING("node"));
+
+ // Successfully intern a node to count the number of allocations
+ SerdNodes* nodes = serd_nodes_new(&allocator.base);
+ const SerdNode* interned1 = serd_nodes_intern(nodes, node);
+ assert(serd_node_equals(node, interned1));
+ assert(serd_nodes_size(nodes) == 1u);
+
+ const size_t n_new_allocs = allocator.n_allocations;
+ serd_nodes_free(nodes);
+
+ // Test that each allocation failing is handled gracefully
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+
+ if ((nodes = serd_nodes_new(&allocator.base))) {
+ const SerdNode* interned2 = serd_nodes_intern(nodes, node);
+ if (interned2) {
+ assert(serd_node_equals(node, interned2));
+ assert(serd_nodes_size(nodes) == 1u);
+ }
+ serd_nodes_free(nodes);
+ }
+ }
+
+ serd_node_free(&allocator.base, node);
+}
+
+static void
test_intern(void)
{
- SerdNodes* nodes = serd_nodes_new();
- SerdNode* node = serd_new_string(SERD_STRING("node"));
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* nodes = serd_nodes_new(allocator);
+ SerdNode* node = serd_new_string(NULL, SERD_STRING("node"));
assert(serd_nodes_size(nodes) == 0u);
assert(!serd_nodes_intern(nodes, NULL));
@@ -43,7 +98,7 @@ test_intern(void)
assert(interned1 == interned2);
assert(serd_nodes_size(nodes) == 1u);
- serd_node_free(node);
+ serd_node_free(NULL, node);
serd_nodes_free(nodes);
}
@@ -52,7 +107,9 @@ test_string(void)
{
static const SerdStringView string = SERD_STRING("string");
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const node = serd_nodes_string(nodes, string);
assert(node);
@@ -68,7 +125,9 @@ test_string(void)
static void
test_invalid_literal(void)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
assert(!serd_nodes_literal(nodes,
SERD_STRING("double meta"),
@@ -94,7 +153,9 @@ test_plain_literal(void)
static const SerdStringView string = SERD_STRING("string");
static const SerdStringView language = SERD_STRING("en");
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const node =
serd_nodes_literal(nodes, string, SERD_HAS_LANGUAGE, language);
@@ -137,7 +198,9 @@ test_typed_literal(void)
static const SerdStringView string = SERD_STRING("string");
static const SerdStringView datatype = SERD_STRING("http://example.org/Type");
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const node =
serd_nodes_literal(nodes, string, SERD_HAS_DATATYPE, datatype);
@@ -165,7 +228,9 @@ test_typed_literal(void)
static void
test_boolean(void)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const false1 = serd_nodes_boolean(nodes, false);
const SerdNode* const false2 = serd_nodes_boolean(nodes, false);
@@ -195,7 +260,9 @@ test_boolean(void)
static void
test_decimal(void)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const a = serd_nodes_decimal(nodes, -12.3456789);
const SerdNode* const b = serd_nodes_decimal(nodes, -12.3456789);
@@ -214,7 +281,9 @@ test_decimal(void)
static void
test_double(void)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const a = serd_nodes_double(nodes, -1.2E3);
const SerdNode* const b = serd_nodes_double(nodes, -1.2E3);
@@ -230,7 +299,9 @@ test_double(void)
static void
test_float(void)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const a = serd_nodes_float(nodes, -1.2E3f);
const SerdNode* const b = serd_nodes_float(nodes, -1.2E3f);
@@ -246,7 +317,9 @@ test_float(void)
static void
test_integer(void)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const a =
serd_nodes_integer(nodes, -1234567890, SERD_EMPTY_STRING());
@@ -271,7 +344,9 @@ test_base64(void)
{
static const char data[] = {'f', 'o', 'o', 'b', 'a', 'r'};
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const a =
serd_nodes_base64(nodes, &data, sizeof(data), SERD_EMPTY_STRING());
@@ -308,7 +383,9 @@ test_uri(void)
{
static const SerdStringView string = SERD_STRING("http://example.org/");
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const node = serd_nodes_uri(nodes, string);
assert(node);
@@ -326,7 +403,9 @@ test_parsed_uri(void)
{
static const SerdStringView string = SERD_STRING("http://example.org/");
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdURIView uri = serd_parse_uri(string.buf);
const SerdNode* const node = serd_nodes_parsed_uri(nodes, uri);
@@ -354,7 +433,9 @@ test_blank(void)
{
static const SerdStringView string = SERD_STRING("b42");
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const node = serd_nodes_blank(nodes, string);
assert(node);
@@ -370,7 +451,9 @@ test_blank(void)
static void
test_deref(void)
{
- SerdNodes* nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* nodes = serd_nodes_new(allocator);
const SerdNode* original = serd_nodes_string(nodes, SERD_STRING("node"));
const SerdNode* another = serd_nodes_string(nodes, SERD_STRING("node"));
@@ -397,9 +480,9 @@ test_deref(void)
assert(!strcmp(serd_node_string(imposter), "node"));
// Check that dereferencing some random unknown node doesn't crash
- SerdNode* unmanaged = serd_new_string(SERD_STRING("unmanaged"));
+ SerdNode* unmanaged = serd_new_string(NULL, SERD_STRING("unmanaged"));
serd_nodes_deref(nodes, unmanaged);
- serd_node_free(unmanaged);
+ serd_node_free(NULL, unmanaged);
serd_nodes_deref(nodes, NULL);
serd_nodes_deref(nodes, imposter);
@@ -410,8 +493,10 @@ test_deref(void)
static void
test_get(void)
{
- SerdNodes* nodes = serd_nodes_new();
- SerdNode* node = serd_new_string(SERD_STRING("node"));
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* nodes = serd_nodes_new(allocator);
+ SerdNode* node = serd_new_string(NULL, SERD_STRING("node"));
assert(!serd_nodes_get(nodes, NULL));
assert(!serd_nodes_get(nodes, node));
@@ -420,13 +505,15 @@ test_get(void)
assert(serd_node_equals(node, interned1));
assert(serd_nodes_get(nodes, node) == interned1);
- serd_node_free(node);
+ serd_node_free(NULL, node);
serd_nodes_free(nodes);
}
int
main(void)
{
+ test_new_failed_alloc();
+ test_intern_failed_alloc();
test_intern();
test_string();
test_invalid_literal();
diff --git a/test/test_overflow.c b/test/test_overflow.c
index f7fdfa58..2461d713 100644
--- a/test/test_overflow.c
+++ b/test/test_overflow.c
@@ -38,7 +38,7 @@ test_size(SerdWorld* const world,
assert(reader);
- SerdNode* string_name = serd_new_string(SERD_STRING("string"));
+ SerdNode* string_name = serd_new_string(NULL, SERD_STRING("string"));
const char* position = str;
SerdInputStream in = serd_open_input_string(&position);
serd_reader_start(reader, &in, string_name, 1);
@@ -46,7 +46,7 @@ test_size(SerdWorld* const world,
const SerdStatus st = serd_reader_read_document(reader);
serd_close_input(&in);
- serd_node_free(string_name);
+ serd_node_free(NULL, string_name);
serd_reader_free(reader);
serd_env_free(env);
serd_sink_free(sink);
@@ -85,7 +85,7 @@ test_ntriples_overflow(void)
NULL,
};
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
for (const char* const* t = test_strings; *t; ++t) {
test_all_sizes(world, *t, SERD_NTRIPLES, 0u);
@@ -211,7 +211,7 @@ test_turtle_overflow(void)
NULL,
};
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
for (const char* const* t = test_strings; *t; ++t) {
test_all_sizes(world, *t, SERD_TURTLE, SERD_READ_VARIABLES);
diff --git a/test/test_read_chunk.c b/test/test_read_chunk.c
index 59f99910..507bbb35 100644
--- a/test/test_read_chunk.c
+++ b/test/test_read_chunk.c
@@ -91,7 +91,7 @@ on_event(void* handle, const SerdEvent* event)
int
main(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdSink* sink = serd_sink_new(world, NULL, on_event, NULL);
static const char* const string = "@prefix eg: <http://example.org/> .\n"
diff --git a/test/test_reader.c b/test/test_reader.c
index 80d9533b..095f736e 100644
--- a/test/test_reader.c
+++ b/test/test_reader.c
@@ -16,6 +16,8 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
@@ -33,6 +35,89 @@ count_statements(void* handle, const SerdEvent* event)
return SERD_SUCCESS;
}
+static void
+test_new_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ FILE* const temp = tmpfile();
+ assert(temp);
+
+ fprintf(temp, "_:s <http://example.org/p> _:o .\n");
+ fflush(temp);
+ fseek(temp, 0L, SEEK_SET);
+
+ SerdWorld* world = serd_world_new(&allocator.base);
+ size_t ignored = 0u;
+ SerdSink* sink = serd_sink_new(world, &ignored, count_statements, NULL);
+ SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
+
+ // Successfully allocate a reader to count the number of allocations
+ const size_t n_world_allocs = allocator.n_allocations;
+ SerdReader* reader = serd_reader_new(world, SERD_TURTLE, 0u, env, sink, 4096);
+ assert(reader);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_world_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_reader_new(world, SERD_TURTLE, 0u, env, sink, 4096));
+ }
+
+ serd_reader_free(reader);
+ serd_env_free(env);
+ serd_sink_free(sink);
+ serd_world_free(world);
+ fclose(temp);
+}
+
+static void
+test_start_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ FILE* const temp = tmpfile();
+ assert(temp);
+
+ fprintf(temp, "_:s <http://example.org/p> _:o .\n");
+ fflush(temp);
+ fseek(temp, 0L, SEEK_SET);
+
+ SerdWorld* world = serd_world_new(&allocator.base);
+ size_t ignored = 0u;
+ SerdSink* sink = serd_sink_new(world, &ignored, count_statements, NULL);
+ SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
+ SerdReader* reader = serd_reader_new(world, SERD_TURTLE, 0u, env, sink, 4096);
+
+ assert(reader);
+
+ SerdInputStream in = serd_open_input_stream(
+ (SerdReadFunc)fread, (SerdErrorFunc)ferror, NULL, temp);
+
+ // Successfully start a new read to count the number of allocations
+ const size_t n_setup_allocs = allocator.n_allocations;
+ assert(serd_reader_start(reader, &in, NULL, 4096) == SERD_SUCCESS);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_setup_allocs;
+ assert(!serd_reader_finish(reader));
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+
+ in = serd_open_input_stream(
+ (SerdReadFunc)fread, (SerdErrorFunc)ferror, NULL, temp);
+
+ SerdStatus st = serd_reader_start(reader, &in, NULL, 4096);
+ assert(st == SERD_BAD_ALLOC);
+ }
+
+ serd_reader_free(reader);
+ serd_env_free(env);
+ serd_sink_free(sink);
+ serd_world_free(world);
+ fclose(temp);
+}
+
SERD_PURE_FUNC
static size_t
prepare_test_read(void* buf, size_t size, size_t nmemb, void* stream)
@@ -58,7 +143,7 @@ prepare_test_error(void* stream)
static void
test_prepare_error(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
size_t n_statements = 0;
FILE* const f = tmpfile();
@@ -93,7 +178,7 @@ test_prepare_error(void)
static void
test_read_string(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
size_t n_statements = 0;
SerdSink* sink = serd_sink_new(world, &n_statements, count_statements, NULL);
@@ -196,7 +281,7 @@ test_read_eof_by_page(void)
fflush(temp);
fseek(temp, 0L, SEEK_SET);
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
size_t ignored = 0u;
SerdSink* sink = serd_sink_new(world, &ignored, count_statements, NULL);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -224,7 +309,7 @@ test_read_eof_by_page(void)
static void
test_read_eof_by_byte(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
size_t ignored = 0u;
SerdSink* sink = serd_sink_new(world, &ignored, count_statements, NULL);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -252,7 +337,7 @@ test_read_eof_by_byte(void)
static void
test_read_chunks(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
size_t n_statements = 0;
FILE* const f = tmpfile();
static const char null = 0;
@@ -324,12 +409,13 @@ test_read_chunks(void)
static void
test_read_empty(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
size_t n_statements = 0;
FILE* const f = tmpfile();
SerdSink* const sink =
serd_sink_new(world, &n_statements, count_statements, NULL);
+
assert(sink);
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -377,7 +463,7 @@ check_cursor(void* handle, const SerdEvent* event)
static void
test_error_cursor(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
bool called = false;
SerdSink* sink = serd_sink_new(world, &called, check_cursor, NULL);
SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -391,7 +477,7 @@ test_error_cursor(void)
"<http://example.org/s> <http://example.org/p> "
"<http://example.org/o> .";
- SerdNode* const string_name = serd_new_string(SERD_STRING("string"));
+ SerdNode* const string_name = serd_new_string(NULL, SERD_STRING("string"));
const char* position = string;
SerdInputStream in = serd_open_input_string(&position);
@@ -402,7 +488,7 @@ test_error_cursor(void)
assert(called);
assert(!serd_close_input(&in));
- serd_node_free(string_name);
+ serd_node_free(NULL, string_name);
serd_reader_free(reader);
serd_env_free(env);
serd_sink_free(sink);
@@ -412,6 +498,8 @@ test_error_cursor(void)
int
main(void)
{
+ test_new_failed_alloc();
+ test_start_failed_alloc();
test_prepare_error();
test_read_string();
test_read_eof_by_page();
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index 52be8d74..69383225 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -36,8 +36,8 @@ count_statements(void* handle, const SerdEvent* event)
static void
test_writer(const char* const path)
{
+ SerdWorld* world = serd_world_new(NULL);
FILE* fd = fopen(path, "wb");
- SerdWorld* world = serd_world_new();
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
assert(fd);
@@ -108,7 +108,7 @@ test_writer(const char* const path)
serd_close_output(&output);
// Test buffer sink
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
output = serd_open_output_buffer(&buffer);
writer = serd_writer_new(world, SERD_TURTLE, 0, env, &output, 1);
@@ -124,7 +124,7 @@ test_writer(const char* const path)
assert(out);
assert(!strcmp(out, "@base <http://example.org/base> .\n"));
- serd_free(out);
+ serd_free(NULL, buffer.buf);
serd_env_free(env);
serd_world_free(world);
@@ -133,7 +133,7 @@ test_writer(const char* const path)
static void
test_reader(const char* path)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
size_t n_statements = 0;
SerdSink* const sink =
diff --git a/test/test_sink.c b/test/test_sink.c
index 7c49cc04..9812d487 100644
--- a/test/test_sink.c
+++ b/test/test_sink.c
@@ -1,5 +1,5 @@
/*
- Copyright 2019-2020 David Robillard <d@drobilla.net>
+ Copyright 2019-2021 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
@@ -16,6 +16,8 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
@@ -95,10 +97,34 @@ on_event(void* const handle, const SerdEvent* const event)
}
static void
+test_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ const size_t n_world_allocs = allocator.n_allocations;
+
+ // Successfully allocate a sink to count the number of allocations
+ SerdSink* const sink = serd_sink_new(world, NULL, NULL, NULL);
+ assert(sink);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_world_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_sink_new(world, NULL, NULL, NULL));
+ }
+
+ serd_sink_free(sink);
+ serd_world_free(world);
+}
+
+static void
test_callbacks(void)
{
- SerdWorld* const world = serd_world_new();
- SerdNodes* const nodes = serd_nodes_new();
+ SerdWorld* const world = serd_world_new(NULL);
+ SerdAllocator* const allocator = serd_world_allocator(world);
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* base = serd_nodes_uri(nodes, SERD_STRING(NS_EG));
const SerdNode* name = serd_nodes_string(nodes, SERD_STRING("eg"));
@@ -108,7 +134,7 @@ test_callbacks(void)
SerdEnv* env = serd_env_new(world, serd_node_string_view(base));
SerdStatement* const statement =
- serd_statement_new(base, uri, blank, NULL, NULL);
+ serd_statement_new(allocator, base, uri, blank, NULL, NULL);
State state = {0, 0, 0, 0, 0, SERD_SUCCESS};
@@ -162,7 +188,7 @@ test_callbacks(void)
serd_sink_free(sink);
- serd_statement_free(statement);
+ serd_statement_free(allocator, statement);
serd_env_free(env);
serd_nodes_free(nodes);
serd_world_free(world);
@@ -174,7 +200,7 @@ test_free(void)
// Free of null should (as always) not crash
serd_sink_free(NULL);
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
// Set up a sink with dynamically allocated data and a free function
uintptr_t* data = (uintptr_t*)calloc(1, sizeof(uintptr_t));
@@ -189,6 +215,7 @@ test_free(void)
int
main(void)
{
+ test_failed_alloc();
test_callbacks();
test_free();
return 0;
diff --git a/test/test_statement.c b/test/test_statement.c
index 45605dd1..b3f2c483 100644
--- a/test/test_statement.c
+++ b/test/test_statement.c
@@ -16,19 +16,78 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
#include <stddef.h>
+#include <stdint.h>
#define NS_EG "http://example.org/"
static void
+test_invalid_new(void)
+{
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ assert(!serd_statement_copy(allocator, NULL));
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
+
+ const SerdNode* const s = serd_nodes_string(nodes, SERD_STRING("s"));
+ const SerdNode* const u = serd_nodes_uri(nodes, SERD_STRING(NS_EG "u"));
+ const SerdNode* const b = serd_nodes_blank(nodes, SERD_STRING(NS_EG "b"));
+
+ // S, P, and G may not be strings (must be resources)
+ assert(!serd_statement_new(allocator, s, u, u, u, NULL));
+ assert(!serd_statement_new(allocator, u, s, u, u, NULL));
+ assert(!serd_statement_new(allocator, u, u, u, s, NULL));
+
+ // P may not be a blank node
+ assert(!serd_statement_new(allocator, u, b, u, u, NULL));
+
+ serd_nodes_free(nodes);
+}
+
+static void
test_copy(void)
{
- assert(!serd_statement_copy(NULL));
+ assert(!serd_statement_copy(NULL, NULL));
+
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ assert(!serd_statement_copy(allocator, NULL));
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
+
+ const SerdNode* const s = serd_nodes_uri(nodes, SERD_STRING(NS_EG "s"));
+ const SerdNode* const p = serd_nodes_uri(nodes, SERD_STRING(NS_EG "p"));
+ const SerdNode* const o = serd_nodes_uri(nodes, SERD_STRING(NS_EG "o"));
+ const SerdNode* const g = serd_nodes_uri(nodes, SERD_STRING(NS_EG "g"));
+
+ SerdStatement* const statement =
+ serd_statement_new(allocator, s, p, o, g, NULL);
+
+ SerdStatement* const copy = serd_statement_copy(allocator, statement);
+
+ assert(serd_statement_equals(copy, statement));
+
+ serd_statement_free(allocator, copy);
+ serd_statement_free(allocator, statement);
+ serd_nodes_free(nodes);
+}
+
+static void
+test_copy_with_caret(void)
+{
+ assert(!serd_statement_copy(NULL, NULL));
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ assert(!serd_statement_copy(allocator, NULL));
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const f = serd_nodes_string(nodes, SERD_STRING("file"));
const SerdNode* const s = serd_nodes_uri(nodes, SERD_STRING(NS_EG "s"));
@@ -36,29 +95,35 @@ test_copy(void)
const SerdNode* const o = serd_nodes_uri(nodes, SERD_STRING(NS_EG "o"));
const SerdNode* const g = serd_nodes_uri(nodes, SERD_STRING(NS_EG "g"));
- SerdCaret* const caret = serd_caret_new(f, 1, 1);
- SerdStatement* const statement = serd_statement_new(s, p, o, g, caret);
- SerdStatement* const copy = serd_statement_copy(statement);
+ SerdCaret* const caret = serd_caret_new(allocator, f, 1, 1);
+
+ SerdStatement* const statement =
+ serd_statement_new(allocator, s, p, o, g, caret);
+
+ SerdStatement* const copy = serd_statement_copy(allocator, statement);
assert(serd_statement_equals(copy, statement));
assert(serd_caret_equals(serd_statement_caret(copy), caret));
- serd_statement_free(copy);
- serd_caret_free(caret);
- serd_statement_free(statement);
+ serd_statement_free(allocator, copy);
+ serd_caret_free(allocator, caret);
+ serd_statement_free(allocator, statement);
serd_nodes_free(nodes);
}
static void
test_free(void)
{
- serd_statement_free(NULL);
+ serd_statement_free(serd_default_allocator(), NULL);
+ serd_statement_free(NULL, NULL);
}
static void
test_fields(void)
{
- SerdNodes* const nodes = serd_nodes_new();
+ SerdAllocator* const allocator = serd_default_allocator();
+
+ SerdNodes* const nodes = serd_nodes_new(allocator);
const SerdNode* const f = serd_nodes_string(nodes, SERD_STRING("file"));
const SerdNode* const s = serd_nodes_uri(nodes, SERD_STRING(NS_EG "s"));
@@ -66,8 +131,10 @@ test_fields(void)
const SerdNode* const o = serd_nodes_uri(nodes, SERD_STRING(NS_EG "o"));
const SerdNode* const g = serd_nodes_uri(nodes, SERD_STRING(NS_EG "g"));
- SerdCaret* const caret = serd_caret_new(f, 1, 1);
- SerdStatement* const statement = serd_statement_new(s, p, o, g, caret);
+ SerdCaret* const caret = serd_caret_new(allocator, f, 1, 1);
+
+ SerdStatement* const statement =
+ serd_statement_new(allocator, s, p, o, g, caret);
assert(serd_statement_equals(statement, statement));
assert(!serd_statement_equals(statement, NULL));
@@ -94,33 +161,86 @@ test_fields(void)
assert(!serd_statement_matches(statement, NULL, NULL, s, NULL));
assert(!serd_statement_matches(statement, NULL, NULL, NULL, s));
- SerdStatement* const diff_s = serd_statement_new(o, p, o, g, caret);
+ SerdStatement* const diff_s =
+ serd_statement_new(allocator, o, p, o, g, caret);
assert(!serd_statement_equals(statement, diff_s));
- serd_statement_free(diff_s);
+ serd_statement_free(allocator, diff_s);
- SerdStatement* const diff_p = serd_statement_new(s, o, o, g, caret);
+ SerdStatement* const diff_p =
+ serd_statement_new(allocator, s, o, o, g, caret);
assert(!serd_statement_equals(statement, diff_p));
- serd_statement_free(diff_p);
+ serd_statement_free(allocator, diff_p);
- SerdStatement* const diff_o = serd_statement_new(s, p, s, g, caret);
+ SerdStatement* const diff_o =
+ serd_statement_new(allocator, s, p, s, g, caret);
assert(!serd_statement_equals(statement, diff_o));
- serd_statement_free(diff_o);
+ serd_statement_free(allocator, diff_o);
- SerdStatement* const diff_g = serd_statement_new(s, p, o, s, caret);
+ SerdStatement* const diff_g =
+ serd_statement_new(allocator, s, p, o, s, caret);
assert(!serd_statement_equals(statement, diff_g));
- serd_statement_free(diff_g);
+ serd_statement_free(allocator, diff_g);
+
+ serd_statement_free(allocator, statement);
+ serd_caret_free(allocator, caret);
+ serd_nodes_free(nodes);
+}
+
+static void
+test_failed_alloc(void)
+{
+ SerdNodes* const nodes = serd_nodes_new(serd_default_allocator());
+
+ const SerdNode* const f = serd_nodes_string(nodes, SERD_STRING("file"));
+ const SerdNode* const s = serd_nodes_uri(nodes, SERD_STRING(NS_EG "s"));
+ const SerdNode* const p = serd_nodes_uri(nodes, SERD_STRING(NS_EG "p"));
+ const SerdNode* const o = serd_nodes_uri(nodes, SERD_STRING(NS_EG "o"));
+ const SerdNode* const g = serd_nodes_uri(nodes, SERD_STRING(NS_EG "g"));
- serd_statement_free(statement);
- serd_caret_free(caret);
+ SerdCaret* const caret = serd_caret_new(serd_default_allocator(), f, 1, 1);
+
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ // Successfully allocate a statement to count the number of allocations
+ SerdStatement* const statement =
+ serd_statement_new(&allocator.base, s, p, o, g, caret);
+ assert(statement);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations;
+ for (size_t i = 0u; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_statement_new(&allocator.base, s, p, o, g, caret));
+ }
+
+ // Successfully copy the statement to count the number of allocations
+ allocator.n_allocations = 0;
+ allocator.n_remaining = SIZE_MAX;
+ SerdStatement* const copy = serd_statement_copy(&allocator.base, statement);
+ assert(copy);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_copy_allocs = allocator.n_allocations;
+ for (size_t i = 0u; i < n_copy_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_statement_copy(&allocator.base, statement));
+ }
+
+ serd_statement_free(&allocator.base, copy);
+ serd_statement_free(&allocator.base, statement);
+ serd_caret_free(&allocator.base, caret);
serd_nodes_free(nodes);
}
int
main(void)
{
+ test_invalid_new();
test_copy();
+ test_copy_with_caret();
test_free();
test_fields();
+ test_failed_alloc();
return 0;
}
diff --git a/test/test_string.c b/test/test_string.c
index c91284bb..cdc4bc33 100644
--- a/test/test_string.c
+++ b/test/test_string.c
@@ -48,7 +48,7 @@ test_strerror(void)
static void
test_canonical_path(const char* const path)
{
- char* const canonical = serd_canonical_path(path);
+ char* const canonical = serd_canonical_path(NULL, path);
assert(canonical);
assert(!strstr(canonical, "../"));
@@ -62,7 +62,7 @@ test_canonical_path(const char* const path)
assert(canonical[0] == '/');
#endif
- serd_free(canonical);
+ serd_free(NULL, canonical);
}
int
diff --git a/test/test_terse_write.c b/test/test_terse_write.c
index 02f6bebf..480bb880 100644
--- a/test/test_terse_write.c
+++ b/test/test_terse_write.c
@@ -20,7 +20,6 @@
#include <assert.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
@@ -42,10 +41,10 @@ check_output(SerdWriter* writer, SerdBuffer* buffer, const char* expected)
static int
test(void)
{
- SerdBuffer buffer = {NULL, 0};
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
- SerdNodes* nodes = serd_nodes_new();
+ SerdNodes* nodes = serd_nodes_new(serd_world_allocator(world));
const SerdNode* b1 = serd_nodes_blank(nodes, SERD_STRING("b1"));
const SerdNode* l1 = serd_nodes_blank(nodes, SERD_STRING("l1"));
@@ -100,10 +99,10 @@ test(void)
serd_writer_free(writer);
serd_close_output(&output);
+ serd_free(NULL, buffer.buf);
serd_nodes_free(nodes);
serd_env_free(env);
serd_world_free(world);
- free(buffer.buf);
return 0;
}
diff --git a/test/test_uri.c b/test/test_uri.c
index 5d204a71..49d72630 100644
--- a/test/test_uri.c
+++ b/test/test_uri.c
@@ -1,5 +1,5 @@
/*
- Copyright 2011-2020 David Robillard <d@drobilla.net>
+ Copyright 2011-2021 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
@@ -16,6 +16,8 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
@@ -24,6 +26,35 @@
#include <string.h>
static void
+test_file_uri_failed_alloc(void)
+{
+ static const char* const string = "file://host/path/spacey%20dir/100%%.ttl";
+
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ // Successfully parse a URI to count the number of allocations
+ char* hostname = NULL;
+ char* path = serd_parse_file_uri(&allocator.base, string, &hostname);
+
+ assert(!strcmp(path, "/path/spacey dir/100%.ttl"));
+ assert(!strcmp(hostname, "host"));
+ serd_free(&allocator.base, path);
+ serd_free(&allocator.base, hostname);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_allocs = allocator.n_allocations;
+ for (size_t i = 0; i < n_allocs; ++i) {
+ allocator.n_remaining = i;
+
+ path = serd_parse_file_uri(&allocator.base, string, &hostname);
+ assert(!path || !hostname);
+
+ serd_free(&allocator.base, path);
+ serd_free(&allocator.base, hostname);
+ }
+}
+
+static void
test_uri_string_has_scheme(void)
{
assert(!serd_uri_string_has_scheme("relative"));
@@ -55,19 +86,20 @@ test_file_uri(const char* const hostname,
}
SerdNode* node =
- serd_new_file_uri(SERD_STRING(path), SERD_OPTIONAL_STRING(hostname));
+ serd_new_file_uri(NULL, SERD_STRING(path), SERD_OPTIONAL_STRING(hostname));
const char* node_str = serd_node_string(node);
char* out_hostname = NULL;
- char* out_path = serd_parse_file_uri(node_str, &out_hostname);
+ char* out_path = serd_parse_file_uri(NULL, node_str, &out_hostname);
+
assert(!strcmp(node_str, expected_uri));
assert((hostname && out_hostname) || (!hostname && !out_hostname));
assert(!hostname || !strcmp(hostname, out_hostname));
assert(!strcmp(out_path, expected_path));
- serd_free(out_path);
- serd_free(out_hostname);
- serd_node_free(node);
+ serd_free(NULL, out_path);
+ serd_free(NULL, out_hostname);
+ serd_node_free(NULL, node);
}
static void
@@ -80,22 +112,22 @@ test_uri_parsing(void)
test_file_uri(NULL, "a/relative <path>", "a/relative%20%3Cpath%3E", NULL);
// Missing trailing '/' after authority
- assert(!serd_parse_file_uri("file://truncated", NULL));
+ assert(!serd_parse_file_uri(NULL, "file://truncated", NULL));
// Check that NULL hostname doesn't crash
- char* out_path = serd_parse_file_uri("file://me/path", NULL);
+ char* out_path = serd_parse_file_uri(NULL, "file://me/path", NULL);
assert(!strcmp(out_path, "/path"));
- serd_free(out_path);
+ serd_free(NULL, out_path);
// Invalid first escape character
- out_path = serd_parse_file_uri("file:///foo/%0Xbar", NULL);
+ out_path = serd_parse_file_uri(NULL, "file:///foo/%0Xbar", NULL);
assert(!strcmp(out_path, "/foo/bar"));
- serd_free(out_path);
+ serd_free(NULL, out_path);
// Invalid second escape character
- out_path = serd_parse_file_uri("file:///foo/%X0bar", NULL);
+ out_path = serd_parse_file_uri(NULL, "file:///foo/%X0bar", NULL);
assert(!strcmp(out_path, "/foo/bar"));
- serd_free(out_path);
+ serd_free(NULL, out_path);
}
static void
@@ -107,12 +139,12 @@ test_parse_uri(void)
const SerdURIView empty_uri = serd_parse_uri("");
SerdNode* const nil =
- serd_new_parsed_uri(serd_resolve_uri(empty_uri, base_uri));
+ serd_new_parsed_uri(NULL, serd_resolve_uri(empty_uri, base_uri));
assert(serd_node_type(nil) == SERD_URI);
assert(!strcmp(serd_node_string(nil), base.buf));
- serd_node_free(nil);
+ serd_node_free(NULL, nil);
}
static void
@@ -161,11 +193,11 @@ check_rel_uri(const char* uri_string,
!root || serd_uri_is_within(uri, serd_node_uri_view(root));
SerdNode* const rel =
- is_within ? serd_new_parsed_uri(serd_relative_uri(uri, base_uri))
- : serd_new_uri(SERD_STRING(uri_string));
+ is_within ? serd_new_parsed_uri(NULL, serd_relative_uri(uri, base_uri))
+ : serd_new_uri(NULL, SERD_STRING(uri_string));
const int ret = strcmp(serd_node_string(rel), expected);
- serd_node_free(rel);
+ serd_node_free(NULL, rel);
assert(!ret);
}
@@ -173,9 +205,10 @@ static void
test_relative_uri(void)
{
SerdNode* const root =
- serd_new_uri(SERD_STRING("http://example.org/a/b/ignored"));
+ serd_new_uri(NULL, SERD_STRING("http://example.org/a/b/ignored"));
- SerdNode* const base = serd_new_uri(SERD_STRING("http://example.org/a/b/c/"));
+ SerdNode* const base =
+ serd_new_uri(NULL, SERD_STRING("http://example.org/a/b/c/"));
check_rel_uri("http://example.org/a/b/c/foo", base, NULL, "foo");
check_rel_uri("http://example.org/a/", base, NULL, "../../");
@@ -189,10 +222,10 @@ test_relative_uri(void)
const SerdURIView ref = serd_parse_uri("child");
const SerdURIView abs = serd_resolve_uri(ref, serd_node_uri_view(base));
const SerdURIView rel = serd_relative_uri(abs, serd_node_uri_view(root));
- SerdNode* const node = serd_new_parsed_uri(rel);
+ SerdNode* const node = serd_new_parsed_uri(NULL, rel);
assert(!strcmp(serd_node_string(node), "c/child"));
- serd_node_free(node);
+ serd_node_free(NULL, node);
}
{
// Check failure when path_prefix is not available for use
@@ -204,8 +237,8 @@ test_relative_uri(void)
assert(!memcmp(&upref, &SERD_URI_NULL, sizeof(ref)));
}
- serd_node_free(base);
- serd_node_free(root);
+ serd_node_free(NULL, base);
+ serd_node_free(NULL, root);
}
static void
@@ -221,15 +254,16 @@ test_uri_resolution(void)
const SerdURIView rel_foo_uri = serd_relative_uri(abs_foo_uri, base_uri);
const SerdURIView resolved_uri = serd_resolve_uri(rel_foo_uri, base_uri);
- SerdNode* const resolved = serd_new_parsed_uri(resolved_uri);
+ SerdNode* const resolved = serd_new_parsed_uri(NULL, resolved_uri);
assert(!strcmp(serd_node_string(resolved), "http://example.org/a/b/c/foo"));
- serd_node_free(resolved);
+ serd_node_free(NULL, resolved);
}
int
main(void)
{
+ test_file_uri_failed_alloc();
test_uri_string_has_scheme();
test_uri_parsing();
test_parse_uri();
diff --git a/test/test_world.c b/test/test_world.c
index 8045d1bf..23958b19 100644
--- a/test/test_world.c
+++ b/test/test_world.c
@@ -16,6 +16,8 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
@@ -23,9 +25,28 @@
#include <string.h>
static void
+test_new_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ // Successfully allocate a world to count the number of allocations
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ assert(world);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_world_new(&allocator.base));
+ }
+
+ serd_world_free(world);
+}
+
+static void
test_get_blank(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
char expected[12];
for (unsigned i = 0; i < 32; ++i) {
@@ -41,7 +62,7 @@ test_get_blank(void)
static void
test_nodes(void)
{
- SerdWorld* const world = serd_world_new();
+ SerdWorld* const world = serd_world_new(NULL);
SerdNodes* const nodes = serd_world_nodes(world);
assert(serd_nodes_size(nodes) > 0u);
@@ -52,6 +73,7 @@ test_nodes(void)
int
main(void)
{
+ test_new_failed_alloc();
test_get_blank();
test_nodes();
diff --git a/test/test_writer.c b/test/test_writer.c
index 7e2386be..23ac222c 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -16,6 +16,8 @@
#undef NDEBUG
+#include "failing_allocator.h"
+
#include "serd/serd.h"
#include <assert.h>
@@ -28,23 +30,112 @@
static void
test_writer_new(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {serd_world_allocator(world), NULL, 0};
SerdOutputStream output = serd_open_output_buffer(&buffer);
assert(!serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 0));
+ serd_env_free(env);
serd_world_free(world);
+}
+
+static void
+test_new_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* const world = serd_world_new(&allocator.base);
+ SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
+ SerdBuffer buffer = {&allocator.base, NULL, 0};
+ SerdOutputStream output = serd_open_output_buffer(&buffer);
+ const size_t n_world_allocs = allocator.n_allocations;
+
+ // Successfully allocate a writer to count the number of allocations
+ SerdWriter* const writer =
+ serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1);
+
+ assert(writer);
+
+ // Test that each allocation failing is handled gracefully
+ const size_t n_new_allocs = allocator.n_allocations - n_world_allocs;
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+ assert(!serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1));
+ }
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+ serd_world_free(world);
+}
+
+static void
+test_write_failed_alloc(void)
+{
+ SerdFailingAllocator allocator = serd_failing_allocator();
+
+ SerdWorld* world = serd_world_new(&allocator.base);
+ SerdNodes* nodes = serd_world_nodes(world);
+ SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
+ SerdBuffer buffer = {&allocator.base, NULL, 0};
+ SerdOutputStream output = serd_open_output_buffer(&buffer);
+
+ const SerdNode* s =
+ serd_nodes_uri(nodes, SERD_STRING("http://example.org/s"));
+
+ const SerdNode* p1 =
+ serd_nodes_uri(nodes, SERD_STRING("http://example.org/p"));
+
+ const SerdNode* p2 = serd_nodes_uri(
+ nodes, SERD_STRING("http://example.org/dramatically/longer/predicate"));
+
+ const SerdNode* o = serd_nodes_blank(nodes, SERD_STRING("o"));
+
+ const size_t n_setup_allocs = allocator.n_allocations;
+
+ // Successfully write a statement to count the number of allocations
+ SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1);
+ const SerdSink* sink = serd_writer_sink(writer);
+ assert(writer);
+ assert(sink);
+ assert(!serd_sink_write(sink, 0u, s, p1, o, NULL));
+ assert(!serd_sink_write(sink, 0u, s, p2, o, NULL));
+ const size_t n_new_allocs = allocator.n_allocations - n_setup_allocs;
+
+ serd_writer_free(writer);
+
+ // Test that each allocation failing is handled gracefully
+ for (size_t i = 0; i < n_new_allocs; ++i) {
+ allocator.n_remaining = i;
+
+ if ((writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1))) {
+ sink = serd_writer_sink(writer);
+
+ const SerdStatus st1 = serd_sink_write(sink, 0u, s, p1, o, NULL);
+ const SerdStatus st2 = serd_sink_write(sink, 0u, s, p2, o, NULL);
+
+ assert(st1 == SERD_BAD_ALLOC || st1 == SERD_BAD_WRITE ||
+ st2 == SERD_BAD_ALLOC || st2 == SERD_BAD_WRITE);
+
+ serd_writer_free(writer);
+ }
+ }
+
+ serd_close_output(&output);
serd_env_free(env);
+ serd_buffer_close(&buffer);
+ serd_free(NULL, buffer.buf);
+
+ serd_world_free(world);
}
static void
test_write_bad_event(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdOutputStream output = serd_open_output_buffer(&buffer);
SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1);
@@ -60,7 +151,7 @@ test_write_bad_event(void)
assert(out);
assert(!strcmp(out, ""));
- serd_free(out);
+ serd_free(NULL, buffer.buf);
serd_writer_free(writer);
serd_env_free(env);
@@ -70,10 +161,10 @@ test_write_bad_event(void)
static void
test_write_long_literal(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdNodes* nodes = serd_world_nodes(world);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdOutputStream output = serd_open_output_buffer(&buffer);
SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1);
@@ -106,7 +197,7 @@ test_write_long_literal(void)
"\t<http://example.org/p> \"\"\"hello \"\"\\\"world\"\"\\\"!\"\"\" .\n";
assert(!strcmp(out, expected));
- serd_free(out);
+ serd_free(NULL, buffer.buf);
serd_world_free(world);
}
@@ -126,7 +217,7 @@ null_sink(const void* const buf,
static void
test_writer_stack_overflow(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdNodes* nodes = serd_world_nodes(world);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -178,7 +269,7 @@ test_strict_write(void)
{
static const char* path = "serd_strict_write_test.ttl";
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdNodes* nodes = serd_world_nodes(world);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -231,7 +322,7 @@ faulty_sink(const void* const buf,
static void
test_write_error(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdNodes* nodes = serd_world_nodes(world);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -277,7 +368,7 @@ test_write_error(void)
static void
test_write_empty_syntax(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdNodes* nodes = serd_world_nodes(world);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -290,7 +381,7 @@ test_write_empty_syntax(void)
const SerdNode* o =
serd_nodes_uri(nodes, SERD_STRING("http://example.org/o"));
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdOutputStream output = serd_open_output_buffer(&buffer);
SerdWriter* writer =
@@ -305,7 +396,7 @@ test_write_empty_syntax(void)
assert(out);
assert(strlen(out) == 0);
- serd_free(out);
+ serd_free(NULL, buffer.buf);
serd_writer_free(writer);
serd_close_output(&output);
@@ -316,7 +407,7 @@ test_write_empty_syntax(void)
static void
test_write_bad_uri(void)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdNodes* nodes = serd_world_nodes(world);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
@@ -328,7 +419,7 @@ test_write_bad_uri(void)
const SerdNode* rel = serd_nodes_uri(nodes, SERD_STRING("rel"));
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdOutputStream output = serd_open_output_buffer(&buffer);
SerdWriter* writer =
@@ -343,7 +434,7 @@ test_write_bad_uri(void)
assert(st == SERD_BAD_ARG);
serd_close_output(&output);
- serd_free(buffer.buf);
+ serd_free(NULL, buffer.buf);
serd_writer_free(writer);
serd_close_output(&output);
serd_env_free(env);
@@ -353,10 +444,10 @@ test_write_bad_uri(void)
static void
check_pname_escape(const char* const lname, const char* const expected)
{
- SerdWorld* world = serd_world_new();
+ SerdWorld* world = serd_world_new(NULL);
SerdNodes* nodes = serd_world_nodes(world);
SerdEnv* env = serd_env_new(world, SERD_EMPTY_STRING());
- SerdBuffer buffer = {NULL, 0};
+ SerdBuffer buffer = {NULL, NULL, 0};
SerdOutputStream output = serd_open_output_buffer(&buffer);
SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0u, env, &output, 1);
@@ -389,7 +480,7 @@ check_pname_escape(const char* const lname, const char* const expected)
char* out = (char*)buffer.buf;
assert(!strcmp(out, expected));
- serd_free(out);
+ serd_free(NULL, buffer.buf);
free(uri);
serd_world_free(world);
@@ -430,6 +521,8 @@ int
main(void)
{
test_writer_new();
+ test_new_failed_alloc();
+ test_write_failed_alloc();
test_write_bad_event();
test_write_long_literal();
test_writer_stack_overflow();
diff --git a/tools/console.c b/tools/console.c
index c2166130..65b06b7b 100644
--- a/tools/console.c
+++ b/tools/console.c
@@ -49,7 +49,7 @@ serd_tool_setup(SerdTool* const tool,
}
// We have something to write to, so build the writing environment
- if (!(tool->world = serd_world_new()) ||
+ if (!(tool->world = serd_world_new(NULL)) ||
!(tool->env = serd_create_env(
tool->world, program, options.base_uri, options.out_filename)) ||
!(tool->writer = serd_writer_new(
@@ -390,7 +390,7 @@ serd_set_base_uri_from_path(SerdEnv* const env, const char* const path)
return SERD_BAD_ARG;
}
- char* const real_path = serd_canonical_path(path);
+ char* const real_path = serd_canonical_path(NULL, path);
if (!real_path) {
return SERD_BAD_ARG;
}
@@ -399,18 +399,22 @@ serd_set_base_uri_from_path(SerdEnv* const env, const char* const path)
SerdNode* base_node = NULL;
if (path[path_len - 1] == '/' || path[path_len - 1] == '\\') {
char* const base_path = (char*)calloc(real_path_len + 2, 1);
+
memcpy(base_path, real_path, real_path_len);
base_path[real_path_len] = path[path_len - 1];
- base_node = serd_new_file_uri(SERD_STRING(base_path), SERD_EMPTY_STRING());
+ base_node =
+ serd_new_file_uri(NULL, SERD_STRING(base_path), SERD_EMPTY_STRING());
+
free(base_path);
} else {
- base_node = serd_new_file_uri(SERD_STRING(real_path), SERD_EMPTY_STRING());
+ base_node =
+ serd_new_file_uri(NULL, SERD_STRING(real_path), SERD_EMPTY_STRING());
}
serd_env_set_base_uri(env, serd_node_string_view(base_node));
- serd_node_free(base_node);
- serd_free(real_path);
+ serd_node_free(NULL, base_node);
+ serd_free(NULL, real_path);
return SERD_SUCCESS;
}
@@ -427,9 +431,9 @@ serd_read_source(SerdWorld* const world,
SerdReader* const reader = serd_reader_new(
world, syntax, opts.input.flags, env, sink, opts.stack_size);
- SerdNode* const name_node = serd_new_string(SERD_STRING(name));
+ SerdNode* const name_node = serd_new_string(NULL, SERD_STRING(name));
SerdStatus st = serd_reader_start(reader, in, name_node, opts.block_size);
- serd_node_free(name_node);
+ serd_node_free(NULL, name_node);
if (!st) {
st = serd_reader_read_document(reader);
}
diff --git a/tools/serd-filter.c b/tools/serd-filter.c
index 199125d5..adf2ab40 100644
--- a/tools/serd-filter.c
+++ b/tools/serd-filter.c
@@ -44,21 +44,30 @@ typedef struct {
SerdNode* g;
} FilterPattern;
+// Context for the pattern event callback
+typedef struct {
+ SerdAllocator* allocator;
+ FilterPattern pattern;
+} PatternEventContext;
+
// Handler for events read from a pattern
static SerdStatus
on_pattern_event(void* const handle, const SerdEvent* const event)
{
+ PatternEventContext* const ctx = (PatternEventContext*)handle;
+ SerdAllocator* const allocator = ctx->allocator;
+
if (event->type == SERD_STATEMENT) {
- FilterPattern* const pat = (FilterPattern*)handle;
+ FilterPattern* const pat = &ctx->pattern;
if (pat->s) {
return SERD_BAD_PATTERN;
}
const SerdStatement* const statement = event->statement.statement;
- pat->s = serd_node_copy(serd_statement_subject(statement));
- pat->p = serd_node_copy(serd_statement_predicate(statement));
- pat->o = serd_node_copy(serd_statement_object(statement));
- pat->g = serd_node_copy(serd_statement_graph(statement));
+ pat->s = serd_node_copy(allocator, serd_statement_subject(statement));
+ pat->p = serd_node_copy(allocator, serd_statement_predicate(statement));
+ pat->o = serd_node_copy(allocator, serd_statement_object(statement));
+ pat->g = serd_node_copy(allocator, serd_statement_graph(statement));
}
return SERD_SUCCESS;
@@ -71,10 +80,12 @@ parse_pattern(SerdWorld* const world,
SerdInputStream* const in,
const bool inclusive)
{
- SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
- FilterPattern pat = {NULL, NULL, NULL, NULL};
- SerdSink* in_sink = serd_sink_new(world, &pat, on_pattern_event, NULL);
- SerdReader* reader = serd_reader_new(
+ SerdAllocator* const allocator = serd_world_allocator(world);
+ SerdEnv* const env = serd_env_new(world, SERD_EMPTY_STRING());
+ PatternEventContext ctx = {allocator, {NULL, NULL, NULL, NULL}};
+
+ SerdSink* in_sink = serd_sink_new(world, &ctx, on_pattern_event, NULL);
+ SerdReader* reader = serd_reader_new(
world, SERD_NQUADS, SERD_READ_VARIABLES, env, in_sink, 4096);
const SerdNode* pattern_name =
@@ -98,13 +109,18 @@ parse_pattern(SerdWorld* const world,
return NULL;
}
- SerdSink* filter =
- serd_filter_new(world, sink, pat.s, pat.p, pat.o, pat.g, inclusive);
-
- serd_node_free(pat.s);
- serd_node_free(pat.p);
- serd_node_free(pat.o);
- serd_node_free(pat.g);
+ SerdSink* filter = serd_filter_new(world,
+ sink,
+ ctx.pattern.s,
+ ctx.pattern.p,
+ ctx.pattern.o,
+ ctx.pattern.g,
+ inclusive);
+
+ serd_node_free(allocator, ctx.pattern.s);
+ serd_node_free(allocator, ctx.pattern.p);
+ serd_node_free(allocator, ctx.pattern.o);
+ serd_node_free(allocator, ctx.pattern.g);
return filter;
}