mongory 0.7.2-x86_64-linux

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.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +88 -0
  4. data/.yardopts +7 -0
  5. data/CHANGELOG.md +364 -0
  6. data/CODE_OF_CONDUCT.md +84 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +488 -0
  9. data/Rakefile +107 -0
  10. data/SUBMODULE_INTEGRATION.md +325 -0
  11. data/docs/advanced_usage.md +40 -0
  12. data/docs/clang_bridge.md +69 -0
  13. data/docs/field_names.md +30 -0
  14. data/docs/migration.md +30 -0
  15. data/docs/performance.md +61 -0
  16. data/examples/README.md +41 -0
  17. data/examples/benchmark-rails.rb +52 -0
  18. data/examples/benchmark.rb +184 -0
  19. data/ext/mongory_ext/extconf.rb +91 -0
  20. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/array.h +122 -0
  21. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/config.h +161 -0
  22. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/error.h +79 -0
  23. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/memory_pool.h +95 -0
  24. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/table.h +127 -0
  25. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/value.h +175 -0
  26. data/ext/mongory_ext/mongory-core/include/mongory-core/matchers/matcher.h +76 -0
  27. data/ext/mongory_ext/mongory-core/include/mongory-core.h +12 -0
  28. data/ext/mongory_ext/mongory-core/src/foundations/array.c +287 -0
  29. data/ext/mongory_ext/mongory-core/src/foundations/array_private.h +19 -0
  30. data/ext/mongory_ext/mongory-core/src/foundations/config.c +270 -0
  31. data/ext/mongory_ext/mongory-core/src/foundations/config_private.h +48 -0
  32. data/ext/mongory_ext/mongory-core/src/foundations/error.c +38 -0
  33. data/ext/mongory_ext/mongory-core/src/foundations/memory_pool.c +298 -0
  34. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.c +65 -0
  35. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.h +49 -0
  36. data/ext/mongory_ext/mongory-core/src/foundations/table.c +498 -0
  37. data/ext/mongory_ext/mongory-core/src/foundations/utils.c +210 -0
  38. data/ext/mongory_ext/mongory-core/src/foundations/utils.h +70 -0
  39. data/ext/mongory_ext/mongory-core/src/foundations/value.c +500 -0
  40. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.c +164 -0
  41. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.h +47 -0
  42. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.c +122 -0
  43. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.h +100 -0
  44. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.c +217 -0
  45. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.h +83 -0
  46. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.c +573 -0
  47. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.h +125 -0
  48. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.c +147 -0
  49. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.h +48 -0
  50. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.c +124 -0
  51. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.h +46 -0
  52. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.c +126 -0
  53. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.h +46 -0
  54. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.c +314 -0
  55. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.h +97 -0
  56. data/ext/mongory_ext/mongory-core/src/matchers/matcher.c +252 -0
  57. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.c +79 -0
  58. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.h +23 -0
  59. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.c +60 -0
  60. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.h +23 -0
  61. data/ext/mongory_ext/mongory_ext.c +683 -0
  62. data/lib/generators/mongory/install/install_generator.rb +42 -0
  63. data/lib/generators/mongory/install/templates/initializer.rb.erb +83 -0
  64. data/lib/generators/mongory/matcher/matcher_generator.rb +56 -0
  65. data/lib/generators/mongory/matcher/templates/matcher.rb.erb +92 -0
  66. data/lib/generators/mongory/matcher/templates/matcher_spec.rb.erb +17 -0
  67. data/lib/mongory/c_query_builder.rb +44 -0
  68. data/lib/mongory/converters/abstract_converter.rb +111 -0
  69. data/lib/mongory/converters/condition_converter.rb +64 -0
  70. data/lib/mongory/converters/converted.rb +81 -0
  71. data/lib/mongory/converters/data_converter.rb +37 -0
  72. data/lib/mongory/converters/key_converter.rb +87 -0
  73. data/lib/mongory/converters/value_converter.rb +52 -0
  74. data/lib/mongory/converters.rb +8 -0
  75. data/lib/mongory/matchers/abstract_matcher.rb +219 -0
  76. data/lib/mongory/matchers/abstract_multi_matcher.rb +124 -0
  77. data/lib/mongory/matchers/and_matcher.rb +72 -0
  78. data/lib/mongory/matchers/array_record_matcher.rb +93 -0
  79. data/lib/mongory/matchers/elem_match_matcher.rb +55 -0
  80. data/lib/mongory/matchers/eq_matcher.rb +46 -0
  81. data/lib/mongory/matchers/every_matcher.rb +56 -0
  82. data/lib/mongory/matchers/exists_matcher.rb +53 -0
  83. data/lib/mongory/matchers/field_matcher.rb +147 -0
  84. data/lib/mongory/matchers/gt_matcher.rb +41 -0
  85. data/lib/mongory/matchers/gte_matcher.rb +41 -0
  86. data/lib/mongory/matchers/hash_condition_matcher.rb +62 -0
  87. data/lib/mongory/matchers/in_matcher.rb +68 -0
  88. data/lib/mongory/matchers/literal_matcher.rb +121 -0
  89. data/lib/mongory/matchers/lt_matcher.rb +41 -0
  90. data/lib/mongory/matchers/lte_matcher.rb +41 -0
  91. data/lib/mongory/matchers/ne_matcher.rb +38 -0
  92. data/lib/mongory/matchers/nin_matcher.rb +68 -0
  93. data/lib/mongory/matchers/not_matcher.rb +40 -0
  94. data/lib/mongory/matchers/or_matcher.rb +68 -0
  95. data/lib/mongory/matchers/present_matcher.rb +55 -0
  96. data/lib/mongory/matchers/regex_matcher.rb +80 -0
  97. data/lib/mongory/matchers/size_matcher.rb +54 -0
  98. data/lib/mongory/matchers.rb +176 -0
  99. data/lib/mongory/mongoid.rb +19 -0
  100. data/lib/mongory/query_builder.rb +257 -0
  101. data/lib/mongory/query_matcher.rb +93 -0
  102. data/lib/mongory/query_operator.rb +28 -0
  103. data/lib/mongory/rails.rb +15 -0
  104. data/lib/mongory/utils/context.rb +48 -0
  105. data/lib/mongory/utils/debugger.rb +125 -0
  106. data/lib/mongory/utils/rails_patch.rb +22 -0
  107. data/lib/mongory/utils/singleton_builder.rb +31 -0
  108. data/lib/mongory/utils.rb +76 -0
  109. data/lib/mongory/version.rb +5 -0
  110. data/lib/mongory.rb +123 -0
  111. data/lib/mongory_ext.so +0 -0
  112. data/mongory.gemspec +50 -0
  113. data/scripts/build_with_core.sh +292 -0
  114. data/sig/mongory.rbs +4 -0
  115. metadata +159 -0
@@ -0,0 +1,70 @@
1
+ #ifndef MONGORY_UTILS_H
2
+ #define MONGORY_UTILS_H
3
+
4
+ #include "mongory-core/foundations/memory_pool.h"
5
+ #include "mongory-core/foundations/value.h"
6
+ #include <stdbool.h>
7
+
8
+ /**
9
+ * @brief Attempts to parse an integer from a string.
10
+ *
11
+ * This utility function is useful for matchers that might operate on array
12
+ * indices or numeric string fields. It checks for valid integer formats and
13
+ * range (`INT_MIN`, `INT_MAX`).
14
+ *
15
+ * @param key The null-terminated string to parse. Must not be NULL or empty.
16
+ * @param out A pointer to an integer where the parsed value will be stored if
17
+ * successful. Must not be NULL.
18
+ * @return bool True if the string was successfully parsed as an integer and is
19
+ * within `int` range, false otherwise. `errno` might be set by `strtol` on
20
+ * failure (e.g. `ERANGE`).
21
+ */
22
+ bool mongory_try_parse_int(const char *key, int *out);
23
+
24
+ /**
25
+ * @brief Copies a string using the Mongory library's memory pool.
26
+ *
27
+ * Allocates memory from the given pool and copies the source string into it.
28
+ * The returned string is null-terminated.
29
+ *
30
+ * @param pool The memory pool to use for allocating the new string.
31
+ * @param str The source string to copy. If NULL, returns NULL.
32
+ * @return char* A pointer to the newly allocated and copied string, or NULL
33
+ * if allocation fails or str is NULL. The caller does not own this memory
34
+ * directly; it will be freed when the pool is freed.
35
+ */
36
+ char *mongory_string_cpy(mongory_memory_pool *pool, char *str);
37
+
38
+ /**
39
+ * @brief Copies a formatted string using the Mongory library's memory pool.
40
+ *
41
+ * Allocates memory from the given pool and copies the formatted string into it.
42
+ * The returned string is null-terminated.
43
+ *
44
+ * @param pool The memory pool to use for allocating the new string.
45
+ * @param format The format string to copy.
46
+ * @param ... The arguments to be formatted into the string.
47
+ * @return char* A pointer to the newly allocated and copied string, or NULL
48
+ * if allocation fails or format is NULL. The caller does not own this memory
49
+ * directly; it will be freed when the pool is freed.
50
+ */
51
+ char *mongory_string_cpyf(mongory_memory_pool *pool, char *format, ...);
52
+
53
+ double mongory_log(double x, double base);
54
+
55
+ bool mongory_validate_ptr(mongory_memory_pool *pool, char *name, void *ptr, char *file, int line);
56
+ #define MONGORY_VALIDATE_PTR(pool, ptr) mongory_validate_ptr(pool, #ptr, ptr, __FILE__, __LINE__)
57
+
58
+ bool mongory_validate_table(mongory_memory_pool *pool, char *name, mongory_value *value, char *file, int line);
59
+ #define MONGORY_VALIDATE_TABLE(pool, value) mongory_validate_table(pool, #value, value, __FILE__, __LINE__)
60
+
61
+ bool mongory_validate_array(mongory_memory_pool *pool, char *name, mongory_value *value, char *file, int line);
62
+ #define MONGORY_VALIDATE_ARRAY(pool, value) mongory_validate_array(pool, #value, value, __FILE__, __LINE__)
63
+
64
+ bool mongory_validate_string(mongory_memory_pool *pool, char *name, mongory_value *value, char *file, int line);
65
+ #define MONGORY_VALIDATE_STRING(pool, value) mongory_validate_string(pool, #value, value, __FILE__, __LINE__)
66
+
67
+ bool mongory_validate_number(mongory_memory_pool *pool, char *name, mongory_value *value, char *file, int line);
68
+ #define MONGORY_VALIDATE_NUMBER(pool, value) mongory_validate_number(pool, #value, value, __FILE__, __LINE__)
69
+
70
+ #endif // MONGORY_UTILS_H
@@ -0,0 +1,500 @@
1
+ /**
2
+ * @file value.c
3
+ * @brief Implements the mongory_value generic value type and its operations.
4
+ *
5
+ * This file provides functions for creating (`wrap`), comparing, and
6
+ * converting `mongory_value` instances to strings. Each data type supported by
7
+ * `mongory_value` has a corresponding comparison function and wrapping
8
+ * function.
9
+ */
10
+ #include "string_buffer.h"
11
+ #include <mongory-core/foundations/array.h>
12
+ #include <mongory-core/foundations/config.h>
13
+ #include "utils.h" // For mongory_string_cpy
14
+ #include <mongory-core/foundations/memory_pool.h>
15
+ #include <mongory-core/foundations/table.h>
16
+ #include <mongory-core/foundations/value.h>
17
+ #include "config_private.h"
18
+ #include <stddef.h> // For NULL
19
+ #include <stdio.h> // For snprintf
20
+ #include <stdlib.h> // For general utilities (not directly used here but common)
21
+ #include <string.h> // For strcmp
22
+
23
+ /**
24
+ * @brief Returns a string literal representing the type of the mongory_value.
25
+ * @param value The mongory_value to inspect.
26
+ * @return A string like "Int", "String", "Array", etc., or "UnknownType".
27
+ */
28
+ char *mongory_type_to_string(mongory_value *value) {
29
+ if (!value)
30
+ return "NullValuePtr"; // Handle null pointer to value itself
31
+ switch (value->type) {
32
+ // Use X-Macro to generate cases for each defined type.
33
+ #define CASE_GEN(name, num, str, field) \
34
+ case name: \
35
+ return str;
36
+ MONGORY_TYPE_MACRO(CASE_GEN)
37
+ #undef CASE_GEN
38
+ default:
39
+ return "UnknownType"; // Fallback for unrecognized types.
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @brief Extracts a void pointer to the raw data within the mongory_value.
45
+ * The caller must know the actual type to cast this pointer correctly.
46
+ * @param value The mongory_value from which to extract data.
47
+ * @return A void pointer to the data, or NULL for unknown types or MONGORY_TYPE_NULL.
48
+ */
49
+ void *mongory_value_extract(mongory_value *value) {
50
+ if (!value)
51
+ return NULL;
52
+ switch (value->type) {
53
+ // Use X-Macro to generate cases. For MONGORY_TYPE_NULL, &value->data.i is
54
+ // returned but is meaningless as data for NULL.
55
+ #define EXTRACT_CASE(name, num, str, field) \
56
+ case name: \
57
+ return (void *)&value->data.field;
58
+ MONGORY_TYPE_MACRO(EXTRACT_CASE)
59
+ #undef EXTRACT_CASE
60
+ default:
61
+ return NULL; // Unknown type.
62
+ }
63
+ }
64
+
65
+ static char *mongory_value_null_to_str(mongory_value *value, mongory_memory_pool *pool);
66
+ static char *mongory_value_bool_to_str(mongory_value *value, mongory_memory_pool *pool);
67
+ static char *mongory_value_int_to_str(mongory_value *value, mongory_memory_pool *pool);
68
+ static char *mongory_value_double_to_str(mongory_value *value, mongory_memory_pool *pool);
69
+ static char *mongory_value_string_to_str(mongory_value *value, mongory_memory_pool *pool);
70
+ static char *mongory_value_array_to_str(mongory_value *value, mongory_memory_pool *pool);
71
+ static char *mongory_value_table_to_str(mongory_value *value, mongory_memory_pool *pool);
72
+ static char *mongory_value_generic_ptr_to_str(mongory_value *value, mongory_memory_pool *pool);
73
+
74
+ /**
75
+ * @brief Internal helper to allocate a new mongory_value structure from a pool.
76
+ * @param pool The memory pool to allocate from.
77
+ * @return A pointer to the new mongory_value, or NULL on allocation failure.
78
+ */
79
+ static inline mongory_value *mongory_value_new(mongory_memory_pool *pool) {
80
+ if (!pool || !pool->alloc)
81
+ return NULL; // Invalid pool.
82
+ mongory_value *value = MG_ALLOC_PTR(pool, mongory_value);
83
+ if (!value) {
84
+ pool->error = &MONGORY_ALLOC_ERROR;
85
+ return NULL;
86
+ }
87
+ value->pool = pool;
88
+ value->origin = NULL; // Default origin to NULL.
89
+ return value;
90
+ }
91
+
92
+ // ============================================================================
93
+ // Comparison Functions
94
+ //
95
+ // Each data type has a corresponding comparison function. These functions
96
+ // are assigned to the `comp` function pointer in the `mongory_value` struct.
97
+ // They typically handle comparisons with other compatible types (e.g., Int
98
+ // can be compared with Double).
99
+ // ============================================================================
100
+
101
+ /** Compares two MONGORY_TYPE_NULL values. Always equal. */
102
+ static inline int mongory_value_null_compare(mongory_value *a, mongory_value *b) {
103
+ (void)a; // 'a' is implicitly MONGORY_TYPE_NULL by context of call.
104
+ if (b->type != MONGORY_TYPE_NULL) {
105
+ return mongory_value_compare_fail; // Cannot compare NULL with non-NULL.
106
+ }
107
+ return 0; // NULL is equal to NULL.
108
+ }
109
+
110
+ /** Wraps a NULL value. The `n` parameter is ignored. */
111
+ mongory_value *mongory_value_wrap_n(mongory_memory_pool *pool, void *n) {
112
+ (void)n; // Parameter 'n' is unused for wrapping NULL.
113
+ mongory_value *value = mongory_value_new(pool);
114
+ if (!value)
115
+ return NULL;
116
+ value->type = MONGORY_TYPE_NULL;
117
+ value->comp = mongory_value_null_compare;
118
+ value->to_str = mongory_value_null_to_str;
119
+ // data union is not explicitly set for NULL.
120
+ return value;
121
+ }
122
+
123
+ // ============================================================================
124
+ // Wrapper Functions
125
+ //
126
+ // These functions create a `mongory_value` and wrap a native C type
127
+ // (like bool, int, char*). They allocate the value from the memory pool and
128
+ // set its type, data, and function pointers (`comp`, `to_str`).
129
+ // ============================================================================
130
+
131
+ /** Compares two MONGORY_TYPE_BOOL values. */
132
+ static inline int mongory_value_bool_compare(mongory_value *a, mongory_value *b) {
133
+ if (b->type != MONGORY_TYPE_BOOL)
134
+ return mongory_value_compare_fail;
135
+ // Standard comparison: (true > false)
136
+ return (a->data.b > b->data.b) - (a->data.b < b->data.b);
137
+ }
138
+
139
+ /** Wraps a boolean value. */
140
+ mongory_value *mongory_value_wrap_b(mongory_memory_pool *pool, bool b_val) {
141
+ mongory_value *value = mongory_value_new(pool);
142
+ if (!value)
143
+ return NULL;
144
+ value->type = MONGORY_TYPE_BOOL;
145
+ value->data.b = b_val;
146
+ value->comp = mongory_value_bool_compare;
147
+ value->to_str = mongory_value_bool_to_str;
148
+ return value;
149
+ }
150
+
151
+ /** Compares MONGORY_TYPE_INT with INT or DOUBLE. */
152
+ static inline int mongory_value_int_compare(mongory_value *a, mongory_value *b) {
153
+ if (b->type == MONGORY_TYPE_DOUBLE) {
154
+ // Compare int (a) as double with double (b).
155
+ double a_as_double = (double)a->data.i;
156
+ double b_value = b->data.d;
157
+ return (a_as_double > b_value) - (a_as_double < b_value);
158
+ }
159
+ if (b->type == MONGORY_TYPE_INT) {
160
+ // Compare int (a) with int (b).
161
+ int64_t a_value = a->data.i;
162
+ int64_t b_value = b->data.i;
163
+ return (a_value > b_value) - (a_value < b_value);
164
+ }
165
+ return mongory_value_compare_fail; // Incompatible type for comparison.
166
+ }
167
+
168
+ /** Wraps an integer (promoted to int64_t). */
169
+ mongory_value *mongory_value_wrap_i(mongory_memory_pool *pool, int64_t i_val) {
170
+ mongory_value *value = mongory_value_new(pool);
171
+ if (!value)
172
+ return NULL;
173
+ value->type = MONGORY_TYPE_INT;
174
+ value->data.i = i_val;
175
+ value->comp = mongory_value_int_compare;
176
+ value->to_str = mongory_value_int_to_str;
177
+ return value;
178
+ }
179
+
180
+ /** Compares MONGORY_TYPE_DOUBLE with DOUBLE or INT. */
181
+ static inline int mongory_value_double_compare(mongory_value *a, mongory_value *b) {
182
+ if (b->type == MONGORY_TYPE_DOUBLE) {
183
+ double a_value = a->data.d;
184
+ double b_value = b->data.d;
185
+ return (a_value > b_value) - (a_value < b_value);
186
+ }
187
+ if (b->type == MONGORY_TYPE_INT) {
188
+ // Compare double (a) with int (b) as double.
189
+ double a_value = a->data.d;
190
+ double b_as_double = (double)b->data.i;
191
+ return (a_value > b_as_double) - (a_value < b_as_double);
192
+ }
193
+ return mongory_value_compare_fail; // Incompatible type.
194
+ }
195
+
196
+ /** Wraps a double value. */
197
+ mongory_value *mongory_value_wrap_d(mongory_memory_pool *pool, double d_val) {
198
+ mongory_value *value = mongory_value_new(pool);
199
+ if (!value)
200
+ return NULL;
201
+ value->type = MONGORY_TYPE_DOUBLE;
202
+ value->data.d = d_val;
203
+ value->comp = mongory_value_double_compare;
204
+ value->to_str = mongory_value_double_to_str;
205
+ return value;
206
+ }
207
+
208
+ /** Compares two MONGORY_TYPE_STRING values lexicographically. */
209
+ static inline int mongory_value_string_compare(mongory_value *a, mongory_value *b) {
210
+ // Ensure both are strings and not NULL pointers.
211
+ if (b->type != MONGORY_TYPE_STRING || a->data.s == NULL || b->data.s == NULL) {
212
+ // If one is NULL string and other is not, how to compare?
213
+ // For now, strict: both must be valid strings.
214
+ // Or, define NULL string < non-NULL string.
215
+ // Current: fail if not string or if actual char* is NULL.
216
+ return mongory_value_compare_fail;
217
+ }
218
+ int cmp_result = strcmp(a->data.s, b->data.s);
219
+ return (cmp_result > 0) - (cmp_result < 0); // Normalize to -1, 0, 1
220
+ }
221
+
222
+ /** Wraps a string. Makes a copy of the string using the pool. */
223
+ mongory_value *mongory_value_wrap_s(mongory_memory_pool *pool, char *s_val) {
224
+ mongory_value *value = mongory_value_new(pool);
225
+ if (!value)
226
+ return NULL;
227
+ value->type = MONGORY_TYPE_STRING;
228
+ // `mongory_string_cpy` handles the allocation and copying of the string.
229
+ // If `s_val` is NULL, `value->data.s` will be NULL.
230
+ // If allocation fails, `value->data.s` will also be NULL.
231
+ // TODO: This function should ideally return NULL if the string copy fails
232
+ // when `s_val` was not NULL, but this requires a more complex error handling
233
+ // strategy with the memory pool.
234
+ value->data.s = mongory_string_cpy(pool, s_val);
235
+ value->comp = mongory_value_string_compare;
236
+ value->to_str = mongory_value_string_to_str;
237
+ return value;
238
+ }
239
+
240
+ /** Compares two MONGORY_TYPE_ARRAY values element by element. */
241
+ static inline int mongory_value_array_compare(mongory_value *a, mongory_value *b) {
242
+ if (b->type != MONGORY_TYPE_ARRAY || a->data.a == NULL || b->data.a == NULL) {
243
+ return mongory_value_compare_fail; // Must be two valid arrays.
244
+ }
245
+ struct mongory_array *array_a = a->data.a;
246
+ struct mongory_array *array_b = b->data.a;
247
+
248
+ // First, compare by array size. An array with fewer elements is "less than"
249
+ // an array with more elements.
250
+ if (array_a->count != array_b->count) {
251
+ return (array_a->count > array_b->count) - (array_a->count < array_b->count);
252
+ }
253
+
254
+ // Same count, compare element by element.
255
+ for (size_t i = 0; i < array_a->count; i++) {
256
+ mongory_value *item_a = array_a->get(array_a, i);
257
+ mongory_value *item_b = array_b->get(array_b, i);
258
+
259
+ // Handle NULL elements within arrays carefully.
260
+ bool a_item_is_null = (item_a == NULL || item_a->type == MONGORY_TYPE_NULL);
261
+ bool b_item_is_null = (item_b == NULL || item_b->type == MONGORY_TYPE_NULL);
262
+
263
+ if (a_item_is_null && b_item_is_null)
264
+ continue; // Both null, considered equal here.
265
+ if (a_item_is_null)
266
+ return -1; // Null is less than non-null.
267
+ if (b_item_is_null)
268
+ return 1; // Non-null is greater than null.
269
+
270
+ // Both items are non-null, compare them using their own comp functions.
271
+ if (!item_a->comp || !item_b->comp)
272
+ return mongory_value_compare_fail; // Should not happen for valid values
273
+
274
+ int cmp_result = item_a->comp(item_a, item_b);
275
+ if (cmp_result == mongory_value_compare_fail)
276
+ return mongory_value_compare_fail; // Incomparable elements
277
+ if (cmp_result != 0) {
278
+ return cmp_result; // First differing element determines array order.
279
+ }
280
+ }
281
+ return 0; // All elements are equal.
282
+ }
283
+
284
+ /** Wraps a mongory_array. */
285
+ mongory_value *mongory_value_wrap_a(mongory_memory_pool *pool, struct mongory_array *a_val) {
286
+ mongory_value *value = mongory_value_new(pool);
287
+ if (!value)
288
+ return NULL;
289
+ value->type = MONGORY_TYPE_ARRAY;
290
+ value->data.a = a_val;
291
+ value->comp = mongory_value_array_compare;
292
+ value->to_str = mongory_value_array_to_str;
293
+ return value;
294
+ }
295
+
296
+ /** Compares two MONGORY_TYPE_TABLE values. Currently unsupported (always fails). */
297
+ static inline int mongory_value_table_compare(mongory_value *a, mongory_value *b) {
298
+ (void)a; // Unused.
299
+ (void)b; // Unused.
300
+ // True table comparison is complex because the order of keys is not
301
+ // significant. It would require iterating over keys of one table and checking
302
+ // for their presence and value equality in the other. This is not
303
+ // implemented. Therefore, tables are considered incomparable.
304
+ return mongory_value_compare_fail;
305
+ }
306
+
307
+ /** Wraps a mongory_table. */
308
+ mongory_value *mongory_value_wrap_t(mongory_memory_pool *pool, struct mongory_table *t_val) {
309
+ mongory_value *value = mongory_value_new(pool);
310
+ if (!value)
311
+ return NULL;
312
+ value->type = MONGORY_TYPE_TABLE;
313
+ value->data.t = t_val;
314
+ value->comp = mongory_value_table_compare;
315
+ value->to_str = mongory_value_table_to_str;
316
+ return value;
317
+ }
318
+
319
+ /** Generic comparison for types not otherwise handled (e.g. POINTER, REGEX, UNSUPPORTED). Always fails. */
320
+ static inline int mongory_value_generic_ptr_compare(mongory_value *a, mongory_value *b) {
321
+ (void)a; // Unused.
322
+ (void)b; // Unused.
323
+ // Generic pointers are generally not comparable in a meaningful way beyond identity.
324
+ return mongory_value_compare_fail;
325
+ }
326
+
327
+ /** Wraps an unsupported/unknown type pointer. */
328
+ mongory_value *mongory_value_wrap_u(mongory_memory_pool *pool, void *u_val) {
329
+ mongory_value *value = mongory_value_new(pool);
330
+ if (!value)
331
+ return NULL;
332
+ value->type = MONGORY_TYPE_UNSUPPORTED;
333
+ value->data.u = u_val;
334
+ value->comp = mongory_value_generic_ptr_compare; // Unsupported types are not comparable.
335
+ value->to_str = mongory_value_generic_ptr_to_str;
336
+ return value;
337
+ }
338
+
339
+ static char *mongory_value_regex_to_str(mongory_value *value, mongory_memory_pool *pool) {
340
+ char *str = mongory_internal_regex_adapter.stringify_func(pool, value);
341
+ if (!str)
342
+ return NULL;
343
+ return str;
344
+ }
345
+
346
+ /** Wraps a regex type pointer. */
347
+ mongory_value *mongory_value_wrap_regex(mongory_memory_pool *pool, void *regex_val) {
348
+ mongory_value *value = mongory_value_new(pool);
349
+ if (!value)
350
+ return NULL;
351
+ value->type = MONGORY_TYPE_REGEX;
352
+ value->data.regex = regex_val;
353
+ value->comp = mongory_value_generic_ptr_compare; // Regex values are not directly comparable.
354
+ value->to_str = mongory_value_regex_to_str;
355
+ return value;
356
+ }
357
+
358
+ /** Wraps a generic void pointer. */
359
+ mongory_value *mongory_value_wrap_ptr(mongory_memory_pool *pool, void *ptr_val) {
360
+ mongory_value *value = mongory_value_new(pool);
361
+ if (!value)
362
+ return NULL;
363
+ value->type = MONGORY_TYPE_POINTER;
364
+ value->data.ptr = ptr_val;
365
+ value->comp = mongory_value_generic_ptr_compare; // Generic pointers are not comparable.
366
+ value->to_str = mongory_value_generic_ptr_to_str;
367
+ return value;
368
+ }
369
+
370
+ // ============================================================================
371
+ // Stringify Functions
372
+ //
373
+ // Each data type has a corresponding `to_str` function that converts the
374
+ // value to a string representation (often JSON-like) and appends it to a
375
+ // `mongory_string_buffer`.
376
+ // ============================================================================
377
+
378
+ static char *mongory_value_null_to_str(mongory_value *value, mongory_memory_pool *pool) {
379
+ (void)value;
380
+ return mongory_string_cpy(pool, "null");
381
+ }
382
+
383
+ static char *mongory_value_bool_to_str(mongory_value *value, mongory_memory_pool *pool) {
384
+ if (!MONGORY_VALIDATE_PTR(pool, &value->data.b)) {
385
+ return NULL;
386
+ }
387
+ return mongory_string_cpy(pool, value->data.b ? "true" : "false");
388
+ }
389
+
390
+ static char *mongory_value_int_to_str(mongory_value *value, mongory_memory_pool *pool) {
391
+ if (!MONGORY_VALIDATE_PTR(pool, &value->data.i)) {
392
+ return NULL;
393
+ }
394
+ return mongory_string_cpyf(pool, "%lld", (long long)value->data.i);
395
+ }
396
+
397
+ static char *mongory_value_double_to_str(mongory_value *value, mongory_memory_pool *pool) {
398
+ if (!MONGORY_VALIDATE_PTR(pool, &value->data.d)) {
399
+ return NULL;
400
+ }
401
+ return mongory_string_cpyf(pool, "%f", value->data.d);
402
+ }
403
+
404
+ static char *mongory_value_string_to_str(mongory_value *value, mongory_memory_pool *pool) {
405
+ if (!MONGORY_VALIDATE_PTR(pool, value->data.s)) {
406
+ return NULL;
407
+ }
408
+ return mongory_string_cpyf(pool, "\"%s\"", value->data.s);
409
+ }
410
+
411
+ typedef struct mongory_value_container_to_str_ctx {
412
+ size_t count;
413
+ size_t total;
414
+ mongory_string_buffer *buffer; /**< The string buffer to append to. */
415
+ } mongory_value_container_to_str_ctx;
416
+
417
+ static bool mongory_value_array_to_str_each(mongory_value *value, void *ctx) {
418
+ mongory_value_container_to_str_ctx *context = (mongory_value_container_to_str_ctx *)ctx;
419
+ mongory_string_buffer *buffer = context->buffer;
420
+ mongory_memory_pool *pool = buffer->pool;
421
+ MONGORY_VALIDATE_PTR(pool, value);
422
+ MONGORY_VALIDATE_PTR(pool, value->to_str);
423
+ if (pool->error != NULL) {
424
+ return false;
425
+ }
426
+ char *str = value->to_str(value, pool);
427
+ if (!str)
428
+ return false;
429
+ mongory_string_buffer_append(buffer, str);
430
+ context->count++;
431
+ if (context->count < context->total) {
432
+ mongory_string_buffer_append(buffer, ",");
433
+ }
434
+ return true;
435
+ }
436
+
437
+ static char *mongory_value_array_to_str(mongory_value *value, mongory_memory_pool *pool) {
438
+ mongory_string_buffer *buffer = mongory_string_buffer_new(pool);
439
+ if (!buffer)
440
+ return NULL;
441
+ mongory_array *array = value->data.a;
442
+ MONGORY_VALIDATE_PTR(pool, array);
443
+ MONGORY_VALIDATE_PTR(pool, array->each);
444
+ MONGORY_VALIDATE_PTR(pool, &array->count);
445
+ if (pool->error != NULL) {
446
+ return NULL;
447
+ }
448
+ mongory_string_buffer_append(buffer, "[");
449
+ mongory_value_container_to_str_ctx ctx = {.count = 0, .total = array->count, .buffer = buffer};
450
+ array->each(array, &ctx, mongory_value_array_to_str_each);
451
+ mongory_string_buffer_append(buffer, "]");
452
+ return mongory_string_buffer_cstr(buffer);
453
+ }
454
+
455
+ static bool mongory_value_table_to_str_each(char *key, mongory_value *value, void *ctx) {
456
+ mongory_value_container_to_str_ctx *context = (mongory_value_container_to_str_ctx *)ctx;
457
+ mongory_string_buffer *buffer = context->buffer;
458
+ mongory_memory_pool *pool = buffer->pool;
459
+ MONGORY_VALIDATE_PTR(pool, key);
460
+ MONGORY_VALIDATE_PTR(pool, value);
461
+ MONGORY_VALIDATE_PTR(pool, value->to_str);
462
+ if (pool->error != NULL) {
463
+ return false;
464
+ }
465
+ mongory_string_buffer_appendf(buffer, "\"%s\":", key);
466
+ char *str = value->to_str(value, buffer->pool);
467
+ if (!str)
468
+ return false;
469
+ mongory_string_buffer_append(buffer, str);
470
+ context->count++;
471
+ if (context->count < context->total) {
472
+ mongory_string_buffer_append(buffer, ",");
473
+ }
474
+ return true;
475
+ }
476
+
477
+ static char *mongory_value_table_to_str(mongory_value *value, mongory_memory_pool *pool) {
478
+ mongory_string_buffer *buffer = mongory_string_buffer_new(pool);
479
+ if (!buffer)
480
+ return NULL;
481
+ mongory_table *table = value->data.t;
482
+ if (!MONGORY_VALIDATE_PTR(pool, table) || !MONGORY_VALIDATE_PTR(pool, table->each) ||
483
+ !MONGORY_VALIDATE_PTR(pool, &table->count)) {
484
+ return NULL;
485
+ }
486
+ if (pool->error != NULL) {
487
+ return NULL;
488
+ }
489
+ mongory_string_buffer_append(buffer, "{");
490
+ mongory_value_container_to_str_ctx ctx = {.count = 0, .total = table->count, .buffer = buffer};
491
+ table->each(table, &ctx, mongory_value_table_to_str_each);
492
+ mongory_string_buffer_append(buffer, "}");
493
+ return mongory_string_buffer_cstr(buffer);
494
+ }
495
+
496
+ static char *mongory_value_generic_ptr_to_str(mongory_value *value, mongory_memory_pool *pool) {
497
+ if (!MONGORY_VALIDATE_PTR(pool, value->data.ptr))
498
+ return NULL;
499
+ return mongory_string_cpyf(pool, "%p", value->data.ptr);
500
+ }