/src/mruby/mrbgems/mruby-set/src/set.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** set.c - Set class |
3 | | ** |
4 | | ** See Copyright Notice in mruby.h |
5 | | */ |
6 | | |
7 | | #include <mruby.h> |
8 | | #include <mruby/array.h> |
9 | | #include <mruby/class.h> |
10 | | #include <mruby/hash.h> |
11 | | #include <mruby/string.h> |
12 | | #include <mruby/variable.h> |
13 | | #include <mruby/proc.h> |
14 | | #include <mruby/data.h> |
15 | | #include <mruby/internal.h> |
16 | | #include <mruby/presym.h> |
17 | | #include <mruby/khash.h> |
18 | | |
19 | | /* Use khash.h for set implementation - set mode (no values, only keys) */ |
20 | | KHASH_DECLARE(set_val, mrb_value, char, FALSE) /* FALSE = set mode */ |
21 | | |
22 | | /* Hash and equality functions for mrb_value keys */ |
23 | | static inline khint_t |
24 | | kset_hash_value(mrb_state *mrb, mrb_value key) |
25 | 17.7k | { |
26 | 17.7k | return (khint_t)mrb_obj_hash_code(mrb, key); |
27 | 17.7k | } |
28 | | |
29 | | static inline mrb_bool |
30 | | kset_equal_value(mrb_state *mrb, mrb_value a, mrb_value b) |
31 | 24.1k | { |
32 | 24.1k | return mrb_eql(mrb, a, b); |
33 | 24.1k | } |
34 | | |
35 | | KHASH_DEFINE(set_val, mrb_value, char, FALSE, kset_hash_value, kset_equal_value) |
36 | | |
37 | | #define KSET_INITIAL_SIZE 4 |
38 | 0 | #define GOLDEN_RATIO_PRIME 0x9e3779b97f4a7c15ULL |
39 | | |
40 | | /* Compatibility layer and type definitions */ |
41 | | typedef kh_set_val_t kset_t; |
42 | | typedef khint_t kset_iter_t; |
43 | | |
44 | | /* API Aliases to khash.h */ |
45 | 0 | #define kset_init(mrb) kh_init(set_val, mrb) |
46 | 750 | #define kset_init_data(mrb, s, sz) kh_init_data(set_val, mrb, s, sz) |
47 | 750 | #define kset_destroy_data(mrb, s) kh_destroy_data(set_val, mrb, s) |
48 | 0 | #define kset_clear(mrb, s) kh_clear(set_val, mrb, s) |
49 | 0 | #define kset_resize(mrb, s, sz) kh_resize(set_val, mrb, s, sz) |
50 | 7.56k | #define kset_put(mrb, s, k) kh_put(set_val, mrb, s, k) |
51 | 0 | #define kset_put2(mrb, s, k, r) kh_put2(set_val, mrb, s, k, r) |
52 | 4.54k | #define kset_get(mrb, s, k) kh_get(set_val, mrb, s, k) |
53 | 916 | #define kset_del(mrb, s, k) kh_del(set_val, mrb, s, k) |
54 | | #define kset_exist(s, k) kh_exist(set_val, s, k) |
55 | 5.21k | #define kset_key(s, k) kh_key(set_val, s, k) |
56 | 256 | #define kset_size(s) kh_size(s) |
57 | 4.54k | #define kset_end(s) kh_end(s) |
58 | | |
59 | 551 | #define KSET_FOREACH(s, k) KHASH_FOREACH(set_val, s, k) |
60 | | |
61 | | /* Helper macros for set state checking */ |
62 | 5.46k | #define kset_is_uninitialized(s) ((s)->data == NULL) |
63 | 249 | #define kset_is_empty(s) (kset_is_uninitialized(s) || kset_size(s) == 0) |
64 | | |
65 | | /* Copy all elements from src to dst (merge operation) */ |
66 | | static void |
67 | | kset_copy_merge(mrb_state *mrb, kset_t *dst, kset_t *src) |
68 | 161 | { |
69 | 161 | if (!kset_is_empty(src)) { |
70 | 161 | int ai = mrb_gc_arena_save(mrb); |
71 | 4.21k | KSET_FOREACH(src, k) { |
72 | 2.38k | kset_put(mrb, dst, kset_key(src, k)); |
73 | 2.38k | mrb_gc_arena_restore(mrb, ai); |
74 | 2.38k | } |
75 | 161 | } |
76 | 161 | } |
77 | | |
78 | | |
79 | | /* Embedded set structure in RSet - exactly 3 pointers */ |
80 | | struct RSet { |
81 | | MRB_OBJECT_HEADER; |
82 | | kset_t set; /* Embedded directly, not a pointer */ |
83 | | }; |
84 | | |
85 | | mrb_static_assert_object_size(struct RSet); |
86 | | |
87 | 5.96k | #define mrb_set_ptr(o) ((struct RSet*)mrb_obj_ptr(o)) |
88 | | |
89 | | /* Get pointer to embedded set */ |
90 | | static kset_t* |
91 | | set_get_kset(mrb_state *mrb, mrb_value self) |
92 | 5.96k | { |
93 | 5.96k | mrb_check_type(mrb, self, MRB_TT_SET); |
94 | 5.96k | return &mrb_set_ptr(self)->set; |
95 | 5.96k | } |
96 | | |
97 | | /* Helper function to ensure set is initialized */ |
98 | | static void |
99 | | set_ensure_initialized(mrb_state *mrb, kset_t *set) |
100 | 3.99k | { |
101 | 3.99k | if (kset_is_uninitialized(set)) { |
102 | 0 | mrb_raise(mrb, E_RUNTIME_ERROR, "uninitialized Set"); |
103 | 0 | } |
104 | 3.99k | } |
105 | | |
106 | | /* Mark function for Set instances */ |
107 | | size_t |
108 | | mrb_gc_mark_set(mrb_state *mrb, struct RBasic *obj) |
109 | 29 | { |
110 | 29 | struct RSet *s = (struct RSet*)obj; |
111 | 29 | kset_t *set = &s->set; |
112 | 29 | if (kset_is_empty(set)) return 0; |
113 | | |
114 | 1.84k | KSET_FOREACH(set, k) { |
115 | 997 | mrb_gc_mark_value(mrb, kset_key(set, k)); |
116 | 997 | } |
117 | 29 | return set->size; |
118 | 29 | } |
119 | | |
120 | | void |
121 | | mrb_gc_free_set(mrb_state *mrb, struct RBasic *obj) |
122 | 750 | { |
123 | 750 | struct RSet *s = (struct RSet*)obj; |
124 | 750 | kset_destroy_data(mrb, &s->set); |
125 | 750 | } |
126 | | |
127 | | size_t |
128 | | mrb_set_memsize(mrb_value set) |
129 | 0 | { |
130 | 0 | size_t size = sizeof(struct RSet); |
131 | 0 | struct RSet *s = mrb_set_ptr(set); |
132 | 0 | kset_t *kset = &s->set; |
133 | 0 | if (kset->data) { |
134 | | /* New khash layout: keys + flags in single allocation */ |
135 | 0 | size += sizeof(mrb_value) * kset->n_buckets; /* keys */ |
136 | 0 | size += kset->n_buckets / 4; /* flags */ |
137 | 0 | } |
138 | 0 | return size; |
139 | 0 | } |
140 | | |
141 | | /* Helper function to check if a value is a Set and return a boolean result */ |
142 | | static mrb_bool |
143 | | set_is_set(mrb_value obj) |
144 | 941 | { |
145 | 941 | return mrb_type(obj) == MRB_TT_SET; |
146 | 941 | } |
147 | | |
148 | | /* Helper function to check if a value is a Set and raise an error if not */ |
149 | | static void |
150 | | set_check_type(mrb_state *mrb, mrb_value obj) |
151 | 0 | { |
152 | 0 | if (!set_is_set(obj)) { |
153 | 0 | mrb_raise(mrb, E_ARGUMENT_ERROR, "value must be a set"); |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | static mrb_value |
158 | | set_init(mrb_state *mrb, mrb_value self) |
159 | 360 | { |
160 | 360 | kset_t *set = set_get_kset(mrb, self); |
161 | 360 | kset_init_data(mrb, set, KSET_INITIAL_SIZE); |
162 | 360 | return self; |
163 | 360 | } |
164 | | |
165 | | /* |
166 | | * call-seq: |
167 | | * set.initialize_copy(orig) |
168 | | * Copy constructor. |
169 | | */ |
170 | | static mrb_value |
171 | | set_init_copy(mrb_state *mrb, mrb_value self) |
172 | 390 | { |
173 | 390 | mrb_value orig = mrb_get_arg1(mrb); |
174 | | |
175 | 390 | if (mrb_type(orig) != MRB_TT_SET) { |
176 | 0 | mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take a Set object"); |
177 | 0 | } |
178 | 390 | if (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig)) { |
179 | 0 | mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object"); |
180 | 0 | } |
181 | | |
182 | 390 | kset_t *orig_set = set_get_kset(mrb, orig); |
183 | 390 | set_ensure_initialized(mrb, orig_set); |
184 | | |
185 | 390 | kset_t *self_set = set_get_kset(mrb, self); |
186 | 390 | kset_init_data(mrb, self_set, kset_size(orig_set)); |
187 | 390 | kh_replace(set_val, mrb, self_set, orig_set); |
188 | | |
189 | 390 | return self; |
190 | 390 | } |
191 | | |
192 | | /* |
193 | | * call-seq: |
194 | | * set.size -> integer |
195 | | * set.length -> integer |
196 | | * |
197 | | * Returns the number of elements. |
198 | | */ |
199 | | static mrb_value |
200 | | set_size(mrb_state *mrb, mrb_value self) |
201 | 0 | { |
202 | 0 | kset_t *set = set_get_kset(mrb, self); |
203 | 0 | if (kset_is_empty(set)) return mrb_fixnum_value(0); |
204 | 0 | return mrb_fixnum_value(kset_size(set)); |
205 | 0 | } |
206 | | |
207 | | /* |
208 | | * call-seq: |
209 | | * set.empty? -> true or false |
210 | | * |
211 | | * Returns true if the set contains no elements. |
212 | | */ |
213 | | static mrb_value |
214 | | set_empty_p(mrb_state *mrb, mrb_value self) |
215 | 0 | { |
216 | 0 | kset_t *set = set_get_kset(mrb, self); |
217 | 0 | return mrb_bool_value(kset_is_empty(set)); |
218 | 0 | } |
219 | | |
220 | | /* |
221 | | * call-seq: |
222 | | * set.clear -> self |
223 | | * |
224 | | * Removes all elements and returns self. |
225 | | */ |
226 | | static mrb_value |
227 | | set_clear(mrb_state *mrb, mrb_value self) |
228 | 0 | { |
229 | 0 | kset_t *set = set_get_kset(mrb, self); |
230 | 0 | if (!kset_is_empty(set)) { |
231 | 0 | kset_clear(mrb, set); |
232 | 0 | } |
233 | 0 | return self; |
234 | 0 | } |
235 | | |
236 | | /* |
237 | | * call-seq: |
238 | | * set.to_a -> array |
239 | | * |
240 | | * Converts the set to an array. |
241 | | */ |
242 | | static mrb_value |
243 | | set_to_a(mrb_state *mrb, mrb_value self) |
244 | 0 | { |
245 | 0 | kset_t *set = set_get_kset(mrb, self); |
246 | |
|
247 | 0 | if (kset_is_empty(set)) return mrb_ary_new(mrb); |
248 | | |
249 | 0 | mrb_value ary = mrb_ary_new_capa(mrb, kset_size(set)); |
250 | |
|
251 | 0 | int ai = mrb_gc_arena_save(mrb); |
252 | 0 | KSET_FOREACH(set, k) { |
253 | 0 | mrb_ary_push(mrb, ary, kset_key(set, k)); |
254 | 0 | mrb_gc_arena_restore(mrb, ai); |
255 | 0 | } |
256 | |
|
257 | 0 | return ary; |
258 | 0 | } |
259 | | |
260 | | /* |
261 | | * call-seq: |
262 | | * set.include?(object) -> true or false |
263 | | * set.member?(object) -> true or false |
264 | | * set === object -> true or false |
265 | | * |
266 | | * Returns true if the set contains the given object. |
267 | | */ |
268 | | static mrb_value |
269 | | set_include_p(mrb_state *mrb, mrb_value self) |
270 | 0 | { |
271 | 0 | mrb_value obj = mrb_get_arg1(mrb); |
272 | 0 | kset_t *set = set_get_kset(mrb, self); |
273 | 0 | if (kset_is_empty(set)) return mrb_false_value(); |
274 | | |
275 | 0 | return mrb_bool_value(kset_get(mrb, set, obj) != kset_end(set)); |
276 | 0 | } |
277 | | |
278 | | /* |
279 | | * call-seq: |
280 | | * set.add(object) -> self |
281 | | * set << object -> self |
282 | | * |
283 | | * Adds the given object to the set and returns self. |
284 | | */ |
285 | | static mrb_value |
286 | | set_add(mrb_state *mrb, mrb_value self) |
287 | 3.60k | { |
288 | 3.60k | mrb_value obj = mrb_get_arg1(mrb); |
289 | 3.60k | kset_t *set = set_get_kset(mrb, self); |
290 | 3.60k | set_ensure_initialized(mrb, set); |
291 | | |
292 | 3.60k | kset_put(mrb, set, obj); |
293 | 3.60k | return self; |
294 | 3.60k | } |
295 | | |
296 | | /* |
297 | | * call-seq: |
298 | | * set.add?(object) -> self or nil |
299 | | * |
300 | | * Adds the given object to the set and returns self. If the object is already |
301 | | * in the set, returns nil. |
302 | | */ |
303 | | static mrb_value |
304 | | set_add_p(mrb_state *mrb, mrb_value self) |
305 | 0 | { |
306 | 0 | mrb_value obj = mrb_get_arg1(mrb); |
307 | 0 | kset_t *set = set_get_kset(mrb, self); |
308 | 0 | set_ensure_initialized(mrb, set); |
309 | |
|
310 | 0 | int ret; |
311 | 0 | kset_put2(mrb, set, obj, &ret); |
312 | 0 | return (ret == 0) ? mrb_nil_value() : self; |
313 | 0 | } |
314 | | |
315 | | /* |
316 | | * call-seq: |
317 | | * set.delete(object) -> self |
318 | | * |
319 | | * Deletes the given object from the set and returns self. |
320 | | */ |
321 | | static mrb_value |
322 | | set_delete(mrb_state *mrb, mrb_value self) |
323 | 20 | { |
324 | 20 | mrb_value obj = mrb_get_arg1(mrb); |
325 | 20 | kset_t *set = set_get_kset(mrb, self); |
326 | 20 | if (kset_is_empty(set)) return self; |
327 | | |
328 | 20 | kset_iter_t k = kset_get(mrb, set, obj); |
329 | 20 | if (k != kset_end(set)) { |
330 | 0 | kset_del(mrb, set, k); |
331 | 0 | } |
332 | 20 | return self; |
333 | 20 | } |
334 | | |
335 | | /* |
336 | | * call-seq: |
337 | | * set.delete?(object) -> self or nil |
338 | | * |
339 | | * Deletes the given object from the set and returns self. If the object is not |
340 | | * in the set, returns nil. |
341 | | */ |
342 | | static mrb_value |
343 | | set_delete_p(mrb_state *mrb, mrb_value self) |
344 | 0 | { |
345 | 0 | mrb_value obj = mrb_get_arg1(mrb); |
346 | 0 | kset_t *set = set_get_kset(mrb, self); |
347 | 0 | if (kset_is_empty(set)) return mrb_nil_value(); |
348 | | |
349 | 0 | kset_iter_t k = kset_get(mrb, set, obj); |
350 | 0 | if (k != kset_end(set)) { |
351 | 0 | kset_del(mrb, set, k); |
352 | 0 | return self; |
353 | 0 | } |
354 | 0 | else { |
355 | 0 | return mrb_nil_value(); |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | | /* |
360 | | * Core implementation of Set-to-Set merge (mutating version) |
361 | | * This is an internal method that will be called from Ruby |
362 | | */ |
363 | | static mrb_value |
364 | | set_core_merge(mrb_state *mrb, mrb_value self) |
365 | 211 | { |
366 | 211 | mrb_value other = mrb_get_arg1(mrb); |
367 | | |
368 | 211 | if (!set_is_set(other)) { |
369 | 211 | return mrb_false_value(); |
370 | 211 | } |
371 | | |
372 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
373 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
374 | |
|
375 | 0 | set_ensure_initialized(mrb, self_set); |
376 | 0 | if (!kset_is_empty(other_set)) { |
377 | 0 | kset_copy_merge(mrb, self_set, other_set); |
378 | 0 | } |
379 | |
|
380 | 0 | return mrb_true_value(); |
381 | 211 | } |
382 | | |
383 | | /* |
384 | | * Core implementation of Set-to-Set subtraction (mutating version) |
385 | | * This is an internal method that will be called from Ruby |
386 | | */ |
387 | | static mrb_value |
388 | | set_core_subtract(mrb_state *mrb, mrb_value self) |
389 | 0 | { |
390 | 0 | mrb_value other = mrb_get_arg1(mrb); |
391 | |
|
392 | 0 | if (!set_is_set(other)) { |
393 | 0 | return mrb_false_value(); |
394 | 0 | } |
395 | | |
396 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
397 | 0 | if (kset_is_empty(self_set)) return mrb_true_value(); |
398 | | |
399 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
400 | 0 | if (kset_is_empty(other_set)) return mrb_true_value(); |
401 | | |
402 | | /* Remove all elements that are in other set */ |
403 | 0 | KSET_FOREACH(other_set, k) { |
404 | 0 | mrb_value key = kset_key(other_set, k); |
405 | 0 | kset_iter_t self_k = kset_get(mrb, self_set, key); |
406 | 0 | if (self_k != kset_end(self_set)) { |
407 | 0 | kset_del(mrb, self_set, self_k); |
408 | 0 | } |
409 | 0 | } |
410 | |
|
411 | 0 | return mrb_true_value(); |
412 | 0 | } |
413 | | |
414 | | /* |
415 | | * Core implementation of Set-to-Set union |
416 | | * This is an internal method that will be called from Ruby |
417 | | */ |
418 | | static mrb_value |
419 | | set_core_union(mrb_state *mrb, mrb_value self) |
420 | 209 | { |
421 | 209 | mrb_value other = mrb_get_arg1(mrb); |
422 | | |
423 | 209 | if (!set_is_set(other)) { |
424 | 48 | return mrb_nil_value(); |
425 | 48 | } |
426 | | |
427 | | /* Create a new set by duplicating self */ |
428 | 161 | mrb_value result = mrb_obj_dup(mrb, self); |
429 | 161 | kset_t *result_set = set_get_kset(mrb, result); |
430 | 161 | if (kset_is_uninitialized(result_set)) { |
431 | | /* If self is empty, initialize the set */ |
432 | 0 | kset_init_data(mrb, result_set, KSET_INITIAL_SIZE); |
433 | 0 | } |
434 | | |
435 | | /* Add all elements from other set */ |
436 | 161 | kset_t *other_set = set_get_kset(mrb, other); |
437 | 161 | if (!kset_is_uninitialized(other_set)) { |
438 | 161 | kset_copy_merge(mrb, result_set, other_set); |
439 | 161 | } |
440 | | |
441 | 161 | return result; |
442 | 209 | } |
443 | | |
444 | | /* |
445 | | * Core implementation of Set-to-Set difference |
446 | | * This is an internal method that will be called from Ruby |
447 | | */ |
448 | | static mrb_value |
449 | | set_core_difference(mrb_state *mrb, mrb_value self) |
450 | 181 | { |
451 | 181 | mrb_value other = mrb_get_arg1(mrb); |
452 | | |
453 | 181 | if (!set_is_set(other)) { |
454 | 20 | return mrb_nil_value(); |
455 | 20 | } |
456 | | |
457 | | /* Create a new set by duplicating self */ |
458 | 161 | mrb_value result = mrb_obj_dup(mrb, self); |
459 | 161 | kset_t *result_set = set_get_kset(mrb, result); |
460 | 161 | if (kset_is_uninitialized(result_set)) { |
461 | | /* If self is empty, return an empty set */ |
462 | 0 | return result; |
463 | 0 | } |
464 | | |
465 | | /* Remove all elements that are in other set */ |
466 | 161 | kset_t *other_set = set_get_kset(mrb, other); |
467 | 161 | if (!kset_is_uninitialized(other_set)) { |
468 | 1.50k | KSET_FOREACH(other_set, k) { |
469 | 916 | mrb_value key = kset_key(other_set, k); |
470 | 916 | kset_iter_t result_k = kset_get(mrb, result_set, key); |
471 | 916 | if (result_k != kset_end(result_set)) { |
472 | 916 | kset_del(mrb, result_set, result_k); |
473 | 916 | } |
474 | 916 | } |
475 | 161 | } |
476 | | |
477 | 161 | return result; |
478 | 161 | } |
479 | | |
480 | | |
481 | | /* |
482 | | * Core implementation of Set-to-Set intersection |
483 | | * This is an internal method that will be called from Ruby |
484 | | */ |
485 | | static mrb_value |
486 | | set_core_intersection(mrb_state *mrb, mrb_value self) |
487 | 161 | { |
488 | 161 | mrb_value other = mrb_get_arg1(mrb); |
489 | | |
490 | 161 | if (!set_is_set(other)) { |
491 | 0 | return mrb_nil_value(); |
492 | 0 | } |
493 | | |
494 | | /* Create a new empty set of the same class as self */ |
495 | 161 | mrb_value result = mrb_obj_new(mrb, mrb_obj_class(mrb, self), 0, NULL); |
496 | 161 | kset_t *result_set = set_get_kset(mrb, result); |
497 | | |
498 | 161 | kset_t *self_set = set_get_kset(mrb, self); |
499 | 161 | if (kset_is_uninitialized(self_set)) return result; |
500 | | |
501 | 161 | kset_t *other_set = set_get_kset(mrb, other); |
502 | 161 | if (kset_is_uninitialized(other_set)) return result; |
503 | | |
504 | 4.21k | KSET_FOREACH(other_set, k) { |
505 | 2.38k | mrb_value key = kset_key(other_set, k); |
506 | 2.38k | kset_iter_t self_k = kset_get(mrb, self_set, key); |
507 | | |
508 | | /* If key exists in self, add it to result */ |
509 | 2.38k | if (self_k != kset_end(self_set)) { |
510 | 916 | kset_put(mrb, result_set, key); |
511 | 916 | } |
512 | 2.38k | } |
513 | | |
514 | 161 | return result; |
515 | 161 | } |
516 | | |
517 | | |
518 | | /* |
519 | | * Core implementation of Set-to-Set XOR (symmetric difference) |
520 | | * This is an internal method that will be called from Ruby |
521 | | */ |
522 | | static mrb_value |
523 | | set_core_xor(mrb_state *mrb, mrb_value self) |
524 | 179 | { |
525 | 179 | mrb_value other = mrb_get_arg1(mrb); |
526 | | |
527 | 179 | if (!set_is_set(other)) { |
528 | 163 | return mrb_nil_value(); |
529 | 163 | } |
530 | | |
531 | 16 | mrb_value result = mrb_obj_new(mrb, mrb_obj_class(mrb, self), 0, NULL); |
532 | 16 | kset_t *result_set = set_get_kset(mrb, result); |
533 | 16 | kset_t *self_set, *other_set; |
534 | | |
535 | 16 | self_set = set_get_kset(mrb, self); |
536 | 16 | other_set = set_get_kset(mrb, other); |
537 | | |
538 | | /* Handle empty sets */ |
539 | 16 | if (kset_is_empty(self_set)) { |
540 | 0 | if (!kset_is_empty(other_set)) { |
541 | 0 | kset_copy_merge(mrb, result_set, other_set); |
542 | 0 | } |
543 | 0 | return result; |
544 | 0 | } |
545 | 16 | if (kset_is_empty(other_set)) { |
546 | 0 | kh_replace(set_val, mrb, result_set, self_set); |
547 | 0 | return result; |
548 | 0 | } |
549 | | |
550 | | /* Add elements from self that are not in other */ |
551 | 16 | int ai = mrb_gc_arena_save(mrb); |
552 | 1.16k | KSET_FOREACH(self_set, k) { |
553 | 601 | mrb_value key = kset_key(self_set, k); |
554 | 601 | kset_iter_t other_k = kset_get(mrb, other_set, key); |
555 | | |
556 | | /* Add to result if not in other */ |
557 | 601 | if (other_k == kset_end(other_set)) { |
558 | 310 | kset_put(mrb, result_set, key); |
559 | 310 | } |
560 | 601 | mrb_gc_arena_restore(mrb, ai); |
561 | 601 | } |
562 | | |
563 | | /* Add elements from other that are not in self */ |
564 | 1.00k | KSET_FOREACH(other_set, k) { |
565 | 621 | mrb_value key = kset_key(other_set, k); |
566 | 621 | kset_iter_t self_k = kset_get(mrb, self_set, key); |
567 | | |
568 | | /* Add to result if not in self */ |
569 | 621 | if (self_k == kset_end(self_set)) { |
570 | 330 | kset_put(mrb, result_set, key); |
571 | 330 | } |
572 | 621 | mrb_gc_arena_restore(mrb, ai); |
573 | 621 | } |
574 | | |
575 | 16 | return result; |
576 | 16 | } |
577 | | |
578 | | /* |
579 | | * call-seq: |
580 | | * set == other -> true or false |
581 | | * |
582 | | * Returns true if two sets are equal. |
583 | | */ |
584 | | /* |
585 | | * call-seq: |
586 | | * set.eql?(other) -> true or false |
587 | | * |
588 | | * Returns true if two sets are equal. |
589 | | */ |
590 | | static mrb_value |
591 | | set_equal(mrb_state *mrb, mrb_value self) |
592 | 0 | { |
593 | 0 | mrb_value other = mrb_get_arg1(mrb); |
594 | | |
595 | | /* Fast path: same object */ |
596 | 0 | if (mrb_obj_equal(mrb, self, other)) { |
597 | 0 | return mrb_true_value(); |
598 | 0 | } |
599 | | |
600 | | /* Only compare with other Set objects */ |
601 | 0 | if (!set_is_set(other)) { |
602 | 0 | return mrb_false_value(); |
603 | 0 | } |
604 | | |
605 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
606 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
607 | | |
608 | | /* Fast path: both empty */ |
609 | 0 | if (kset_is_empty(self_set) && kset_is_empty(other_set)) { |
610 | 0 | return mrb_true_value(); |
611 | 0 | } |
612 | | |
613 | | /* Fast path: different sizes */ |
614 | 0 | if (kset_size(self_set) != kset_size(other_set)) { |
615 | 0 | return mrb_false_value(); |
616 | 0 | } |
617 | | |
618 | | /* Compare elements: iterate through the smaller hash for efficiency */ |
619 | 0 | int ai = mrb_gc_arena_save(mrb); |
620 | 0 | KSET_FOREACH(self_set, k) { |
621 | 0 | kset_iter_t k2 = kset_get(mrb, other_set, kset_key(self_set, k)); |
622 | 0 | if (k2 == kset_end(other_set)) { |
623 | 0 | return mrb_false_value(); /* Element in self not found in other */ |
624 | 0 | } |
625 | 0 | mrb_gc_arena_restore(mrb, ai); |
626 | 0 | } |
627 | | |
628 | 0 | return mrb_true_value(); |
629 | 0 | } |
630 | | |
631 | | /* |
632 | | * call-seq: |
633 | | * set.hash -> integer |
634 | | * |
635 | | * Compute a hash-code for this set. |
636 | | * Uses an improved hash algorithm for better distribution. |
637 | | */ |
638 | | static mrb_value |
639 | | set_hash_m(mrb_state *mrb, mrb_value self) |
640 | 0 | { |
641 | 0 | kset_t *set = set_get_kset(mrb, self); |
642 | | |
643 | | /* Use order-independent hash algorithm for sets */ |
644 | 0 | uint64_t hash = 0; /* Start with zero for XOR accumulation */ |
645 | | |
646 | | /* Include the size of the set in the hash */ |
647 | 0 | size_t size = kset_size(set); |
648 | 0 | hash ^= size * GOLDEN_RATIO_PRIME; |
649 | |
|
650 | 0 | if (!kset_is_uninitialized(set) && size > 0) { |
651 | | /* Process each element - order independent using XOR */ |
652 | 0 | int ai = mrb_gc_arena_save(mrb); |
653 | 0 | KSET_FOREACH(set, k) { |
654 | | /* Get element's hash code */ |
655 | 0 | khint_t elem_hash = (khint_t)mrb_obj_hash_code(mrb, kset_key(set, k)); |
656 | | |
657 | | /* XOR is commutative, so order doesn't matter */ |
658 | 0 | hash ^= elem_hash * GOLDEN_RATIO_PRIME; |
659 | |
|
660 | 0 | mrb_gc_arena_restore(mrb, ai); |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | | /* Final mixing to improve distribution */ |
665 | 0 | hash ^= hash >> 32; |
666 | |
|
667 | 0 | return mrb_fixnum_value((mrb_int)hash); |
668 | 0 | } |
669 | | |
670 | | /* |
671 | | * call-seq: |
672 | | * set.superset?(other) -> true or false |
673 | | * set >= other -> true or false |
674 | | * |
675 | | * Returns true if the set is a superset of the given set. |
676 | | */ |
677 | | static mrb_value |
678 | | set_superset_p(mrb_state *mrb, mrb_value self) |
679 | 0 | { |
680 | 0 | mrb_value other = mrb_get_arg1(mrb); |
681 | | |
682 | | /* Check if other is a Set */ |
683 | 0 | set_check_type(mrb, other); |
684 | |
|
685 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
686 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
687 | | |
688 | | /* Handle empty sets */ |
689 | 0 | if (kset_is_empty(other_set)) { |
690 | 0 | return mrb_true_value(); /* Empty set is a subset of any set */ |
691 | 0 | } |
692 | | |
693 | 0 | if (kset_is_uninitialized(self_set)) { |
694 | 0 | return mrb_false_value(); /* Empty set is not a superset of a non-empty set */ |
695 | 0 | } |
696 | | |
697 | | /* Check size first - a superset must be at least as large as the subset */ |
698 | 0 | if (kset_size(self_set) < kset_size(other_set)) { |
699 | 0 | return mrb_false_value(); |
700 | 0 | } |
701 | | |
702 | | /* Check if all elements in other are in self */ |
703 | 0 | int ai = mrb_gc_arena_save(mrb); |
704 | 0 | KSET_FOREACH(other_set, k) { |
705 | 0 | kset_iter_t self_k = kset_get(mrb, self_set, kset_key(other_set, k)); |
706 | 0 | if (self_k == kset_end(self_set)) { |
707 | 0 | return mrb_false_value(); /* Element in other not found in self */ |
708 | 0 | } |
709 | 0 | mrb_gc_arena_restore(mrb, ai); |
710 | 0 | } |
711 | | |
712 | 0 | return mrb_true_value(); |
713 | 0 | } |
714 | | |
715 | | /* |
716 | | * call-seq: |
717 | | * set.proper_superset?(other) -> true or false |
718 | | * set > other -> true or false |
719 | | * |
720 | | * Returns true if the set is a proper superset of the given set. |
721 | | */ |
722 | | static mrb_value |
723 | | set_proper_superset_p(mrb_state *mrb, mrb_value self) |
724 | 0 | { |
725 | 0 | mrb_value other = mrb_get_arg1(mrb); |
726 | | |
727 | | /* Check if other is a Set */ |
728 | 0 | set_check_type(mrb, other); |
729 | |
|
730 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
731 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
732 | | |
733 | | /* Handle empty sets */ |
734 | 0 | if (kset_is_empty(other_set)) { |
735 | | /* Empty set is a proper subset of any non-empty set */ |
736 | 0 | return !kset_is_empty(self_set) ? mrb_true_value() : mrb_false_value(); |
737 | 0 | } |
738 | | |
739 | 0 | if (kset_is_uninitialized(self_set)) { |
740 | 0 | return mrb_false_value(); /* Empty set is not a proper superset of any set */ |
741 | 0 | } |
742 | | |
743 | | /* For a proper superset, self must be strictly larger than other */ |
744 | 0 | if (kset_size(self_set) <= kset_size(other_set)) { |
745 | 0 | return mrb_false_value(); |
746 | 0 | } |
747 | | |
748 | | /* Check if all elements in other are in self */ |
749 | 0 | int ai = mrb_gc_arena_save(mrb); |
750 | 0 | KSET_FOREACH(other_set, k) { |
751 | 0 | kset_iter_t self_k = kset_get(mrb, self_set, kset_key(other_set, k)); |
752 | 0 | if (self_k == kset_end(self_set)) { |
753 | 0 | return mrb_false_value(); /* Element in other not found in self */ |
754 | 0 | } |
755 | 0 | mrb_gc_arena_restore(mrb, ai); |
756 | 0 | } |
757 | | |
758 | 0 | return mrb_true_value(); |
759 | 0 | } |
760 | | |
761 | | /* |
762 | | * call-seq: |
763 | | * set.subset?(other) -> true or false |
764 | | * set <= other -> true or false |
765 | | * |
766 | | * Returns true if the set is a subset of the given set. |
767 | | */ |
768 | | static mrb_value |
769 | | set_subset_p(mrb_state *mrb, mrb_value self) |
770 | 0 | { |
771 | 0 | mrb_value other = mrb_get_arg1(mrb); |
772 | | |
773 | | /* Check if other is a Set */ |
774 | 0 | set_check_type(mrb, other); |
775 | |
|
776 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
777 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
778 | | |
779 | | /* Handle empty sets */ |
780 | 0 | if (kset_is_empty(self_set)) { |
781 | 0 | return mrb_true_value(); /* Empty set is a subset of any set */ |
782 | 0 | } |
783 | | |
784 | 0 | if (kset_is_uninitialized(other_set)) { |
785 | 0 | return mrb_false_value(); /* Non-empty set is not a subset of an empty set */ |
786 | 0 | } |
787 | | |
788 | | /* Check size first - a subset cannot be larger than its superset */ |
789 | 0 | if (kset_size(other_set) < kset_size(self_set)) { |
790 | 0 | return mrb_false_value(); |
791 | 0 | } |
792 | | |
793 | | /* Check if all elements in self are in other */ |
794 | 0 | int ai = mrb_gc_arena_save(mrb); |
795 | 0 | KSET_FOREACH(self_set, k) { |
796 | 0 | kset_iter_t other_k = kset_get(mrb, other_set, kset_key(self_set, k)); |
797 | 0 | if (other_k == kset_end(other_set)) { |
798 | 0 | return mrb_false_value(); /* Element in self not found in other */ |
799 | 0 | } |
800 | 0 | mrb_gc_arena_restore(mrb, ai); |
801 | 0 | } |
802 | | |
803 | 0 | return mrb_true_value(); |
804 | 0 | } |
805 | | |
806 | | /* |
807 | | * call-seq: |
808 | | * set.proper_subset?(other) -> true or false |
809 | | * set < other -> true or false |
810 | | * |
811 | | * Returns true if the set is a proper subset of the given set. |
812 | | */ |
813 | | static mrb_value |
814 | | set_proper_subset_p(mrb_state *mrb, mrb_value self) |
815 | 0 | { |
816 | 0 | mrb_value other = mrb_get_arg1(mrb); |
817 | | |
818 | | /* Check if other is a Set */ |
819 | 0 | set_check_type(mrb, other); |
820 | |
|
821 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
822 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
823 | | |
824 | | /* Handle empty sets */ |
825 | 0 | if (kset_is_empty(self_set)) { |
826 | | /* Empty set is a proper subset of any non-empty set */ |
827 | 0 | return !kset_is_empty(other_set) ? mrb_true_value() : mrb_false_value(); |
828 | 0 | } |
829 | | |
830 | 0 | if (kset_is_uninitialized(other_set)) { |
831 | 0 | return mrb_false_value(); /* Non-empty set is not a proper subset of an empty set */ |
832 | 0 | } |
833 | | |
834 | | /* For a proper subset, self must be strictly smaller than other */ |
835 | 0 | if (kset_size(other_set) <= kset_size(self_set)) { |
836 | 0 | return mrb_false_value(); |
837 | 0 | } |
838 | | |
839 | | /* Check if all elements in self are in other */ |
840 | 0 | int ai = mrb_gc_arena_save(mrb); |
841 | 0 | KSET_FOREACH(self_set, k) { |
842 | 0 | kset_iter_t other_k = kset_get(mrb, other_set, kset_key(self_set, k)); |
843 | 0 | if (other_k == kset_end(other_set)) { |
844 | 0 | return mrb_false_value(); /* Element in self not found in other */ |
845 | 0 | } |
846 | 0 | mrb_gc_arena_restore(mrb, ai); |
847 | 0 | } |
848 | | |
849 | 0 | return mrb_true_value(); |
850 | 0 | } |
851 | | |
852 | | /* |
853 | | * call-seq: |
854 | | * set.intersect?(other) -> true or false |
855 | | * |
856 | | * Returns true if the set and the given set have at least one element in common. |
857 | | */ |
858 | | static mrb_value |
859 | | set_intersect_p(mrb_state *mrb, mrb_value self) |
860 | 0 | { |
861 | 0 | mrb_value other = mrb_get_arg1(mrb); |
862 | | |
863 | | /* Check if other is a Set */ |
864 | 0 | set_check_type(mrb, other); |
865 | |
|
866 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
867 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
868 | | |
869 | | /* Handle empty sets */ |
870 | 0 | if (kset_is_empty(self_set) || kset_is_empty(other_set)) { |
871 | 0 | return mrb_false_value(); /* Empty sets have no elements in common */ |
872 | 0 | } |
873 | | |
874 | | /* Iterate through the smaller set for efficiency */ |
875 | 0 | int ai = mrb_gc_arena_save(mrb); |
876 | 0 | if (kset_size(self_set) < kset_size(other_set)) { |
877 | 0 | KSET_FOREACH(self_set, k) { |
878 | 0 | kset_iter_t other_k = kset_get(mrb, other_set, kset_key(self_set, k)); |
879 | 0 | if (other_k != kset_end(other_set)) { |
880 | 0 | return mrb_true_value(); /* Found a common element */ |
881 | 0 | } |
882 | 0 | mrb_gc_arena_restore(mrb, ai); |
883 | 0 | } |
884 | 0 | } |
885 | 0 | else { |
886 | 0 | KSET_FOREACH(other_set, k) { |
887 | 0 | kset_iter_t self_k = kset_get(mrb, self_set, kset_key(other_set, k)); |
888 | 0 | if (self_k != kset_end(self_set)) { |
889 | 0 | return mrb_true_value(); /* Found a common element */ |
890 | 0 | } |
891 | 0 | mrb_gc_arena_restore(mrb, ai); |
892 | 0 | } |
893 | 0 | } |
894 | | |
895 | 0 | return mrb_false_value(); /* No common elements found */ |
896 | 0 | } |
897 | | |
898 | | /* |
899 | | * call-seq: |
900 | | * set.disjoint?(other) -> true or false |
901 | | * |
902 | | * Returns true if the set and the given set have no elements in common. |
903 | | */ |
904 | | static mrb_value |
905 | | set_disjoint_p(mrb_state *mrb, mrb_value self) |
906 | 0 | { |
907 | 0 | mrb_value result = set_intersect_p(mrb, self); |
908 | 0 | return mrb_bool_value(!mrb_test(result)); |
909 | 0 | } |
910 | | |
911 | | /* |
912 | | * call-seq: |
913 | | * set <=> other -> -1, 0, +1, or nil |
914 | | * |
915 | | * Compares this set with another set. |
916 | | * Returns -1 if this set is a proper subset of the other set, |
917 | | * +1 if this set is a proper superset of the other set, |
918 | | * 0 if the sets are equal, |
919 | | * or nil if the sets cannot be compared (they are neither subsets nor supersets). |
920 | | */ |
921 | | static mrb_value |
922 | | set_cmp(mrb_state *mrb, mrb_value self) |
923 | 0 | { |
924 | 0 | mrb_value other = mrb_get_arg1(mrb); |
925 | |
|
926 | 0 | if (!set_is_set(other)) { |
927 | 0 | return mrb_nil_value(); |
928 | 0 | } |
929 | | |
930 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
931 | 0 | kset_t *other_set = set_get_kset(mrb, other); |
932 | | |
933 | | /* Handle empty sets */ |
934 | 0 | if (kset_is_empty(self_set)) { |
935 | 0 | if (kset_is_empty(other_set)) { |
936 | 0 | return mrb_fixnum_value(0); /* Both empty, they're equal */ |
937 | 0 | } |
938 | 0 | return mrb_fixnum_value(-1); /* Empty set is a proper subset of any non-empty set */ |
939 | 0 | } |
940 | | |
941 | 0 | if (kset_is_empty(other_set)) { |
942 | 0 | return mrb_fixnum_value(1); /* Any non-empty set is a proper superset of an empty set */ |
943 | 0 | } |
944 | | |
945 | | /* Compare sizes */ |
946 | 0 | mrb_int size_diff = kset_size(self_set) - kset_size(other_set); |
947 | |
|
948 | 0 | if (size_diff < 0) { |
949 | | /* self might be a proper subset of other */ |
950 | 0 | int ai = mrb_gc_arena_save(mrb); |
951 | 0 | KSET_FOREACH(self_set, k) { |
952 | 0 | kset_iter_t other_k = kset_get(mrb, other_set, kset_key(self_set, k)); |
953 | 0 | if (other_k == kset_end(other_set)) { |
954 | | /* Not a subset */ |
955 | 0 | return mrb_nil_value(); /* Not comparable */ |
956 | 0 | } |
957 | 0 | mrb_gc_arena_restore(mrb, ai); |
958 | 0 | } |
959 | | |
960 | | /* All elements of self are in other, and self is smaller than other */ |
961 | 0 | return mrb_fixnum_value(-1); /* self is a proper subset of other */ |
962 | 0 | } |
963 | 0 | else if (size_diff > 0) { |
964 | | /* self might be a proper superset of other */ |
965 | 0 | int ai = mrb_gc_arena_save(mrb); |
966 | 0 | KSET_FOREACH(other_set, k) { |
967 | 0 | kset_iter_t self_k = kset_get(mrb, self_set, kset_key(other_set, k)); |
968 | 0 | if (self_k == kset_end(self_set)) { |
969 | | /* Not a superset */ |
970 | 0 | return mrb_nil_value(); /* Not comparable */ |
971 | 0 | } |
972 | 0 | mrb_gc_arena_restore(mrb, ai); |
973 | 0 | } |
974 | | |
975 | | /* All elements of other are in self, and self is larger than other */ |
976 | 0 | return mrb_fixnum_value(1); /* self is a proper superset of other */ |
977 | 0 | } |
978 | 0 | else { /* size_diff == 0 */ |
979 | | /* Same size, check if they're equal */ |
980 | 0 | mrb_bool is_equal = TRUE; |
981 | |
|
982 | 0 | int ai3 = mrb_gc_arena_save(mrb); |
983 | 0 | KSET_FOREACH(self_set, k) { |
984 | 0 | kset_iter_t other_k = kset_get(mrb, other_set, kset_key(self_set, k)); |
985 | 0 | if (other_k == kset_end(other_set)) { |
986 | 0 | is_equal = FALSE; |
987 | 0 | break; |
988 | 0 | } |
989 | 0 | mrb_gc_arena_restore(mrb, ai3); |
990 | 0 | } |
991 | |
|
992 | 0 | if (is_equal) { |
993 | 0 | return mrb_fixnum_value(0); /* Sets are equal */ |
994 | 0 | } |
995 | 0 | } |
996 | | |
997 | | /* Sets are not comparable */ |
998 | 0 | return mrb_nil_value(); |
999 | 0 | } |
1000 | | |
1001 | | /* |
1002 | | * call-seq: |
1003 | | * set.join(separator = nil) -> string |
1004 | | * |
1005 | | * Returns a string created by converting each element of the set to a string, |
1006 | | * separated by the given separator. |
1007 | | */ |
1008 | | static mrb_value |
1009 | | set_join(mrb_state *mrb, mrb_value self) |
1010 | 0 | { |
1011 | 0 | mrb_value separator = mrb_nil_value(); |
1012 | 0 | mrb_get_args(mrb, "|S", &separator); |
1013 | |
|
1014 | 0 | kset_t *set = set_get_kset(mrb, self); |
1015 | 0 | if (kset_is_empty(set)) { |
1016 | 0 | return mrb_str_new_lit(mrb, ""); |
1017 | 0 | } |
1018 | | |
1019 | | /* Get separator string */ |
1020 | 0 | const char *sep_ptr = ""; |
1021 | 0 | mrb_int sep_len = 0; |
1022 | 0 | if (!mrb_nil_p(separator)) { |
1023 | 0 | sep_ptr = RSTRING_PTR(separator); |
1024 | 0 | sep_len = RSTRING_LEN(separator); |
1025 | 0 | } |
1026 | | |
1027 | | /* Create result string */ |
1028 | 0 | mrb_value result = mrb_str_new_capa(mrb, 64); /* Initial capacity */ |
1029 | 0 | mrb_bool first = TRUE; |
1030 | | |
1031 | | /* Iterate through all elements */ |
1032 | 0 | int ai = mrb_gc_arena_save(mrb); |
1033 | 0 | KSET_FOREACH(set, k) { |
1034 | 0 | if (!first) { |
1035 | 0 | mrb_str_cat(mrb, result, sep_ptr, sep_len); |
1036 | 0 | } |
1037 | 0 | else { |
1038 | 0 | first = FALSE; |
1039 | 0 | } |
1040 | |
|
1041 | 0 | mrb_value elem = kset_key(set, k); |
1042 | 0 | mrb_value str = mrb_obj_as_string(mrb, elem); |
1043 | 0 | mrb_str_cat_str(mrb, result, str); |
1044 | |
|
1045 | 0 | mrb_gc_arena_restore(mrb, ai); |
1046 | 0 | } |
1047 | |
|
1048 | 0 | return result; |
1049 | 0 | } |
1050 | | |
1051 | | /* |
1052 | | * call-seq: |
1053 | | * set.inspect -> string |
1054 | | * set.to_s -> string |
1055 | | * |
1056 | | * Returns a string representation of the set. |
1057 | | * Format: Set[elem1, elem2, ...] |
1058 | | */ |
1059 | | static mrb_value |
1060 | | set_inspect(mrb_state *mrb, mrb_value self) |
1061 | 7 | { |
1062 | 7 | struct RClass* c = mrb_obj_class(mrb, self); |
1063 | 7 | const char* classname = mrb_class_name(mrb, c); |
1064 | 7 | kset_t *set = set_get_kset(mrb, self); |
1065 | | |
1066 | | /* Handle empty set */ |
1067 | 7 | if (kset_is_empty(set)) { |
1068 | 0 | return mrb_format(mrb, "%s[]", classname); |
1069 | 0 | } |
1070 | | |
1071 | | /* Handle recursive inspection */ |
1072 | 7 | if (MRB_RECURSIVE_UNARY_P(mrb, MRB_SYM(inspect), self)) { |
1073 | 0 | return mrb_format(mrb, "%s[...]", classname); |
1074 | 0 | } |
1075 | | |
1076 | | /* Estimate buffer size based on set size */ |
1077 | 7 | size_t size = kset_size(set); |
1078 | 7 | size_t buffer_size = 16 + strlen(classname) + (size * 8); /* Rough estimate */ |
1079 | | |
1080 | | /* Create the beginning of the string with pre-allocated capacity */ |
1081 | 7 | mrb_value result_str = mrb_str_new_capa(mrb, buffer_size); |
1082 | 7 | mrb_str_cat_cstr(mrb, result_str, classname); |
1083 | 7 | mrb_str_cat_lit(mrb, result_str, "["); |
1084 | | |
1085 | | /* Iterate through all elements */ |
1086 | 7 | mrb_bool first = TRUE; |
1087 | 7 | int ai = mrb_gc_arena_save(mrb); |
1088 | 1.79k | KSET_FOREACH(set, k) { |
1089 | 693 | if (!first) { |
1090 | 686 | mrb_str_cat_lit(mrb, result_str, ", "); |
1091 | 686 | } |
1092 | 7 | else { |
1093 | 7 | first = FALSE; |
1094 | 7 | } |
1095 | | |
1096 | 693 | mrb_value elem = kset_key(set, k); |
1097 | 693 | mrb_value entry_str = mrb_inspect(mrb, elem); |
1098 | 693 | mrb_str_cat_str(mrb, result_str, entry_str); |
1099 | | |
1100 | 693 | mrb_gc_arena_restore(mrb, ai); |
1101 | 693 | } |
1102 | | |
1103 | | /* Add the closing part */ |
1104 | 7 | mrb_str_cat_lit(mrb, result_str, "]"); |
1105 | | |
1106 | 7 | return result_str; |
1107 | 7 | } |
1108 | | |
1109 | | /* |
1110 | | * call-seq: |
1111 | | * set.reset -> self |
1112 | | * |
1113 | | * Resets the internal state after modification to existing elements. |
1114 | | * This is necessary when the hash value of objects in the set has changed. |
1115 | | * It rebuilds the hash table to ensure all elements can be found. |
1116 | | */ |
1117 | | static mrb_value |
1118 | | set_reset(mrb_state *mrb, mrb_value self) |
1119 | 0 | { |
1120 | 0 | mrb_check_frozen_value(mrb, self); |
1121 | |
|
1122 | 0 | kset_t *set = set_get_kset(mrb, self); |
1123 | 0 | if (!kset_is_empty(set)) { |
1124 | 0 | kset_resize(mrb, set, kset_size(set)); |
1125 | 0 | } |
1126 | |
|
1127 | 0 | return self; |
1128 | 0 | } |
1129 | | |
1130 | | /* |
1131 | | * call-seq: |
1132 | | * set.add_all(*objects) -> self |
1133 | | * |
1134 | | * Adds multiple objects to the set and returns self. |
1135 | | */ |
1136 | | static mrb_value |
1137 | | set_add_all(mrb_state *mrb, mrb_value self) |
1138 | 0 | { |
1139 | 0 | const mrb_value *argv; |
1140 | 0 | mrb_int argc; |
1141 | |
|
1142 | 0 | mrb_get_args(mrb, "*", &argv, &argc); |
1143 | 0 | kset_t *set = set_get_kset(mrb, self); |
1144 | 0 | set_ensure_initialized(mrb, set); |
1145 | |
|
1146 | 0 | int ai = mrb_gc_arena_save(mrb); |
1147 | 0 | for (mrb_int i = 0; i < argc; i++) { |
1148 | 0 | kset_put(mrb, set, argv[i]); |
1149 | 0 | mrb_gc_arena_restore(mrb, ai); |
1150 | 0 | } |
1151 | |
|
1152 | 0 | return self; |
1153 | 0 | } |
1154 | | |
1155 | | /* |
1156 | | * Optimized implementation for flattening sets |
1157 | | * Uses a more efficient algorithm with minimal memory usage |
1158 | | */ |
1159 | | |
1160 | | /* Small array for tracking seen object IDs to detect cycles */ |
1161 | 0 | #define MAX_NESTED_DEPTH 16 |
1162 | | |
1163 | | /* |
1164 | | * Recursively flattens a set by merging nested sets into the target set. |
1165 | | * This is an internal helper function that does not call back to the VM. |
1166 | | * |
1167 | | * @param mrb The mruby state |
1168 | | * @param target The target set table to add elements to |
1169 | | * @param source The source set table to flatten |
1170 | | * @param seen_count Pointer to the current count of seen sets (recursion depth) |
1171 | | * @return 0 on success, -1 if recursion depth exceeds maximum |
1172 | | */ |
1173 | | static int |
1174 | | set_flatten_recursive(mrb_state *mrb, kset_t *target, kset_t *source, int *seen_count) |
1175 | 0 | { |
1176 | 0 | if (!source || !target) return 0; |
1177 | 0 | if (*seen_count >= MAX_NESTED_DEPTH) return -1; |
1178 | | |
1179 | 0 | int ai = mrb_gc_arena_save(mrb); |
1180 | | /* Process each element in the source set */ |
1181 | 0 | KSET_FOREACH(source, k) { |
1182 | 0 | mrb_value elem = kset_key(source, k); |
1183 | | |
1184 | | /* Check if element is a Set */ |
1185 | 0 | if (set_is_set(elem)) { |
1186 | | /* Increment recursion depth */ |
1187 | 0 | (*seen_count)++; |
1188 | | |
1189 | | /* Recursively flatten the nested set */ |
1190 | 0 | kset_t *nested_set = set_get_kset(mrb, elem); |
1191 | 0 | if (nested_set) { |
1192 | 0 | int nested_result = set_flatten_recursive(mrb, target, nested_set, seen_count); |
1193 | 0 | if (nested_result < 0) { |
1194 | 0 | return nested_result; /* Propagate error code */ |
1195 | 0 | } |
1196 | 0 | } |
1197 | | |
1198 | | /* Decrement recursion depth */ |
1199 | 0 | (*seen_count)--; |
1200 | 0 | } |
1201 | 0 | else { |
1202 | | /* Add non-Set element directly */ |
1203 | 0 | kset_put(mrb, target, elem); |
1204 | 0 | } |
1205 | 0 | mrb_gc_arena_restore(mrb, ai); |
1206 | 0 | } |
1207 | 0 | return 0; |
1208 | 0 | } |
1209 | | |
1210 | | /* |
1211 | | * Helper function: Check if a set has any nested sets |
1212 | | * Returns TRUE if nested sets found, FALSE otherwise |
1213 | | */ |
1214 | | static mrb_bool |
1215 | | set_has_nested_sets(mrb_state *mrb, kset_t *set) |
1216 | 0 | { |
1217 | 0 | if (kset_is_empty(set)) return FALSE; |
1218 | | |
1219 | 0 | int ai = mrb_gc_arena_save(mrb); |
1220 | 0 | KSET_FOREACH(set, k) { |
1221 | 0 | if (set_is_set(kset_key(set, k))) { |
1222 | 0 | return TRUE; |
1223 | 0 | } |
1224 | 0 | mrb_gc_arena_restore(mrb, ai); |
1225 | 0 | } |
1226 | 0 | return FALSE; |
1227 | 0 | } |
1228 | | |
1229 | | /* |
1230 | | * Helper function: Perform the actual flattening operation |
1231 | | * Returns the flattened set (creates a new kset_t*) |
1232 | | */ |
1233 | | static kset_t* |
1234 | | set_do_flatten(mrb_state *mrb, kset_t *source_set) |
1235 | 0 | { |
1236 | 0 | kset_t *result_set = kset_init(mrb); |
1237 | 0 | int seen_count = 0; |
1238 | |
|
1239 | 0 | if (set_flatten_recursive(mrb, result_set, source_set, &seen_count) < 0) { |
1240 | 0 | kh_destroy(set_val, mrb, result_set); |
1241 | 0 | mrb_raise(mrb, E_ARGUMENT_ERROR, "flatten recursion depth too deep"); |
1242 | 0 | } |
1243 | | |
1244 | 0 | return result_set; |
1245 | 0 | } |
1246 | | |
1247 | | /* |
1248 | | * call-seq: |
1249 | | * set.flatten -> new_set |
1250 | | * |
1251 | | * Returns a new set that is a flattened version of this set. |
1252 | | * Recursively flattens nested sets. |
1253 | | */ |
1254 | | static mrb_value |
1255 | | set_flatten(mrb_state *mrb, mrb_value self) |
1256 | 0 | { |
1257 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
1258 | | |
1259 | | /* Fast path for empty sets */ |
1260 | 0 | if (kset_is_empty(self_set)) { |
1261 | 0 | return mrb_obj_new(mrb, mrb_obj_class(mrb, self), 0, NULL); |
1262 | 0 | } |
1263 | | |
1264 | | /* Fast path: check if there are any nested sets */ |
1265 | 0 | if (!set_has_nested_sets(mrb, self_set)) { |
1266 | 0 | return mrb_obj_dup(mrb, self); |
1267 | 0 | } |
1268 | | |
1269 | | /* Create a new set and flatten into it */ |
1270 | 0 | mrb_value result = mrb_obj_new(mrb, mrb_obj_class(mrb, self), 0, NULL); |
1271 | 0 | kset_t *result_set = set_get_kset(mrb, result); |
1272 | |
|
1273 | 0 | kset_t *flattened = set_do_flatten(mrb, self_set); |
1274 | | |
1275 | | /* Replace result_set's data with flattened data */ |
1276 | 0 | kset_destroy_data(mrb, result_set); |
1277 | 0 | *result_set = *flattened; |
1278 | 0 | mrb_free(mrb, flattened); |
1279 | |
|
1280 | 0 | return result; |
1281 | 0 | } |
1282 | | |
1283 | | /* |
1284 | | * call-seq: |
1285 | | * set.flatten! -> self or nil |
1286 | | * |
1287 | | * Replaces the contents of this set with a flattened version of itself. |
1288 | | * Returns self if flattened, nil if no changes were made. |
1289 | | */ |
1290 | | static mrb_value |
1291 | | set_flatten_bang(mrb_state *mrb, mrb_value self) |
1292 | 0 | { |
1293 | 0 | mrb_check_frozen_value(mrb, self); |
1294 | |
|
1295 | 0 | kset_t *self_set = set_get_kset(mrb, self); |
1296 | 0 | if (kset_is_empty(self_set)) { |
1297 | 0 | return mrb_nil_value(); /* No changes needed for empty set */ |
1298 | 0 | } |
1299 | | |
1300 | | /* Check if there are any nested sets */ |
1301 | 0 | if (!set_has_nested_sets(mrb, self_set)) { |
1302 | 0 | return mrb_nil_value(); /* No nested sets, no changes needed */ |
1303 | 0 | } |
1304 | | |
1305 | | /* Flatten into a new set and replace self */ |
1306 | 0 | kset_t *flattened = set_do_flatten(mrb, self_set); |
1307 | |
|
1308 | 0 | kset_destroy_data(mrb, self_set); |
1309 | 0 | *self_set = *flattened; |
1310 | 0 | mrb_free(mrb, flattened); |
1311 | |
|
1312 | 0 | return self; |
1313 | 0 | } |
1314 | | |
1315 | | /* |
1316 | | * call-seq: |
1317 | | * set.delete_all(*objects) -> self |
1318 | | * |
1319 | | * Deletes multiple objects from the set and returns self. |
1320 | | */ |
1321 | | static mrb_value |
1322 | | set_delete_all(mrb_state *mrb, mrb_value self) |
1323 | 0 | { |
1324 | 0 | const mrb_value *argv; |
1325 | 0 | mrb_int argc; |
1326 | |
|
1327 | 0 | mrb_get_args(mrb, "*", &argv, &argc); |
1328 | 0 | kset_t *ks = set_get_kset(mrb, self); |
1329 | 0 | if (kset_is_uninitialized(ks)) return self; |
1330 | | |
1331 | 0 | int ai = mrb_gc_arena_save(mrb); |
1332 | 0 | for (mrb_int i = 0; i < argc; i++) { |
1333 | 0 | kset_iter_t k = kset_get(mrb, ks, argv[i]); |
1334 | 0 | if (k != kset_end(ks)) { |
1335 | 0 | kset_del(mrb, ks, k); |
1336 | 0 | } |
1337 | 0 | mrb_gc_arena_restore(mrb, ai); |
1338 | 0 | } |
1339 | |
|
1340 | 0 | return self; |
1341 | 0 | } |
1342 | | |
1343 | | /* |
1344 | | * call-seq: |
1345 | | * set.include_all?(*objects) -> true or false |
1346 | | * |
1347 | | * Returns true if the set contains all of the given objects. |
1348 | | */ |
1349 | | static mrb_value |
1350 | | set_include_all_p(mrb_state *mrb, mrb_value self) |
1351 | 0 | { |
1352 | 0 | const mrb_value *argv; |
1353 | 0 | mrb_int argc; |
1354 | |
|
1355 | 0 | mrb_get_args(mrb, "*", &argv, &argc); |
1356 | 0 | kset_t *ks = set_get_kset(mrb, self); |
1357 | 0 | if (kset_is_uninitialized(ks)) return mrb_false_value(); |
1358 | | |
1359 | 0 | for (mrb_int i = 0; i < argc; i++) { |
1360 | 0 | kset_iter_t k = kset_get(mrb, ks, argv[i]); |
1361 | 0 | if (k == kset_end(ks)) { |
1362 | 0 | return mrb_false_value(); |
1363 | 0 | } |
1364 | 0 | } |
1365 | | |
1366 | 0 | return mrb_true_value(); |
1367 | 0 | } |
1368 | | |
1369 | | /* |
1370 | | * call-seq: |
1371 | | * set.include_any?(*objects) -> true or false |
1372 | | * |
1373 | | * Returns true if the set contains any of the given objects. |
1374 | | */ |
1375 | | static mrb_value |
1376 | | set_include_any_p(mrb_state *mrb, mrb_value self) |
1377 | 0 | { |
1378 | 0 | const mrb_value *argv; |
1379 | 0 | mrb_int argc; |
1380 | |
|
1381 | 0 | mrb_get_args(mrb, "*", &argv, &argc); |
1382 | 0 | kset_t *ks = set_get_kset(mrb, self); |
1383 | 0 | if (kset_is_empty(ks)) return mrb_false_value(); |
1384 | | |
1385 | 0 | for (mrb_int i = 0; i < argc; i++) { |
1386 | 0 | kset_iter_t k = kset_get(mrb, ks, argv[i]); |
1387 | 0 | if (k != kset_end(ks)) { |
1388 | 0 | return mrb_true_value(); |
1389 | 0 | } |
1390 | 0 | } |
1391 | | |
1392 | 0 | return mrb_false_value(); |
1393 | 0 | } |
1394 | | |
1395 | | /* |
1396 | | * call-seq: |
1397 | | * Set[*ary] -> new_set |
1398 | | * |
1399 | | * Creates a new set containing the given objects. |
1400 | | */ |
1401 | | static mrb_value |
1402 | | set_s_create(mrb_state *mrb, mrb_value klass) |
1403 | 20 | { |
1404 | 20 | const mrb_value *argv; |
1405 | 20 | mrb_int argc; |
1406 | | |
1407 | 20 | mrb_get_args(mrb, "*", &argv, &argc); |
1408 | | |
1409 | | /* Optimized direct creation */ |
1410 | 20 | mrb_value set = mrb_obj_new(mrb, mrb_class_ptr(klass), 0, NULL); |
1411 | 20 | kset_t *ks = set_get_kset(mrb, set); |
1412 | | |
1413 | 40 | for (mrb_int i = 0; i < argc; i++) { |
1414 | 20 | kset_put(mrb, ks, argv[i]); |
1415 | 20 | } |
1416 | | |
1417 | 20 | return set; |
1418 | 20 | } |
1419 | | |
1420 | | void |
1421 | | mrb_mruby_set_gem_init(mrb_state *mrb) |
1422 | 784 | { |
1423 | 784 | struct RClass *set; |
1424 | | |
1425 | 784 | set = mrb_define_class(mrb, "Set", mrb->object_class); |
1426 | 784 | MRB_SET_INSTANCE_TT(set, MRB_TT_SET); |
1427 | | |
1428 | 784 | mrb_include_module(mrb, set, mrb_module_get(mrb, "Enumerable")); |
1429 | | |
1430 | 784 | mrb_define_class_method(mrb, set, "[]", set_s_create, MRB_ARGS_ANY()); |
1431 | | |
1432 | 784 | mrb_define_private_method(mrb, set, "initialize_copy", set_init_copy, MRB_ARGS_REQ(1)); |
1433 | | |
1434 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(size), set_size, MRB_ARGS_NONE()); |
1435 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(length), set_size, MRB_ARGS_NONE()); |
1436 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(empty), set_empty_p, MRB_ARGS_NONE()); |
1437 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(clear), set_clear, MRB_ARGS_NONE()); |
1438 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(to_a), set_to_a, MRB_ARGS_NONE()); |
1439 | | |
1440 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(include), set_include_p, MRB_ARGS_REQ(1)); |
1441 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(member), set_include_p, MRB_ARGS_REQ(1)); |
1442 | 784 | mrb_define_method_id(mrb, set, MRB_OPSYM(eqq), set_include_p, MRB_ARGS_REQ(1)); |
1443 | | |
1444 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(add), set_add, MRB_ARGS_REQ(1)); |
1445 | 784 | mrb_define_method_id(mrb, set, MRB_OPSYM(lshift), set_add, MRB_ARGS_REQ(1)); |
1446 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(add), set_add_p, MRB_ARGS_REQ(1)); |
1447 | | |
1448 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(delete), set_delete, MRB_ARGS_REQ(1)); |
1449 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(delete), set_delete_p, MRB_ARGS_REQ(1)); |
1450 | | |
1451 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(__init), set_init, MRB_ARGS_NONE()); |
1452 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(__merge), set_core_merge, MRB_ARGS_REQ(1)); |
1453 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(__subtract), set_core_subtract, MRB_ARGS_REQ(1)); |
1454 | | |
1455 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(__union), set_core_union, MRB_ARGS_REQ(1)); |
1456 | | |
1457 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(__difference), set_core_difference, MRB_ARGS_REQ(1)); |
1458 | | |
1459 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(__intersection), set_core_intersection, MRB_ARGS_REQ(1)); |
1460 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(__xor), set_core_xor, MRB_ARGS_REQ(1)); |
1461 | | |
1462 | 784 | mrb_define_method_id(mrb, set, MRB_OPSYM(eq), set_equal, MRB_ARGS_REQ(1)); |
1463 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(hash), set_hash_m, MRB_ARGS_NONE()); |
1464 | 784 | mrb_define_alias(mrb, set, "eql?", "=="); |
1465 | | |
1466 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(join), set_join, MRB_ARGS_OPT(1)); |
1467 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(inspect), set_inspect, MRB_ARGS_NONE()); |
1468 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(to_s), set_inspect, MRB_ARGS_NONE()); |
1469 | | |
1470 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(reset), set_reset, MRB_ARGS_NONE()); |
1471 | | |
1472 | | /* Bulk operation methods */ |
1473 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(add_all), set_add_all, MRB_ARGS_ANY()); |
1474 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(delete_all), set_delete_all, MRB_ARGS_ANY()); |
1475 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(include_all), set_include_all_p, MRB_ARGS_ANY()); |
1476 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(include_any), set_include_any_p, MRB_ARGS_ANY()); |
1477 | | |
1478 | | /* Register our new C implementations */ |
1479 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(superset), set_superset_p, MRB_ARGS_REQ(1)); |
1480 | 784 | mrb_define_method_id(mrb, set, MRB_OPSYM(ge), set_superset_p, MRB_ARGS_REQ(1)); |
1481 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(proper_superset), set_proper_superset_p, MRB_ARGS_REQ(1)); |
1482 | 784 | mrb_define_method_id(mrb, set, MRB_OPSYM(gt), set_proper_superset_p, MRB_ARGS_REQ(1)); |
1483 | | |
1484 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(subset), set_subset_p, MRB_ARGS_REQ(1)); |
1485 | 784 | mrb_define_method_id(mrb, set, MRB_OPSYM(le), set_subset_p, MRB_ARGS_REQ(1)); |
1486 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(proper_subset), set_proper_subset_p, MRB_ARGS_REQ(1)); |
1487 | 784 | mrb_define_method_id(mrb, set, MRB_OPSYM(lt), set_proper_subset_p, MRB_ARGS_REQ(1)); |
1488 | | |
1489 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(intersect), set_intersect_p, MRB_ARGS_REQ(1)); |
1490 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_Q(disjoint), set_disjoint_p, MRB_ARGS_REQ(1)); |
1491 | | |
1492 | 784 | mrb_define_method_id(mrb, set, MRB_OPSYM(cmp), set_cmp, MRB_ARGS_REQ(1)); |
1493 | | |
1494 | 784 | mrb_define_method_id(mrb, set, MRB_SYM(flatten), set_flatten, MRB_ARGS_NONE()); |
1495 | 784 | mrb_define_method_id(mrb, set, MRB_SYM_B(flatten), set_flatten_bang, MRB_ARGS_NONE()); |
1496 | 784 | } |
1497 | | |
1498 | | void |
1499 | | mrb_mruby_set_gem_final(mrb_state *mrb) |
1500 | 784 | { |
1501 | 784 | } |