mongory 0.7.6 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +0 -14
- data/ext/mongory_ext/extconf.rb +19 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/array.h +122 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/config.h +161 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/error.h +79 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/memory_pool.h +95 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/table.h +127 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/value.h +175 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/matchers/matcher.h +76 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core.h +12 -0
- data/ext/mongory_ext/mongory-core/src/foundations/array.c +287 -0
- data/ext/mongory_ext/mongory-core/src/foundations/array_private.h +19 -0
- data/ext/mongory_ext/mongory-core/src/foundations/config.c +270 -0
- data/ext/mongory_ext/mongory-core/src/foundations/config_private.h +48 -0
- data/ext/mongory_ext/mongory-core/src/foundations/error.c +38 -0
- data/ext/mongory_ext/mongory-core/src/foundations/memory_pool.c +298 -0
- data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.c +65 -0
- data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.h +49 -0
- data/ext/mongory_ext/mongory-core/src/foundations/table.c +498 -0
- data/ext/mongory_ext/mongory-core/src/foundations/utils.c +210 -0
- data/ext/mongory_ext/mongory-core/src/foundations/utils.h +70 -0
- data/ext/mongory_ext/mongory-core/src/foundations/value.c +500 -0
- data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.c +164 -0
- data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.h +47 -0
- data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.c +122 -0
- data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.h +100 -0
- data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.c +217 -0
- data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.h +83 -0
- data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.c +573 -0
- data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.h +125 -0
- data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.c +147 -0
- data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.h +48 -0
- data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.c +124 -0
- data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.h +46 -0
- data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.c +126 -0
- data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.h +46 -0
- data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.c +314 -0
- data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.h +97 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher.c +252 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.c +79 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.h +23 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.c +60 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.h +23 -0
- data/lib/mongory/version.rb +1 -1
- data/lib/mongory.rb +2 -2
- metadata +43 -2
@@ -0,0 +1,270 @@
|
|
1
|
+
/**
|
2
|
+
* @file config.c
|
3
|
+
* @brief Implements global configuration and initialization for the Mongory
|
4
|
+
* library.
|
5
|
+
*
|
6
|
+
* This file manages the lifecycle of global resources such as the internal
|
7
|
+
* memory pool, regex adapter, matcher registration table, and value converters.
|
8
|
+
* It provides the `mongory_init` and `mongory_cleanup` functions, as well as
|
9
|
+
* setters for customizable components and a string copy utility.
|
10
|
+
*/
|
11
|
+
#include "mongory-core/foundations/config.h"
|
12
|
+
#include "../matchers/base_matcher.h" // For mongory_matcher_build_func
|
13
|
+
#include "../matchers/compare_matcher.h" // For specific matcher constructors
|
14
|
+
#include "../matchers/composite_matcher.h" // For specific matcher constructors
|
15
|
+
#include "../matchers/existance_matcher.h" // For specific matcher constructors
|
16
|
+
#include "../matchers/inclusion_matcher.h" // For specific matcher constructors
|
17
|
+
#include "../matchers/literal_matcher.h" // For specific matcher constructors
|
18
|
+
#include "../matchers/external_matcher.h" // For specific matcher constructors
|
19
|
+
#include "config_private.h" // For mongory_regex_adapter, mongory_value_converter, etc.
|
20
|
+
#include "mongory-core/foundations/memory_pool.h"
|
21
|
+
#include "mongory-core/foundations/table.h"
|
22
|
+
#include "mongory-core/foundations/value.h"
|
23
|
+
|
24
|
+
static bool mongory_regex_default_func(mongory_memory_pool *pool, mongory_value *pattern, mongory_value *value);
|
25
|
+
static char *mongory_regex_default_stringify_func(mongory_memory_pool *pool, mongory_value *pattern);
|
26
|
+
|
27
|
+
// Global internal memory pool for the library.
|
28
|
+
mongory_memory_pool *mongory_internal_pool = NULL;
|
29
|
+
// Global adapter for regex operations.
|
30
|
+
mongory_regex_adapter mongory_internal_regex_adapter = {
|
31
|
+
.match_func = mongory_regex_default_func,
|
32
|
+
.stringify_func = mongory_regex_default_stringify_func,
|
33
|
+
};
|
34
|
+
// Global table mapping matcher names (e.g., "$eq") to their build functions.
|
35
|
+
mongory_table *mongory_matcher_mapping = NULL;
|
36
|
+
// Global converter for handling external data types.
|
37
|
+
mongory_value_converter mongory_internal_value_converter = {
|
38
|
+
.deep_convert = NULL,
|
39
|
+
.shallow_convert = NULL,
|
40
|
+
.recover = NULL,
|
41
|
+
};
|
42
|
+
// Global adapter for custom matchers.
|
43
|
+
mongory_matcher_custom_adapter mongory_custom_matcher_adapter = {
|
44
|
+
.build = NULL,
|
45
|
+
.lookup = NULL,
|
46
|
+
.match = NULL,
|
47
|
+
};
|
48
|
+
|
49
|
+
bool mongory_matcher_trace_result_colorful = true;
|
50
|
+
|
51
|
+
/**
|
52
|
+
* @brief Initializes the internal memory pool if it hasn't been already.
|
53
|
+
* This pool is used for allocations by various library components.
|
54
|
+
*/
|
55
|
+
static inline void mongory_internal_pool_init() {
|
56
|
+
if (mongory_internal_pool != NULL) {
|
57
|
+
return; // Already initialized.
|
58
|
+
}
|
59
|
+
mongory_internal_pool = mongory_memory_pool_new();
|
60
|
+
// TODO: Add error handling if mongory_memory_pool_new returns NULL.
|
61
|
+
}
|
62
|
+
|
63
|
+
/**
|
64
|
+
* @brief Default regex function that always returns false.
|
65
|
+
* Used if no custom regex function is set via mongory_regex_func_set.
|
66
|
+
* @param pool Unused.
|
67
|
+
* @param pattern Unused.
|
68
|
+
* @param value Unused.
|
69
|
+
* @return Always false.
|
70
|
+
*/
|
71
|
+
static bool mongory_regex_default_func(mongory_memory_pool *pool, mongory_value *pattern, mongory_value *value) {
|
72
|
+
(void)pool; // Mark as unused to prevent compiler warnings.
|
73
|
+
(void)pattern; // Mark as unused.
|
74
|
+
(void)value; // Mark as unused.
|
75
|
+
return false; // Default behavior is no match.
|
76
|
+
}
|
77
|
+
|
78
|
+
static char *mongory_regex_default_stringify_func(mongory_memory_pool *pool, mongory_value *pattern) {
|
79
|
+
(void)pool; // Mark as unused to prevent compiler warnings.
|
80
|
+
(void)pattern; // Mark as unused.
|
81
|
+
return "//"; // Default behavior is no stringification.
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* @brief Sets the global regex matching function.
|
86
|
+
* Initializes the regex adapter if it's not already.
|
87
|
+
* @param func The custom regex function to use.
|
88
|
+
*/
|
89
|
+
void mongory_regex_func_set(mongory_regex_func func) {
|
90
|
+
mongory_internal_regex_adapter.match_func = func;
|
91
|
+
}
|
92
|
+
|
93
|
+
/**
|
94
|
+
* @brief Sets the global regex stringify function.
|
95
|
+
* Initializes the regex adapter if it's not already.
|
96
|
+
* @param func The custom regex stringify function to use.
|
97
|
+
*/
|
98
|
+
void mongory_regex_stringify_func_set(mongory_regex_stringify_func func) {
|
99
|
+
mongory_internal_regex_adapter.stringify_func = func;
|
100
|
+
}
|
101
|
+
/**
|
102
|
+
* @brief Initializes the matcher mapping table if it hasn't been already.
|
103
|
+
* This table stores registrations of matcher names to their constructor
|
104
|
+
* functions.
|
105
|
+
*/
|
106
|
+
static inline void mongory_matcher_mapping_init() {
|
107
|
+
if (mongory_matcher_mapping != NULL) {
|
108
|
+
return; // Already initialized.
|
109
|
+
}
|
110
|
+
|
111
|
+
// Ensure the internal pool is initialized first.
|
112
|
+
mongory_internal_pool_init();
|
113
|
+
if (mongory_internal_pool == NULL) {
|
114
|
+
return; // Cannot proceed if pool initialization failed.
|
115
|
+
}
|
116
|
+
|
117
|
+
mongory_matcher_mapping = mongory_table_new(mongory_internal_pool);
|
118
|
+
if (mongory_matcher_mapping == NULL) {
|
119
|
+
// TODO: Set error state.
|
120
|
+
return;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
/**
|
125
|
+
* @brief Registers a matcher build function with a given name.
|
126
|
+
* The build function is stored in the global matcher mapping table.
|
127
|
+
* @param name The name of the matcher (e.g., "$eq", "$in").
|
128
|
+
* @param build_func A function pointer to the matcher's constructor.
|
129
|
+
*/
|
130
|
+
void mongory_matcher_register(char *name, mongory_matcher_build_func build_func) {
|
131
|
+
// Ensure mapping is initialized.
|
132
|
+
if (mongory_matcher_mapping == NULL) {
|
133
|
+
mongory_matcher_mapping_init();
|
134
|
+
if (mongory_matcher_mapping == NULL) {
|
135
|
+
// TODO: Set error: Cannot register if mapping init failed.
|
136
|
+
return;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
// Wrap the function pointer in a mongory_value to store in the table.
|
141
|
+
mongory_value *value = mongory_value_wrap_ptr(mongory_internal_pool, build_func);
|
142
|
+
if (value == NULL) {
|
143
|
+
// TODO: Set error: Failed to wrap pointer.
|
144
|
+
return;
|
145
|
+
}
|
146
|
+
|
147
|
+
mongory_matcher_mapping->set(mongory_matcher_mapping, name, value);
|
148
|
+
// TODO: Check return value of set?
|
149
|
+
}
|
150
|
+
|
151
|
+
/**
|
152
|
+
* @brief Retrieves a matcher build function by its name from the global
|
153
|
+
* mapping.
|
154
|
+
* @param name The name of the matcher to retrieve.
|
155
|
+
* @return mongory_matcher_build_func A function pointer to the matcher's
|
156
|
+
* constructor, or NULL if not found.
|
157
|
+
*/
|
158
|
+
mongory_matcher_build_func mongory_matcher_build_func_get(char *name) {
|
159
|
+
if (mongory_matcher_mapping == NULL) {
|
160
|
+
return NULL; // Mapping not initialized or init failed.
|
161
|
+
}
|
162
|
+
|
163
|
+
mongory_value *value = mongory_matcher_mapping->get(mongory_matcher_mapping, name);
|
164
|
+
if (value == NULL || value->type != MONGORY_TYPE_POINTER) {
|
165
|
+
return NULL; // Not found or not a pointer type as expected.
|
166
|
+
}
|
167
|
+
|
168
|
+
return (mongory_matcher_build_func)value->data.ptr;
|
169
|
+
}
|
170
|
+
|
171
|
+
/**
|
172
|
+
* @brief Sets the function for deep conversion of external values.
|
173
|
+
* Initializes the value converter if necessary.
|
174
|
+
* @param deep_convert The deep conversion function.
|
175
|
+
*/
|
176
|
+
void mongory_value_converter_deep_convert_set(mongory_deep_convert_func deep_convert) {
|
177
|
+
mongory_internal_value_converter.deep_convert = deep_convert;
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* @brief Sets the function for shallow conversion of external values.
|
182
|
+
* Initializes the value converter if necessary.
|
183
|
+
* @param shallow_convert The shallow conversion function.
|
184
|
+
*/
|
185
|
+
void mongory_value_converter_shallow_convert_set(mongory_shallow_convert_func shallow_convert) {
|
186
|
+
mongory_internal_value_converter.shallow_convert = shallow_convert;
|
187
|
+
}
|
188
|
+
|
189
|
+
/**
|
190
|
+
* @brief Sets the function for recovering external values from mongory_value.
|
191
|
+
* Initializes the value converter if necessary.
|
192
|
+
* @param recover The recovery function.
|
193
|
+
*/
|
194
|
+
void mongory_value_converter_recover_set(mongory_recover_func recover) {
|
195
|
+
mongory_internal_value_converter.recover = recover;
|
196
|
+
}
|
197
|
+
|
198
|
+
|
199
|
+
void mongory_custom_matcher_match_func_set(bool (*match)(void *external_matcher, mongory_value *value)) {
|
200
|
+
mongory_custom_matcher_adapter.match = match;
|
201
|
+
}
|
202
|
+
|
203
|
+
void mongory_custom_matcher_build_func_set(mongory_matcher_custom_context *(*build)(char *key, mongory_value *condition, void *extern_ctx)) {
|
204
|
+
mongory_custom_matcher_adapter.build = build;
|
205
|
+
}
|
206
|
+
|
207
|
+
void mongory_custom_matcher_lookup_func_set(bool (*lookup)(char *key)) {
|
208
|
+
mongory_custom_matcher_adapter.lookup = lookup;
|
209
|
+
}
|
210
|
+
|
211
|
+
void mongory_matcher_trace_result_colorful_set(bool colorful) {
|
212
|
+
mongory_matcher_trace_result_colorful = colorful;
|
213
|
+
}
|
214
|
+
|
215
|
+
/**
|
216
|
+
* @brief Initializes all core Mongory library components.
|
217
|
+
* This includes the internal memory pool, regex adapter, matcher mapping table,
|
218
|
+
* and value converter. It then registers all standard matcher types.
|
219
|
+
* This function MUST be called before using most other library features.
|
220
|
+
*/
|
221
|
+
void mongory_init() {
|
222
|
+
// Initialize all global components. Order can be important if one init
|
223
|
+
// depends on another (e.g., most depend on the pool).
|
224
|
+
mongory_internal_pool_init();
|
225
|
+
mongory_matcher_mapping_init();
|
226
|
+
|
227
|
+
// Register all standard matchers.
|
228
|
+
// Note: These registrations rely on mongory_internal_pool and
|
229
|
+
// mongory_matcher_mapping being successfully initialized.
|
230
|
+
// TODO: Add checks to ensure initializations were successful before
|
231
|
+
// proceeding.
|
232
|
+
mongory_matcher_register("$in", mongory_matcher_in_new);
|
233
|
+
mongory_matcher_register("$nin", mongory_matcher_not_in_new);
|
234
|
+
mongory_matcher_register("$eq", mongory_matcher_equal_new);
|
235
|
+
mongory_matcher_register("$ne", mongory_matcher_not_equal_new);
|
236
|
+
mongory_matcher_register("$gt", mongory_matcher_greater_than_new);
|
237
|
+
mongory_matcher_register("$gte", mongory_matcher_greater_than_or_equal_new);
|
238
|
+
mongory_matcher_register("$lt", mongory_matcher_less_than_new);
|
239
|
+
mongory_matcher_register("$lte", mongory_matcher_less_than_or_equal_new);
|
240
|
+
mongory_matcher_register("$exists", mongory_matcher_exists_new);
|
241
|
+
mongory_matcher_register("$present", mongory_matcher_present_new);
|
242
|
+
mongory_matcher_register("$regex", mongory_matcher_regex_new);
|
243
|
+
mongory_matcher_register("$and", mongory_matcher_and_new);
|
244
|
+
mongory_matcher_register("$or", mongory_matcher_or_new);
|
245
|
+
mongory_matcher_register("$elemMatch", mongory_matcher_elem_match_new);
|
246
|
+
mongory_matcher_register("$every", mongory_matcher_every_new);
|
247
|
+
mongory_matcher_register("$not", mongory_matcher_not_new);
|
248
|
+
mongory_matcher_register("$size", mongory_matcher_size_new);
|
249
|
+
}
|
250
|
+
|
251
|
+
/**
|
252
|
+
* @brief Cleans up all resources allocated by the Mongory library.
|
253
|
+
* This primarily involves freeing the internal memory pool, which should, in
|
254
|
+
* turn, release all memory allocated from it. It also resets global pointers
|
255
|
+
* to NULL. This should be called when the library is no longer needed to
|
256
|
+
* prevent memory leaks.
|
257
|
+
*/
|
258
|
+
void mongory_cleanup() {
|
259
|
+
if (mongory_internal_pool != NULL) {
|
260
|
+
// Freeing the pool should handle all allocations made from it,
|
261
|
+
// including the regex_adapter, matcher_mapping table (and its contents if
|
262
|
+
// they were allocated from this pool), and value_converter.
|
263
|
+
mongory_internal_pool->free(mongory_internal_pool);
|
264
|
+
mongory_internal_pool = NULL;
|
265
|
+
}
|
266
|
+
|
267
|
+
// Set other global pointers to NULL to indicate they are no longer valid.
|
268
|
+
// The memory they pointed to should have been managed by the internal pool.
|
269
|
+
mongory_matcher_mapping = NULL; // The table itself and its nodes.
|
270
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#ifndef MONGORY_FOUNDATIONS_CONFIG_PRIVATE_H
|
2
|
+
#define MONGORY_FOUNDATIONS_CONFIG_PRIVATE_H
|
3
|
+
#include "../matchers/base_matcher.h"
|
4
|
+
#include "mongory-core/foundations/config.h"
|
5
|
+
#include "mongory-core/foundations/table.h"
|
6
|
+
#include "mongory-core/matchers/matcher.h"
|
7
|
+
|
8
|
+
typedef struct mongory_regex_adapter {
|
9
|
+
mongory_regex_func match_func;
|
10
|
+
mongory_regex_stringify_func stringify_func;
|
11
|
+
} mongory_regex_adapter;
|
12
|
+
|
13
|
+
typedef struct mongory_value_converter {
|
14
|
+
mongory_deep_convert_func deep_convert;
|
15
|
+
mongory_shallow_convert_func shallow_convert;
|
16
|
+
mongory_recover_func recover;
|
17
|
+
} mongory_value_converter;
|
18
|
+
|
19
|
+
/**
|
20
|
+
* @brief Function pointer type for matching a value against an external matcher.
|
21
|
+
*
|
22
|
+
* This function is called when a value needs to be matched against an external
|
23
|
+
* matcher.
|
24
|
+
*
|
25
|
+
* @param external_matcher The external reference to the matcher.
|
26
|
+
* @param value The value to match against.
|
27
|
+
* @return bool True if the value matches the matcher, false otherwise.
|
28
|
+
*/
|
29
|
+
typedef struct mongory_matcher_custom_adapter {
|
30
|
+
bool (*match)(void *external_matcher, mongory_value *value); // Match a value against an external matcher.
|
31
|
+
mongory_matcher_custom_context *(*build)(char *key, mongory_value *condition, void *extern_ctx); // Build an external matcher reference.
|
32
|
+
bool (*lookup)(char *key); // Lookup a matcher reference by key.
|
33
|
+
} mongory_matcher_custom_adapter;
|
34
|
+
|
35
|
+
extern mongory_memory_pool *mongory_internal_pool;
|
36
|
+
extern mongory_regex_adapter mongory_internal_regex_adapter;
|
37
|
+
extern mongory_table *mongory_matcher_mapping;
|
38
|
+
extern mongory_value_converter mongory_internal_value_converter;
|
39
|
+
extern mongory_matcher_custom_adapter mongory_custom_matcher_adapter;
|
40
|
+
extern bool mongory_matcher_trace_result_colorful;
|
41
|
+
|
42
|
+
typedef mongory_matcher *(*mongory_matcher_build_func)(mongory_memory_pool *pool,
|
43
|
+
mongory_value *condition,
|
44
|
+
void *extern_ctx); // build function
|
45
|
+
void mongory_matcher_register(char *name, mongory_matcher_build_func build_func);
|
46
|
+
mongory_matcher_build_func mongory_matcher_build_func_get(char *name);
|
47
|
+
|
48
|
+
#endif
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#ifndef MONGORY_ERROR_C
|
2
|
+
#define MONGORY_ERROR_C
|
3
|
+
/**
|
4
|
+
* @file error.c
|
5
|
+
* @brief Implements error type to string conversion for the Mongory library.
|
6
|
+
*/
|
7
|
+
#include <mongory-core/foundations/error.h>
|
8
|
+
|
9
|
+
/**
|
10
|
+
* @brief Converts a mongory_error_type enum to its human-readable string
|
11
|
+
* representation.
|
12
|
+
*
|
13
|
+
* This function uses the MONGORY_ERROR_TYPE_MACRO to generate a switch
|
14
|
+
* statement that maps each error enum to its corresponding string.
|
15
|
+
*
|
16
|
+
* @param type The mongory_error_type enum value to convert.
|
17
|
+
* @return const char* A string literal representing the error type. If the
|
18
|
+
* type is not recognized, it returns "Unknown Error Type".
|
19
|
+
*/
|
20
|
+
const char *mongory_error_type_to_string(enum mongory_error_type type) {
|
21
|
+
switch (type) {
|
22
|
+
// Use the X-Macro to generate case statements for each error type.
|
23
|
+
#define DEFINE_ERROR_STRING(name, num, str) \
|
24
|
+
case name: \
|
25
|
+
return str;
|
26
|
+
MONGORY_ERROR_TYPE_MACRO(DEFINE_ERROR_STRING)
|
27
|
+
#undef DEFINE_ERROR_STRING // Undefine the macro after use.
|
28
|
+
default:
|
29
|
+
// Fallback for any undefined or unknown error types.
|
30
|
+
return "Unknown Error Type";
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
mongory_error MONGORY_ALLOC_ERROR = {
|
35
|
+
.type = MONGORY_ERROR_MEMORY,
|
36
|
+
.message = "Memory Allocation Failed",
|
37
|
+
};
|
38
|
+
#endif
|
@@ -0,0 +1,298 @@
|
|
1
|
+
/**
|
2
|
+
* @file memory_pool.c
|
3
|
+
* @brief Implements a memory pool for the Mongory library.
|
4
|
+
*
|
5
|
+
* This implementation uses a linked list of memory chunks. Allocations are
|
6
|
+
* made from the current chunk. If the current chunk is full, a new, larger
|
7
|
+
* chunk is allocated and added to the list. Freeing the pool deallocates all
|
8
|
+
* chunks. It also supports tracing externally allocated memory.
|
9
|
+
*/
|
10
|
+
#include <mongory-core/foundations/memory_pool.h>
|
11
|
+
#include <stdio.h> // For NULL, though stdlib.h or stddef.h is more common
|
12
|
+
#include <stdlib.h> // For calloc, free, etc.
|
13
|
+
#include <string.h> // For memset
|
14
|
+
|
15
|
+
/**
|
16
|
+
* @def MONGORY_INITIAL_CHUNK_SIZE
|
17
|
+
* @brief The initial size in bytes for the first memory chunk allocated by the
|
18
|
+
* pool.
|
19
|
+
*/
|
20
|
+
#define MONGORY_INITIAL_CHUNK_SIZE 2048
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @def MONGORY_ALIGN8(size)
|
24
|
+
* @brief Macro to align a given size to an 8-byte boundary.
|
25
|
+
* This is useful for ensuring that memory allocations are suitably aligned
|
26
|
+
* for various data types.
|
27
|
+
* @param size The original size.
|
28
|
+
* @return The size aligned up to the nearest multiple of 8.
|
29
|
+
*/
|
30
|
+
#define MONGORY_ALIGN8(size) (((size) + 7) & ~((size_t)7))
|
31
|
+
|
32
|
+
/**
|
33
|
+
* @struct mongory_memory_node
|
34
|
+
* @brief Represents a node in the linked list of memory chunks within the pool.
|
35
|
+
*
|
36
|
+
* Each node holds a pointer to a block of memory (`ptr`), its total `size`,
|
37
|
+
* how much of it is `used`, and a pointer to the `next` node in the list.
|
38
|
+
* This structure is also used by the tracing mechanism to track external
|
39
|
+
* allocations.
|
40
|
+
*/
|
41
|
+
typedef struct mongory_memory_node {
|
42
|
+
void *ptr; /**< Pointer to the allocated memory block. */
|
43
|
+
size_t size; /**< Total size of the memory block. */
|
44
|
+
size_t used; /**< Bytes used in this memory block. */
|
45
|
+
struct mongory_memory_node *next; /**< Pointer to the next memory node. */
|
46
|
+
} mongory_memory_node;
|
47
|
+
|
48
|
+
/**
|
49
|
+
* @struct mongory_memory_pool_ctx
|
50
|
+
* @brief Internal context for a mongory_memory_pool.
|
51
|
+
*
|
52
|
+
* Stores the current `chunk_size` (which doubles on growth), pointers to
|
53
|
+
* the `head` and `current` memory nodes for allocations, and a list (`extra`)
|
54
|
+
* for traced external allocations.
|
55
|
+
*/
|
56
|
+
typedef struct mongory_memory_pool_ctx {
|
57
|
+
size_t chunk_size; /**< Current preferred size for new chunks. */
|
58
|
+
mongory_memory_node *head; /**< Head of the list of memory chunks. */
|
59
|
+
mongory_memory_node *current; /**< Current chunk to allocate from. */
|
60
|
+
mongory_memory_node *extra; /**< Head of list for externally traced memory. */
|
61
|
+
} mongory_memory_pool_ctx;
|
62
|
+
|
63
|
+
/**
|
64
|
+
* @brief Allocates a new memory chunk (node and its associated memory block).
|
65
|
+
*
|
66
|
+
* Uses `calloc` to ensure memory is zero-initialized.
|
67
|
+
*
|
68
|
+
* @param chunk_size The size of the memory block to allocate for this chunk.
|
69
|
+
* @return mongory_memory_node* Pointer to the new memory node, or NULL on
|
70
|
+
* failure.
|
71
|
+
*/
|
72
|
+
static inline mongory_memory_node *mongory_memory_chunk_new(size_t chunk_size) {
|
73
|
+
mongory_memory_node *node = calloc(1, sizeof(mongory_memory_node));
|
74
|
+
if (!node) {
|
75
|
+
return NULL; // Failed to allocate node structure.
|
76
|
+
}
|
77
|
+
|
78
|
+
void *mem = calloc(1, chunk_size);
|
79
|
+
if (!mem) {
|
80
|
+
free(node); // Clean up allocated node structure.
|
81
|
+
return NULL; // Failed to allocate memory block for the chunk.
|
82
|
+
}
|
83
|
+
|
84
|
+
node->ptr = mem;
|
85
|
+
node->size = chunk_size;
|
86
|
+
node->used = 0;
|
87
|
+
node->next = NULL;
|
88
|
+
|
89
|
+
return node;
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* @brief Grows the memory pool by adding a new, larger chunk.
|
94
|
+
*
|
95
|
+
* The new chunk size is at least double the previous chunk size, and large
|
96
|
+
* enough to satisfy `request_size`. The new chunk becomes the `current` chunk.
|
97
|
+
*
|
98
|
+
* @param ctx Pointer to the memory pool's context.
|
99
|
+
* @param request_size The minimum size required from the new chunk for an
|
100
|
+
* upcoming allocation.
|
101
|
+
* @return true if growth was successful, false otherwise.
|
102
|
+
*/
|
103
|
+
static inline bool mongory_memory_pool_grow(mongory_memory_pool_ctx *ctx, size_t request_size) {
|
104
|
+
if (ctx->current->next) {
|
105
|
+
ctx->current = ctx->current->next;
|
106
|
+
return true; // Already have a next chunk.
|
107
|
+
}
|
108
|
+
// Double the chunk size, ensuring it's at least as large as request_size.
|
109
|
+
ctx->chunk_size *= 2;
|
110
|
+
while (request_size > ctx->chunk_size) {
|
111
|
+
ctx->chunk_size *= 2;
|
112
|
+
}
|
113
|
+
|
114
|
+
mongory_memory_node *new_chunk = mongory_memory_chunk_new(ctx->chunk_size);
|
115
|
+
if (!new_chunk) {
|
116
|
+
return false; // Failed to create a new chunk.
|
117
|
+
}
|
118
|
+
|
119
|
+
// Link the new chunk and update the current pointer.
|
120
|
+
ctx->current->next = new_chunk;
|
121
|
+
ctx->current = new_chunk;
|
122
|
+
|
123
|
+
return true;
|
124
|
+
}
|
125
|
+
|
126
|
+
/**
|
127
|
+
* @brief Allocates memory from the pool. Implements `pool->alloc`.
|
128
|
+
*
|
129
|
+
* Allocates `size` bytes (aligned to 8 bytes) from the `current` chunk.
|
130
|
+
* If the current chunk doesn't have enough space, `mongory_memory_pool_grow`
|
131
|
+
* is called to add a new chunk.
|
132
|
+
*
|
133
|
+
* @param pool Pointer to the `mongory_memory_pool`.
|
134
|
+
* @param size The number of bytes to allocate.
|
135
|
+
* @return void* Pointer to the allocated memory, or NULL on failure.
|
136
|
+
*/
|
137
|
+
static inline void *mongory_memory_pool_alloc(mongory_memory_pool *pool, size_t size) {
|
138
|
+
mongory_memory_pool_ctx *pool_ctx = (mongory_memory_pool_ctx *)pool->ctx;
|
139
|
+
size = MONGORY_ALIGN8(size); // Ensure 8-byte alignment.
|
140
|
+
|
141
|
+
size_t balance = pool_ctx->current->size - pool_ctx->current->used;
|
142
|
+
if (size > balance) {
|
143
|
+
// Not enough space in current chunk, try to grow.
|
144
|
+
if (!mongory_memory_pool_grow(pool_ctx, size)) {
|
145
|
+
return NULL; // Growth failed.
|
146
|
+
}
|
147
|
+
// After successful growth, pool_ctx->current points to the new chunk.
|
148
|
+
}
|
149
|
+
|
150
|
+
// Allocate from the current chunk.
|
151
|
+
void *ptr = (char *)pool_ctx->current->ptr + pool_ctx->current->used;
|
152
|
+
pool_ctx->current->used += size;
|
153
|
+
|
154
|
+
return ptr;
|
155
|
+
}
|
156
|
+
|
157
|
+
static inline void mongory_memory_pool_reset(mongory_memory_pool *pool) {
|
158
|
+
mongory_memory_pool_ctx *pool_ctx = (mongory_memory_pool_ctx *)pool->ctx;
|
159
|
+
pool_ctx->current = pool_ctx->head;
|
160
|
+
mongory_memory_node *node = pool_ctx->head;
|
161
|
+
while (node) {
|
162
|
+
node->used = 0;
|
163
|
+
node = node->next;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
/**
|
168
|
+
* @brief Frees a linked list of memory nodes.
|
169
|
+
*
|
170
|
+
* Iterates through the list, freeing the memory block (`node->ptr`) and then
|
171
|
+
* the node structure itself for each node. Memory blocks are zeroed out before
|
172
|
+
* freeing for security/safety.
|
173
|
+
*
|
174
|
+
* @param head Pointer to the head of the memory node list to free.
|
175
|
+
*/
|
176
|
+
static inline void mongory_memory_pool_node_list_free(mongory_memory_node *head) {
|
177
|
+
mongory_memory_node *node = head;
|
178
|
+
while (node) {
|
179
|
+
mongory_memory_node *next = node->next;
|
180
|
+
if (node->ptr) {
|
181
|
+
memset(node->ptr, 0, node->size); // Clear memory for safety.
|
182
|
+
free(node->ptr); // Free the actual memory block.
|
183
|
+
}
|
184
|
+
memset(node, 0,
|
185
|
+
sizeof(mongory_memory_node)); // Clear the node structure itself.
|
186
|
+
free(node); // Free the node structure.
|
187
|
+
node = next;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
/**
|
192
|
+
* @brief Destroys the memory pool and frees all associated memory.
|
193
|
+
* Implements `pool->free`.
|
194
|
+
*
|
195
|
+
* Frees all memory chunks allocated by the pool (`ctx->head` list) and all
|
196
|
+
* externally traced memory blocks (`ctx->extra` list). Then frees the pool
|
197
|
+
* context and the pool structure itself.
|
198
|
+
*
|
199
|
+
* @param pool Pointer to the `mongory_memory_pool` to destroy.
|
200
|
+
*/
|
201
|
+
static inline void mongory_memory_pool_destroy(mongory_memory_pool *pool) {
|
202
|
+
if (!pool)
|
203
|
+
return;
|
204
|
+
mongory_memory_pool_ctx *ctx = (mongory_memory_pool_ctx *)pool->ctx;
|
205
|
+
|
206
|
+
if (ctx) {
|
207
|
+
// Free the main list of memory chunks.
|
208
|
+
mongory_memory_pool_node_list_free(ctx->head);
|
209
|
+
// Free the list of externally traced memory chunks.
|
210
|
+
mongory_memory_pool_node_list_free(ctx->extra);
|
211
|
+
|
212
|
+
memset(ctx, 0,
|
213
|
+
sizeof(mongory_memory_pool_ctx)); // Clear the context structure.
|
214
|
+
free(ctx); // Free the context structure.
|
215
|
+
}
|
216
|
+
|
217
|
+
memset(pool, 0, sizeof(mongory_memory_pool)); // Clear the pool structure.
|
218
|
+
free(pool); // Free the pool structure.
|
219
|
+
}
|
220
|
+
|
221
|
+
/**
|
222
|
+
* @brief Traces an externally allocated piece of memory. Implements
|
223
|
+
* `pool->trace`.
|
224
|
+
*
|
225
|
+
* Creates a new `mongory_memory_node` to track the external memory block and
|
226
|
+
* adds it to the `extra` list in the pool's context. This memory will be
|
227
|
+
* freed when `mongory_memory_pool_destroy` is called if this trace function
|
228
|
+
* is used.
|
229
|
+
*
|
230
|
+
* @param pool Pointer to the `mongory_memory_pool`.
|
231
|
+
* @param ptr Pointer to the externally allocated memory.
|
232
|
+
* @param size Size of the externally allocated memory.
|
233
|
+
*/
|
234
|
+
static inline void mongory_memory_pool_trace(mongory_memory_pool *pool, void *ptr, size_t size) {
|
235
|
+
mongory_memory_pool_ctx *pool_ctx = (mongory_memory_pool_ctx *)pool->ctx;
|
236
|
+
|
237
|
+
// Create a new node to trace this external allocation.
|
238
|
+
mongory_memory_node *extra_alloc_tracer = calloc(1, sizeof(mongory_memory_node));
|
239
|
+
if (!extra_alloc_tracer) {
|
240
|
+
// Allocation of tracer node failed; cannot trace.
|
241
|
+
// This might lead to a leak of 'ptr' if the caller expects the pool to
|
242
|
+
// manage it. Consider error reporting.
|
243
|
+
return;
|
244
|
+
}
|
245
|
+
extra_alloc_tracer->ptr = ptr; // This is the externally allocated memory.
|
246
|
+
extra_alloc_tracer->size = size; // Its size.
|
247
|
+
extra_alloc_tracer->used = size; // Mark as fully "used" in context of tracing.
|
248
|
+
extra_alloc_tracer->next = pool_ctx->extra; // Prepend to extra list.
|
249
|
+
pool_ctx->extra = extra_alloc_tracer;
|
250
|
+
}
|
251
|
+
|
252
|
+
/**
|
253
|
+
* @brief Creates and initializes a new memory pool.
|
254
|
+
*
|
255
|
+
* Allocates the `mongory_memory_pool` structure, its internal
|
256
|
+
* `mongory_memory_pool_ctx`, and the first memory chunk. Sets up the function
|
257
|
+
* pointers for `alloc`, `free`, and `trace`.
|
258
|
+
*
|
259
|
+
* @return mongory_memory_pool* Pointer to the new pool, or NULL on failure.
|
260
|
+
*/
|
261
|
+
mongory_memory_pool *mongory_memory_pool_new() {
|
262
|
+
// Allocate the main pool structure.
|
263
|
+
mongory_memory_pool *pool = calloc(1, sizeof(mongory_memory_pool));
|
264
|
+
if (!pool) {
|
265
|
+
return NULL;
|
266
|
+
}
|
267
|
+
|
268
|
+
// Allocate the internal context for the pool.
|
269
|
+
mongory_memory_pool_ctx *ctx = calloc(1, sizeof(mongory_memory_pool_ctx));
|
270
|
+
if (!ctx) {
|
271
|
+
free(pool); // Clean up partially allocated pool.
|
272
|
+
return NULL;
|
273
|
+
}
|
274
|
+
|
275
|
+
// Allocate the first memory chunk.
|
276
|
+
mongory_memory_node *first_chunk = mongory_memory_chunk_new(MONGORY_INITIAL_CHUNK_SIZE);
|
277
|
+
if (!first_chunk) {
|
278
|
+
free(ctx); // Clean up context.
|
279
|
+
free(pool); // Clean up pool structure.
|
280
|
+
return NULL;
|
281
|
+
}
|
282
|
+
|
283
|
+
// Initialize context fields.
|
284
|
+
ctx->chunk_size = MONGORY_INITIAL_CHUNK_SIZE;
|
285
|
+
ctx->head = first_chunk;
|
286
|
+
ctx->current = first_chunk;
|
287
|
+
ctx->extra = NULL; // No extra traced allocations initially.
|
288
|
+
|
289
|
+
// Initialize pool fields.
|
290
|
+
pool->ctx = ctx;
|
291
|
+
pool->alloc = mongory_memory_pool_alloc;
|
292
|
+
pool->reset = mongory_memory_pool_reset;
|
293
|
+
pool->free = mongory_memory_pool_destroy;
|
294
|
+
pool->trace = mongory_memory_pool_trace;
|
295
|
+
pool->error = NULL; // No error initially.
|
296
|
+
|
297
|
+
return pool;
|
298
|
+
}
|