mongory 0.7.3-aarch64-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.

Potentially problematic release.


This version of mongory might be problematic. Click here for more details.

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 +62 -0
  113. data/scripts/build_with_core.sh +292 -0
  114. data/sig/mongory.rbs +4 -0
  115. metadata +159 -0
@@ -0,0 +1,683 @@
1
+ /**
2
+ * @file mongory_ext.c
3
+ * @brief Ruby C extension wrapper for mongory-core
4
+ *
5
+ * This file provides Ruby bindings for the mongory-core C library,
6
+ * allowing Ruby applications to use high-performance C-based matching
7
+ * operations while maintaining the elegant Ruby DSL.
8
+ */
9
+ #include "mongory-core.h"
10
+ #include <ruby.h>
11
+ #include <ruby/encoding.h>
12
+ #include <string.h>
13
+
14
+ // Ruby module and class definitions
15
+ static VALUE mMongory;
16
+ static VALUE cMongoryMatcher;
17
+ static VALUE cMongoryMatcherContext;
18
+ static VALUE mMongoryMatchers;
19
+
20
+ // Error classes
21
+ static VALUE eMongoryError;
22
+ static VALUE eMongoryTypeError;
23
+
24
+ // Converter instance
25
+ static VALUE inMongoryDataConverter;
26
+ static VALUE inMongoryConditionConverter;
27
+
28
+ // Matcher wrapper structure
29
+ typedef struct ruby_mongory_matcher_t {
30
+ mongory_matcher *matcher;
31
+ mongory_value *condition;
32
+ mongory_memory_pool *pool;
33
+ mongory_memory_pool *scratch_pool;
34
+ mongory_table *string_map;
35
+ mongory_table *symbol_map;
36
+ mongory_array *mark_list;
37
+ bool trace_enabled;
38
+ VALUE ctx;
39
+ } ruby_mongory_matcher_t;
40
+
41
+ typedef struct ruby_mongory_memory_pool_t {
42
+ mongory_memory_pool base;
43
+ ruby_mongory_matcher_t *owner;
44
+ } ruby_mongory_memory_pool_t;
45
+
46
+ typedef struct ruby_mongory_table_t {
47
+ mongory_table base;
48
+ VALUE rb_hash;
49
+ ruby_mongory_matcher_t *owner;
50
+ } ruby_mongory_table_t;
51
+
52
+ typedef struct ruby_mongory_array_t {
53
+ mongory_array base;
54
+ VALUE rb_array;
55
+ ruby_mongory_matcher_t *owner;
56
+ } ruby_mongory_array_t;
57
+
58
+ typedef struct {
59
+ mongory_table *table;
60
+ mongory_memory_pool *pool;
61
+ } hash_conv_ctx;
62
+
63
+ // Forward declarations
64
+ static void ruby_mongory_matcher_mark(void *ptr);
65
+ static void ruby_mongory_matcher_free(void *ptr);
66
+ mongory_value *ruby_to_mongory_value_deep(mongory_memory_pool *pool, VALUE rb_value);
67
+ mongory_value *ruby_to_mongory_value_shallow(mongory_memory_pool *pool, VALUE rb_value);
68
+ mongory_value *ruby_mongory_table_wrap(mongory_memory_pool *pool, VALUE rb_hash);
69
+ mongory_value *ruby_mongory_array_wrap(mongory_memory_pool *pool, VALUE rb_array);
70
+ static VALUE cache_fetch_string(ruby_mongory_matcher_t *owner, const char *key);
71
+ static VALUE cache_fetch_symbol(ruby_mongory_matcher_t *owner, const char *key);
72
+ static ruby_mongory_memory_pool_t *ruby_mongory_memory_pool_new();
73
+ static void rb_mongory_matcher_parse_argv(ruby_mongory_matcher_t *wrapper, int argc, VALUE *argv);
74
+ static bool mongory_error_handling(mongory_memory_pool *pool, char *error_message);
75
+
76
+ static const rb_data_type_t ruby_mongory_matcher_type = {
77
+ .wrap_struct_name = "mongory_matcher",
78
+ .function = {
79
+ .dmark = ruby_mongory_matcher_mark,
80
+ .dfree = ruby_mongory_matcher_free,
81
+ .dsize = NULL,
82
+ },
83
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
84
+ };
85
+
86
+ /**
87
+ * Ruby method implementations
88
+ */
89
+
90
+ // Mongory::CMatcher.new(condition)
91
+ static VALUE ruby_mongory_matcher_new(int argc, VALUE *argv, VALUE class) {
92
+ ruby_mongory_memory_pool_t *matcher_pool = ruby_mongory_memory_pool_new();
93
+ ruby_mongory_memory_pool_t *scratch_pool = ruby_mongory_memory_pool_new();
94
+ ruby_mongory_matcher_t *wrapper = ALLOC(ruby_mongory_matcher_t);
95
+ wrapper->pool = &matcher_pool->base;
96
+ wrapper->scratch_pool = &scratch_pool->base;
97
+ wrapper->string_map = mongory_table_new(wrapper->pool);
98
+ wrapper->symbol_map = mongory_table_new(wrapper->pool);
99
+ wrapper->mark_list = mongory_array_new(wrapper->pool);
100
+ wrapper->trace_enabled = false;
101
+ matcher_pool->owner = wrapper;
102
+ scratch_pool->owner = wrapper;
103
+ rb_mongory_matcher_parse_argv(wrapper, argc, argv);
104
+
105
+ mongory_matcher *matcher = mongory_matcher_new(wrapper->pool, wrapper->condition, wrapper->ctx);
106
+ if (mongory_error_handling(wrapper->pool, "Failed to create matcher")) {
107
+ return Qnil;
108
+ }
109
+
110
+ wrapper->matcher = matcher;
111
+ return TypedData_Wrap_Struct(class, &ruby_mongory_matcher_type, wrapper);
112
+ }
113
+
114
+ static void rb_mongory_matcher_parse_argv(ruby_mongory_matcher_t *wrapper, int argc, VALUE *argv) {
115
+ VALUE condition, kw_hash;
116
+ rb_scan_args(argc, argv, "1:", &condition, &kw_hash);
117
+ const ID ctx_id[1] = { rb_intern("context") };
118
+ VALUE kw_vals[1] = { Qundef };
119
+ if (kw_hash != Qnil) {
120
+ rb_get_kwargs(kw_hash, ctx_id, 1, 0, kw_vals);
121
+ }
122
+ if (kw_vals[0] != Qundef) {
123
+ wrapper->ctx = kw_vals[0];
124
+ } else {
125
+ wrapper->ctx = rb_funcall(cMongoryMatcherContext, rb_intern("new"), 0);
126
+ }
127
+ VALUE converted_condition = rb_funcall(inMongoryConditionConverter, rb_intern("convert"), 1, condition);
128
+ wrapper->condition = ruby_to_mongory_value_deep(wrapper->pool, converted_condition);
129
+ wrapper->mark_list->push(wrapper->mark_list, wrapper->condition);
130
+ mongory_value *store_ctx = mongory_value_wrap_u(wrapper->pool, (void *)wrapper->ctx);
131
+ store_ctx->origin = (void *)wrapper->ctx;
132
+ wrapper->mark_list->push(wrapper->mark_list, store_ctx);
133
+ }
134
+
135
+ // Mongory::CMatcher#match(data)
136
+ static VALUE ruby_mongory_matcher_match(VALUE self, VALUE data) {
137
+ ruby_mongory_matcher_t *wrapper;
138
+ TypedData_Get_Struct(self, ruby_mongory_matcher_t, &ruby_mongory_matcher_type, wrapper);
139
+ mongory_value *data_value;
140
+ data_value = ruby_to_mongory_value_shallow(wrapper->scratch_pool, data);
141
+ if (mongory_error_handling(wrapper->scratch_pool, "Match failed")) {
142
+ return Qnil;
143
+ }
144
+ bool result = mongory_matcher_match(wrapper->matcher, data_value);
145
+ if (!wrapper->trace_enabled) {
146
+ wrapper->scratch_pool->reset(wrapper->scratch_pool);
147
+ }
148
+ return result ? Qtrue : Qfalse;
149
+ }
150
+
151
+ // Mongory::CMatcher#explain
152
+ static VALUE ruby_mongory_matcher_explain(VALUE self) {
153
+ ruby_mongory_matcher_t *wrapper;
154
+ TypedData_Get_Struct(self, ruby_mongory_matcher_t, &ruby_mongory_matcher_type, wrapper);
155
+ mongory_matcher_explain(wrapper->matcher, wrapper->scratch_pool);
156
+ if (mongory_error_handling(wrapper->scratch_pool, "Explain failed")) {
157
+ return Qnil;
158
+ }
159
+ wrapper->scratch_pool->reset(wrapper->scratch_pool);
160
+ return Qnil;
161
+ }
162
+
163
+ // Mongory::CMatcher#trace(data)
164
+ static VALUE ruby_mongory_matcher_trace(VALUE self, VALUE data) {
165
+ ruby_mongory_matcher_t *wrapper;
166
+ TypedData_Get_Struct(self, ruby_mongory_matcher_t, &ruby_mongory_matcher_type, wrapper);
167
+ mongory_value *data_value = ruby_to_mongory_value_shallow(wrapper->scratch_pool, data);
168
+ if (mongory_error_handling(wrapper->scratch_pool, "Trace failed")) {
169
+ return Qnil;
170
+ }
171
+ bool matched = mongory_matcher_trace(wrapper->matcher, data_value);
172
+ wrapper->scratch_pool->reset(wrapper->scratch_pool);
173
+ return matched ? Qtrue : Qfalse;
174
+ }
175
+
176
+ // Mongory::CMatcher#enable_trace
177
+ static VALUE ruby_mongory_matcher_enable_trace(VALUE self) {
178
+ ruby_mongory_matcher_t *wrapper;
179
+ TypedData_Get_Struct(self, ruby_mongory_matcher_t, &ruby_mongory_matcher_type, wrapper);
180
+ mongory_matcher_enable_trace(wrapper->matcher, wrapper->scratch_pool);
181
+ if (mongory_error_handling(wrapper->scratch_pool, "Enable trace failed")) {
182
+ return Qnil;
183
+ }
184
+ wrapper->trace_enabled = true;
185
+ return Qnil;
186
+ }
187
+
188
+ // Mongory::CMatcher#disable_trace
189
+ static VALUE ruby_mongory_matcher_disable_trace(VALUE self) {
190
+ ruby_mongory_matcher_t *wrapper;
191
+ TypedData_Get_Struct(self, ruby_mongory_matcher_t, &ruby_mongory_matcher_type, wrapper);
192
+ mongory_matcher_disable_trace(wrapper->matcher);
193
+ if (mongory_error_handling(wrapper->scratch_pool, "Disable trace failed")) {
194
+ return Qnil;
195
+ }
196
+ wrapper->scratch_pool->reset(wrapper->scratch_pool);
197
+ wrapper->trace_enabled = false;
198
+ return Qnil;
199
+ }
200
+
201
+ // Mongory::CMatcher#print_trace
202
+ static VALUE ruby_mongory_matcher_print_trace(VALUE self) {
203
+ ruby_mongory_matcher_t *wrapper;
204
+ TypedData_Get_Struct(self, ruby_mongory_matcher_t, &ruby_mongory_matcher_type, wrapper);
205
+ mongory_matcher_print_trace(wrapper->matcher);
206
+ mongory_error_handling(wrapper->scratch_pool, "Print trace failed");
207
+ return Qnil;
208
+ }
209
+
210
+ // Mongory::CMatcher#condition
211
+ static VALUE ruby_mongory_matcher_condition(VALUE self) {
212
+ ruby_mongory_matcher_t *wrapper;
213
+ TypedData_Get_Struct(self, ruby_mongory_matcher_t, &ruby_mongory_matcher_type, wrapper);
214
+ return (VALUE)wrapper->condition->origin;
215
+ }
216
+
217
+ // Mongory::CMatcher#context
218
+ static VALUE ruby_mongory_matcher_context(VALUE self) {
219
+ ruby_mongory_matcher_t *wrapper;
220
+ TypedData_Get_Struct(self, ruby_mongory_matcher_t, &ruby_mongory_matcher_type, wrapper);
221
+ return wrapper->ctx ? wrapper->ctx : Qnil;
222
+ }
223
+
224
+ // Mongory::CMatcher.trace_result_colorful=(colorful)
225
+ static VALUE ruby_mongory_matcher_trace_result_colorful(VALUE self, VALUE colorful) {
226
+ (void)self;
227
+ mongory_matcher_trace_result_colorful_set(RTEST(colorful));
228
+ return Qnil;
229
+ }
230
+
231
+ /**
232
+ * Create a new memory pool
233
+ */
234
+ static ruby_mongory_memory_pool_t *ruby_mongory_memory_pool_new() {
235
+ ruby_mongory_memory_pool_t *pool = malloc(sizeof(ruby_mongory_memory_pool_t));
236
+ mongory_memory_pool *base = mongory_memory_pool_new();
237
+ memcpy(&pool->base, base, sizeof(mongory_memory_pool));
238
+ free(base);
239
+
240
+ return pool;
241
+ }
242
+
243
+ /**
244
+ * Ruby GC management functions
245
+ */
246
+ static void ruby_mongory_matcher_free(void *ptr) {
247
+ ruby_mongory_matcher_t *wrapper = (ruby_mongory_matcher_t *)ptr;
248
+ mongory_memory_pool *pool = wrapper->pool;
249
+ mongory_memory_pool *scratch_pool = wrapper->scratch_pool;
250
+ pool->free(pool);
251
+ scratch_pool->free(scratch_pool);
252
+ xfree(wrapper);
253
+ }
254
+
255
+ /**
256
+ * GC marking callback for mongory_array
257
+ */
258
+ static bool gc_mark_array_cb(mongory_value *value, void *acc) {
259
+ (void)acc;
260
+ if (value && value->origin) rb_gc_mark((VALUE)value->origin);
261
+ return true;
262
+ }
263
+
264
+ /**
265
+ * GC marking callback for mongory_matcher
266
+ */
267
+ static void ruby_mongory_matcher_mark(void *ptr) {
268
+ ruby_mongory_matcher_t *wrapper = (ruby_mongory_matcher_t *)ptr;
269
+ if (!wrapper) return;
270
+ wrapper->mark_list->each(wrapper->mark_list, NULL, gc_mark_array_cb);
271
+ }
272
+
273
+ /**
274
+ * Helper functions for Ruby/C conversion
275
+ */
276
+
277
+ // Helper function to convert Ruby value to C string
278
+ static char *ruby_mongory_value_to_cstr(mongory_value *value, mongory_memory_pool *pool) {
279
+ (void)pool;
280
+ VALUE rb_value = (VALUE)value->origin;
281
+ VALUE rb_str = rb_funcall(rb_value, rb_intern("inspect"), 0);
282
+ return StringValueCStr(rb_str);
283
+ }
284
+
285
+ // Helper function for primitive conversion of Ruby value to mongory_value
286
+ static mongory_value *ruby_to_mongory_value_primitive(mongory_memory_pool *pool, VALUE rb_value) {
287
+ mongory_value *mg_value = NULL;
288
+ switch (TYPE(rb_value)) {
289
+ case T_NIL:
290
+ mg_value = mongory_value_wrap_n(pool, NULL);
291
+ break;
292
+
293
+ case T_TRUE:
294
+ mg_value = mongory_value_wrap_b(pool, true);
295
+ break;
296
+
297
+ case T_FALSE:
298
+ mg_value = mongory_value_wrap_b(pool, false);
299
+ break;
300
+
301
+ case T_FIXNUM:
302
+ mg_value = mongory_value_wrap_i(pool, rb_num2long_inline(rb_value));
303
+ break;
304
+
305
+ case T_BIGNUM:
306
+ mg_value = mongory_value_wrap_i(pool, rb_num2ll_inline(rb_value));
307
+ break;
308
+
309
+ case T_FLOAT:
310
+ mg_value = mongory_value_wrap_d(pool, rb_num2dbl(rb_value));
311
+ break;
312
+
313
+ case T_STRING:
314
+ mg_value = mongory_value_wrap_s(pool, StringValueCStr(rb_value));
315
+ break;
316
+
317
+ case T_SYMBOL:
318
+ mg_value = mongory_value_wrap_s(pool, (char *)rb_id2name(rb_sym2id(rb_value)));
319
+ break;
320
+
321
+ case T_REGEXP:
322
+ mg_value = mongory_value_wrap_regex(pool, (void *)rb_value);
323
+ break;
324
+ }
325
+ return mg_value;
326
+ }
327
+ // Shallow conversion: Convert Ruby value to mongory_value (fully materialize arrays/tables)
328
+ static mongory_value *ruby_to_mongory_value_shallow_rec(mongory_memory_pool *pool, VALUE rb_value, bool converted) {
329
+ mongory_value *mg_value = ruby_to_mongory_value_primitive(pool, rb_value);
330
+ if (mg_value) {
331
+ mg_value->origin = rb_value;
332
+ return mg_value;
333
+ }
334
+
335
+ switch (TYPE(rb_value)) {
336
+ case T_ARRAY: {
337
+ mg_value = ruby_mongory_array_wrap(pool, rb_value);
338
+ break;
339
+ }
340
+
341
+ case T_HASH: {
342
+ mg_value = ruby_mongory_table_wrap(pool, rb_value);
343
+ break;
344
+ }
345
+
346
+ default:
347
+ if (converted) {
348
+ mg_value = mongory_value_wrap_u(pool, (void *)rb_value);
349
+ break;
350
+ } else {
351
+ VALUE converted_value = rb_funcall(inMongoryDataConverter, rb_intern("convert"), 1, rb_value);
352
+ return ruby_to_mongory_value_shallow_rec(pool, converted_value, true);
353
+ }
354
+ }
355
+ mg_value->origin = rb_value;
356
+ return mg_value;
357
+ }
358
+
359
+ // Shallow conversion: Convert Ruby value to mongory_value (fully materialize arrays/tables)
360
+ mongory_value *ruby_to_mongory_value_shallow(mongory_memory_pool *pool, VALUE rb_value) {
361
+ return ruby_to_mongory_value_shallow_rec(pool, rb_value, false);
362
+ }
363
+
364
+ // Helper function for deep conversion of hash values
365
+ static int hash_foreach_deep_convert_cb(VALUE key, VALUE val, VALUE ptr) {
366
+ hash_conv_ctx *ctx = (hash_conv_ctx *)ptr;
367
+ ruby_mongory_memory_pool_t *rb_pool = (ruby_mongory_memory_pool_t *)ctx->pool;
368
+ ruby_mongory_matcher_t *owner = rb_pool->owner;
369
+ mongory_table *store_map;
370
+ char *key_str;
371
+ if (SYMBOL_P(key)) {
372
+ key_str = (char *)rb_id2name(SYM2ID(key));
373
+ store_map = owner->symbol_map;
374
+ } else {
375
+ key_str = StringValueCStr(key);
376
+ store_map = owner->string_map;
377
+ }
378
+ mongory_value *store = mongory_value_wrap_u(ctx->pool, NULL);
379
+ store->origin = (void *)key;
380
+ store_map->set(store_map, key_str, store);
381
+ mongory_value *cval = ruby_to_mongory_value_deep(ctx->pool, val);
382
+ ctx->table->set(ctx->table, key_str, cval);
383
+ return ST_CONTINUE;
384
+ }
385
+
386
+ // Deep conversion: Convert Ruby value to mongory_value (fully materialize arrays/tables)
387
+ static mongory_value *ruby_to_mongory_value_deep_rec(mongory_memory_pool *pool, VALUE rb_value, bool converted) {
388
+ mongory_value *mg_value = ruby_to_mongory_value_primitive(pool, rb_value);
389
+ if (mg_value) {
390
+ mg_value->origin = rb_value;
391
+ return mg_value;
392
+ }
393
+ switch (TYPE(rb_value)) {
394
+ case T_ARRAY: {
395
+ mongory_array *array = mongory_array_new(pool);
396
+
397
+ for (long i = 0; i < RARRAY_LEN(rb_value); i++) {
398
+ array->push(array, ruby_to_mongory_value_deep(pool, RARRAY_AREF(rb_value, i)));
399
+ }
400
+ mg_value = mongory_value_wrap_a(pool, array);
401
+ break;
402
+ }
403
+
404
+ case T_HASH: {
405
+ mongory_table *table = mongory_table_new(pool);
406
+
407
+ hash_conv_ctx ctx = {table, pool};
408
+ rb_hash_foreach(rb_value, hash_foreach_deep_convert_cb, (VALUE)&ctx);
409
+
410
+ mg_value = mongory_value_wrap_t(pool, table);
411
+ break;
412
+ }
413
+
414
+ default:
415
+ if (converted) {
416
+ mg_value = mongory_value_wrap_u(pool, (void *)rb_value);
417
+ break;
418
+ } else {
419
+ VALUE converted_value = rb_funcall(inMongoryDataConverter, rb_intern("convert"), 1, rb_value);
420
+ mg_value = ruby_to_mongory_value_deep_rec(pool, converted_value, true);
421
+ break;
422
+ }
423
+ }
424
+ mg_value->origin = rb_value;
425
+ return mg_value;
426
+ }
427
+
428
+ // Deep conversion: Convert Ruby value to mongory_value (fully materialize arrays/tables)
429
+ mongory_value *ruby_to_mongory_value_deep(mongory_memory_pool *pool, VALUE rb_value) {
430
+ return ruby_to_mongory_value_deep_rec(pool, rb_value, false);
431
+ }
432
+
433
+ // Get value from Ruby hash via mongory_table
434
+ mongory_value *ruby_mongory_table_get(mongory_table *self, char *key) {
435
+ ruby_mongory_table_t *table = (ruby_mongory_table_t *)self;
436
+ VALUE rb_hash = table->rb_hash;
437
+ VALUE rb_value = Qundef;
438
+
439
+ // Use cached Ruby String key if possible
440
+ VALUE key_str = cache_fetch_string(table->owner, key);
441
+ rb_value = rb_hash_lookup2(rb_hash, key_str, Qundef);
442
+
443
+ if (rb_value == Qundef) {
444
+ // Fallback to Symbol key, using cache
445
+ VALUE key_sym = cache_fetch_symbol(table->owner, key);
446
+ rb_value = rb_hash_lookup2(rb_hash, key_sym, Qundef);
447
+ }
448
+
449
+ if (rb_value == Qundef) {
450
+ return NULL;
451
+ }
452
+ return ruby_to_mongory_value_shallow(table->base.pool, rb_value);
453
+ }
454
+
455
+ // Shallow conversion: Wrap Ruby hash as mongory_table
456
+ mongory_value *ruby_mongory_table_wrap(mongory_memory_pool *pool, VALUE rb_hash) {
457
+ ruby_mongory_table_t *table = MG_ALLOC_PTR(pool, ruby_mongory_table_t);
458
+ ruby_mongory_memory_pool_t *rb_pool = (ruby_mongory_memory_pool_t *)pool;
459
+ table->base.pool = pool;
460
+ table->base.get = ruby_mongory_table_get;
461
+ table->rb_hash = rb_hash;
462
+ table->base.count = RHASH_SIZE(rb_hash);
463
+ table->owner = rb_pool->owner;
464
+ mongory_value *mg_value = mongory_value_wrap_t(pool, &table->base);
465
+ mg_value->origin = (void *)rb_hash;
466
+ mg_value->to_str = ruby_mongory_value_to_cstr;
467
+ return mg_value;
468
+ }
469
+
470
+ // Get value from Ruby array via mongory_array
471
+ static mongory_value *ruby_mongory_array_get(mongory_array *self, size_t index) {
472
+ ruby_mongory_array_t *array = (ruby_mongory_array_t *)self;
473
+ VALUE rb_array = array->rb_array;
474
+ if (index >= (size_t)RARRAY_LEN(rb_array)) {
475
+ return NULL;
476
+ }
477
+ VALUE rb_value = rb_ary_entry(rb_array, index);
478
+ return ruby_to_mongory_value_shallow(self->pool, rb_value);
479
+ }
480
+
481
+ // Shallow conversion: Wrap Ruby array as mongory_array
482
+ mongory_value *ruby_mongory_array_wrap(mongory_memory_pool *pool, VALUE rb_array) {
483
+ ruby_mongory_array_t *array = MG_ALLOC_PTR(pool, ruby_mongory_array_t);
484
+ ruby_mongory_memory_pool_t *rb_pool = (ruby_mongory_memory_pool_t *)pool;
485
+ array->base.pool = pool;
486
+ array->base.get = ruby_mongory_array_get;
487
+ array->rb_array = rb_array;
488
+ array->base.count = RARRAY_LEN(rb_array);
489
+ array->owner = rb_pool->owner;
490
+ mongory_value *mg_value = mongory_value_wrap_a(pool, &array->base);
491
+ mg_value->origin = (void *)rb_array;
492
+ mg_value->to_str = ruby_mongory_value_to_cstr;
493
+ return mg_value;
494
+ }
495
+
496
+ // Convert mongory_value to Ruby value (returns the original Ruby value where binding in below conversion is needed)
497
+ void *mongory_value_to_ruby(mongory_memory_pool *pool, mongory_value *value) {
498
+ (void)pool;
499
+ if (!value)
500
+ return NULL;
501
+ return value->origin;
502
+ }
503
+
504
+ // ===== Cache helper implementations =====
505
+ static VALUE cache_fetch_string(ruby_mongory_matcher_t *owner, const char *key) {
506
+ if (!owner || !owner->string_map) return rb_utf8_str_new_cstr(key);
507
+ mongory_value *v = owner->string_map->get(owner->string_map, (char *)key);
508
+ if (v && v->origin) return (VALUE)v->origin;
509
+ VALUE s = rb_utf8_str_new_cstr(key);
510
+ mongory_value *store = mongory_value_wrap_u(owner->pool, NULL);
511
+ store->origin = (void *)s;
512
+ owner->string_map->set(owner->string_map, (char *)key, store);
513
+ owner->mark_list->push(owner->mark_list, store);
514
+ return s;
515
+ }
516
+
517
+ // Helper function to convert char* key to Ruby symbol
518
+ static inline VALUE char_key_to_symbol(const char *key, rb_encoding *enc) {
519
+ ID id = rb_check_id_cstr(key, (long)strlen(key), enc);
520
+ if (!id) id = rb_intern3(key, (long)strlen(key), enc);
521
+ return ID2SYM(id);
522
+ }
523
+
524
+ // Cache helper for Ruby symbol keys
525
+ static VALUE cache_fetch_symbol(ruby_mongory_matcher_t *owner, const char *key) {
526
+ rb_encoding *enc = rb_utf8_encoding();
527
+ if (!owner || !owner->symbol_map) {
528
+ return char_key_to_symbol(key, enc);
529
+ }
530
+ mongory_value *v = owner->symbol_map->get(owner->symbol_map, (char *)key);
531
+ if (v && v->origin) return (VALUE)v->origin;
532
+ VALUE sym = char_key_to_symbol(key, enc);
533
+ mongory_value *store = mongory_value_wrap_u(owner->pool, NULL);
534
+ store->origin = (void *)sym;
535
+ owner->symbol_map->set(owner->symbol_map, (char *)key, store);
536
+ owner->mark_list->push(owner->mark_list, store);
537
+ return sym;
538
+ }
539
+
540
+ // Regex adapter bridging to Ruby's Regexp
541
+ static bool ruby_regex_match_adapter(mongory_memory_pool *pool, mongory_value *pattern, mongory_value *value) {
542
+ if (!pattern || !value) {
543
+ return false;
544
+ }
545
+ if (value->type != MONGORY_TYPE_STRING) {
546
+ return false;
547
+ }
548
+
549
+ VALUE rb_str = (VALUE)value->origin;
550
+ VALUE rb_re = Qnil;
551
+
552
+ if (pattern->type == MONGORY_TYPE_REGEX && pattern->data.regex) {
553
+ rb_re = (VALUE)pattern->data.regex;
554
+ } else if (pattern->type == MONGORY_TYPE_STRING) {
555
+ rb_re = rb_funcall(rb_cRegexp, rb_intern("new"), 1, (VALUE)pattern->origin);
556
+ mongory_value *temp_value = mongory_value_wrap_regex(pool, (void *)rb_re);
557
+ memcpy(pattern, temp_value, sizeof(mongory_value));
558
+ pattern->origin = (void *)rb_re;
559
+ } else {
560
+ return false;
561
+ }
562
+
563
+ VALUE matched = rb_funcall(rb_re, rb_intern("match?"), 1, rb_str);
564
+ return RTEST(matched);
565
+ }
566
+
567
+ // Regex adapter bridging to Ruby's Regexp
568
+ static char *ruby_regex_stringify_adapter(mongory_memory_pool *pool, mongory_value *pattern) {
569
+ (void)pool;
570
+ if (pattern->type != MONGORY_TYPE_REGEX) {
571
+ return NULL;
572
+ }
573
+ VALUE rb_re = (VALUE)pattern->data.regex;
574
+ VALUE rb_str = rb_funcall(rb_re, rb_intern("inspect"), 0);
575
+ return StringValueCStr(rb_str);
576
+ }
577
+
578
+ // Custom matcher adapter bridging to Ruby's custom matcher
579
+ static mongory_matcher_custom_context *ruby_custom_matcher_build(char *key, mongory_value *condition, void *ctx) {
580
+ mongory_memory_pool *pool = condition->pool;
581
+ ruby_mongory_memory_pool_t *rb_pool = (ruby_mongory_memory_pool_t *)pool;
582
+ ruby_mongory_matcher_t *owner = rb_pool->owner;
583
+ VALUE matcher_class = rb_funcall(mMongoryMatchers, rb_intern("lookup"), 1, cache_fetch_string(owner, key));
584
+ if (matcher_class == Qnil) {
585
+ return NULL;
586
+ }
587
+ VALUE kw_hash = rb_hash_new();
588
+ rb_hash_aset(kw_hash, ID2SYM(rb_intern("context")), (VALUE)ctx);
589
+
590
+ #ifdef RB_PASS_KEYWORDS
591
+ VALUE argv_new[2] = { (VALUE)condition->origin, kw_hash };
592
+ VALUE matcher = rb_funcallv_kw(matcher_class, rb_intern("new"), 2, argv_new, RB_PASS_KEYWORDS);
593
+ #else
594
+ VALUE matcher = rb_funcall(matcher_class, rb_intern("new"), 2, (VALUE)condition->origin, kw_hash);
595
+ #endif
596
+
597
+ if (matcher == Qnil) {
598
+ return NULL;
599
+ }
600
+ mongory_value *matcher_value = mongory_value_wrap_u(pool, NULL);
601
+ matcher_value->origin = (void *)matcher;
602
+ owner->mark_list->push(owner->mark_list, matcher_value);
603
+ VALUE class_name = rb_funcall(matcher_class, rb_intern("name"), 0);
604
+ mongory_matcher_custom_context *return_ctx = MG_ALLOC_PTR(pool, mongory_matcher_custom_context);
605
+ if (return_ctx == NULL) {
606
+ return NULL;
607
+ }
608
+ return_ctx->name = StringValueCStr(class_name);
609
+ return_ctx->external_matcher = (void *)matcher;
610
+ return return_ctx;
611
+ }
612
+
613
+ // Custom matcher adapter bridging to Ruby's custom matcher
614
+ static bool ruby_custom_matcher_match(void *ruby_matcher, mongory_value *value) {
615
+ VALUE matcher = (VALUE)ruby_matcher;
616
+ VALUE match_result = rb_funcall(matcher, rb_intern("match?"), 1, value->origin);
617
+ return RTEST(match_result);
618
+ }
619
+
620
+ // Custom matcher adapter bridging to Ruby's custom matcher
621
+ static bool ruby_custom_matcher_lookup(char *key) {
622
+ VALUE matcher_class = rb_funcall(mMongoryMatchers, rb_intern("lookup"), 1, rb_str_new_cstr(key));
623
+ return RTEST(matcher_class);
624
+ }
625
+
626
+ // Error handling for mongory_memory_pool
627
+ static bool mongory_error_handling(mongory_memory_pool *pool, char *error_message) {
628
+ if (pool->error) {
629
+ rb_raise(eMongoryTypeError, "%s: %s", error_message, pool->error->message);
630
+ pool->error = NULL;
631
+ pool->reset(pool);
632
+ return true;
633
+ }
634
+ return false;
635
+ }
636
+
637
+ /**
638
+ * Extension initialization
639
+ */
640
+ void Init_mongory_ext(void) {
641
+ // Initialize mongory core
642
+ mongory_init();
643
+
644
+ // Define modules and classes
645
+ mMongory = rb_define_module("Mongory");
646
+ cMongoryMatcher = rb_define_class_under(mMongory, "CMatcher", rb_cObject);
647
+ mMongoryMatchers = rb_define_module_under(mMongory, "Matchers");
648
+ VALUE mMongoryUtils = rb_define_module_under(mMongory, "Utils");
649
+ cMongoryMatcherContext = rb_define_class_under(mMongoryUtils, "Context", rb_cObject);
650
+ // Mongory converters
651
+ inMongoryDataConverter = rb_funcall(mMongory, rb_intern("data_converter"), 0);
652
+ inMongoryConditionConverter = rb_funcall(mMongory, rb_intern("condition_converter"), 0);
653
+
654
+ // Define error classes
655
+ eMongoryError = rb_define_class_under(mMongory, "Error", rb_eStandardError);
656
+ eMongoryTypeError = rb_define_class_under(mMongory, "TypeError", eMongoryError);
657
+
658
+ // Define Matcher methods
659
+ rb_define_singleton_method(cMongoryMatcher, "new", ruby_mongory_matcher_new, -1);
660
+ rb_define_singleton_method(cMongoryMatcher, "trace_result_colorful=", ruby_mongory_matcher_trace_result_colorful, 1);
661
+ rb_define_method(cMongoryMatcher, "match?", ruby_mongory_matcher_match, 1);
662
+ rb_define_method(cMongoryMatcher, "explain", ruby_mongory_matcher_explain, 0);
663
+ rb_define_method(cMongoryMatcher, "condition", ruby_mongory_matcher_condition, 0);
664
+ rb_define_method(cMongoryMatcher, "context", ruby_mongory_matcher_context, 0);
665
+ rb_define_method(cMongoryMatcher, "trace", ruby_mongory_matcher_trace, 1);
666
+ rb_define_method(cMongoryMatcher, "enable_trace", ruby_mongory_matcher_enable_trace, 0);
667
+ rb_define_method(cMongoryMatcher, "disable_trace", ruby_mongory_matcher_disable_trace, 0);
668
+ rb_define_method(cMongoryMatcher, "print_trace", ruby_mongory_matcher_print_trace, 0);
669
+
670
+ // Set regex adapter to use Ruby's Regexp
671
+ mongory_regex_func_set(ruby_regex_match_adapter);
672
+ mongory_regex_stringify_func_set(ruby_regex_stringify_adapter);
673
+
674
+ // Set value converter functions
675
+ mongory_value_converter_deep_convert_set(ruby_to_mongory_value_deep);
676
+ mongory_value_converter_shallow_convert_set(ruby_to_mongory_value_shallow);
677
+ mongory_value_converter_recover_set(mongory_value_to_ruby);
678
+
679
+ // Set custom matcher adapter
680
+ mongory_custom_matcher_match_func_set(ruby_custom_matcher_match);
681
+ mongory_custom_matcher_build_func_set(ruby_custom_matcher_build);
682
+ mongory_custom_matcher_lookup_func_set(ruby_custom_matcher_lookup);
683
+ }