aboutsummaryrefslogtreecommitdiffstats
path: root/include/serd
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-10-27 14:15:31 -0400
committerDavid Robillard <d@drobilla.net>2022-01-28 21:57:24 -0500
commit30487c277ac5d4be5786733ca7b98adb4c810ae9 (patch)
treef1b00a7725d74a594fcd91de2aea924485356528 /include/serd
parent56cceb103dc633d6af957472945e792187a5dd4e (diff)
downloadserd-30487c277ac5d4be5786733ca7b98adb4c810ae9.tar.gz
serd-30487c277ac5d4be5786733ca7b98adb4c810ae9.tar.bz2
serd-30487c277ac5d4be5786733ca7b98adb4c810ae9.zip
Add custom allocator support
Diffstat (limited to 'include/serd')
-rw-r--r--include/serd/serd.h262
1 files changed, 201 insertions, 61 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