diff options
author | David Robillard <d@drobilla.net> | 2021-10-27 14:15:31 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-01-28 21:57:24 -0500 |
commit | 30487c277ac5d4be5786733ca7b98adb4c810ae9 (patch) | |
tree | f1b00a7725d74a594fcd91de2aea924485356528 | |
parent | 56cceb103dc633d6af957472945e792187a5dd4e (diff) | |
download | serd-30487c277ac5d4be5786733ca7b98adb4c810ae9.tar.gz serd-30487c277ac5d4be5786733ca7b98adb4c810ae9.tar.bz2 serd-30487c277ac5d4be5786733ca7b98adb4c810ae9.zip |
Add custom allocator support
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, ©, 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, ©, NULL, (flags & SERD_NO_TYPE_FIRST)); + } + zix_hash_free(list_subjects); return st; } @@ -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 @@ -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 @@ -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; } @@ -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); } } @@ -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 @@ -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; } |