File Coverage

TreeRBXS.xs
Criterion Covered Total %
statement 1607 1935 83.0
branch 1098 1764 62.2
condition n/a
subroutine n/a
pod n/a
total 2705 3699 73.1


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4             #define NEED_mg_findext
5             #define NEED_ASSUME
6             #include "ppport.h"
7              
8             /* The core Red/Black algorithm which operates on rbtree_node_t */
9             #include "rbtree.h"
10              
11             struct TreeRBXS;
12             struct TreeRBXS_item;
13              
14             #define AUTOCREATE 1
15             #define OR_DIE 2
16              
17             /* These get serialized as bytes for Storable, so should not change,
18             * or else STORABLE_thaw needs adapted.
19             */
20             #define KEY_TYPE_ANY 1
21             #define KEY_TYPE_CLAIM 2
22             #define KEY_TYPE_INT 3
23             #define KEY_TYPE_FLOAT 4
24             #define KEY_TYPE_BSTR 5
25             #define KEY_TYPE_USTR 6
26             #define KEY_TYPE_MAX 6
27              
28             /* I am only using foldEQ for parsing user parameters, not for the sort functions,
29             * so this should be fine for Perl < 5.14 */
30             #ifndef foldEQ
31             static bool shim_foldEQ(const char *s1, const char *s2, int len) {
32             for (--len; len >= 0; --len)
33             if (toLOWER(s1[len]) != toLOWER(s2[len]))
34             return 0;
35             return 1;
36             }
37             #define foldEQ shim_foldEQ
38             #endif
39              
40 23           static bool looks_like_integer(SV *sv) {
41 23 50         if (!sv) return false;
42 23 50         if (SvMAGICAL(sv))
43 0           mg_get(sv);
44 23 50         if (SvIOK(sv) || SvUOK(sv))
    0          
45 23           return true;
46 0 0         if (SvNOK(sv) && (SvNV(sv) == (NV)(IV)SvNV(sv)))
    0          
47 0           return true;
48 0 0         if (SvPOK(sv)) {
49             STRLEN len;
50 0           const char *str= SvPV(sv, len);
51 0 0         if (len == 0 || !((str[0] >= '0' && str[0] <= '9') || str[0] == '-' || str[0] == '+'))
    0          
    0          
    0          
    0          
52 0           return false;
53 0 0         while (--len > 0) {
54 0 0         if (str[len] < '0' || str[len] > '9')
    0          
55 0           return false;
56             }
57 0           return true;
58             }
59 0           return false;
60             }
61              
62 24           static int parse_key_type(SV *type_sv) {
63             const char *str;
64 24           IV key_type= -1;
65 24 100         if (SvIOK(type_sv)) {
66 16           key_type= SvIV(type_sv);
67 16 50         if (key_type < 1 || key_type > KEY_TYPE_MAX)
    50          
68 0           key_type= -1;
69             }
70 8 50         else if (SvPOK(type_sv)) {
71             STRLEN len;
72 8           str= SvPV(type_sv, len);
73 8 100         if (len > 9 && foldEQ(str, "KEY_TYPE_", 9)) {
    50          
74 2           str += 9;
75 2           len -= 9;
76             }
77 15 50         key_type= (len == 3 && foldEQ(str, "ANY", 3))? KEY_TYPE_ANY
78 23 100         : (len == 5 && foldEQ(str, "CLAIM", 5))? KEY_TYPE_CLAIM
    50          
    0          
79 8 100         : (len == 3 && foldEQ(str, "INT", 3))? KEY_TYPE_INT
    50          
80 1 50         : (len == 5 && foldEQ(str, "FLOAT", 5))? KEY_TYPE_FLOAT
    0          
81 1 50         : (len == 4 && foldEQ(str, "BSTR", 4))? KEY_TYPE_BSTR
    50          
82 0 0         : (len == 4 && foldEQ(str, "USTR", 4))? KEY_TYPE_USTR
    0          
83             : -1;
84             }
85 24           return (int) key_type;
86             }
87              
88 20           static const char *get_key_type_name(int key_type) {
89 20           switch (key_type) {
90 5           case KEY_TYPE_ANY: return "KEY_TYPE_ANY";
91 0           case KEY_TYPE_CLAIM: return "KEY_TYPE_CLAIM";
92 2           case KEY_TYPE_INT: return "KEY_TYPE_INT";
93 2           case KEY_TYPE_FLOAT: return "KEY_TYPE_FLOAT";
94 4           case KEY_TYPE_BSTR: return "KEY_TYPE_BSTR";
95 7           case KEY_TYPE_USTR: return "KEY_TYPE_USTR";
96 0           default: return NULL;
97             }
98             }
99              
100             typedef int TreeRBXS_cmp_fn(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b);
101             static TreeRBXS_cmp_fn TreeRBXS_cmp_int;
102             static TreeRBXS_cmp_fn TreeRBXS_cmp_float;
103             static TreeRBXS_cmp_fn TreeRBXS_cmp_memcmp;
104             //static TreeRBXS_cmp_fn TreeRBXS_cmp_utf8;
105             static TreeRBXS_cmp_fn TreeRBXS_cmp_numsplit;
106             static TreeRBXS_cmp_fn TreeRBXS_cmp_perl;
107             static TreeRBXS_cmp_fn TreeRBXS_cmp_perl_cb;
108              
109             typedef SV* TreeRBXS_xform_fn(struct TreeRBXS *tree, SV *orig_key);
110             static TreeRBXS_xform_fn TreeRBXS_xform_fc;
111              
112             /* These get serialized as bytes for Storable, so should not change,
113             * or else STORABLE_thaw needs adapted.
114             */
115             #define CMP_PERL 1
116             #define CMP_INT 2
117             #define CMP_FLOAT 3
118             #define CMP_MEMCMP 4
119             #define CMP_STR 5
120             #define CMP_SUB 6
121             #define CMP_NUMSPLIT 7
122             #define CMP_FOLDCASE 8
123             #define CMP_NUMSPLIT_FOLDCASE 9
124             #define CMP_MAX 9
125              
126 56           static int parse_cmp_fn(SV *cmp_sv) {
127 56           int cmp_id= -1;
128 56 100         if (SvROK(cmp_sv) && SvTYPE(SvRV(cmp_sv)) == SVt_PVCV)
    50          
129 8           cmp_id= CMP_SUB;
130 48 100         else if (SvIOK(cmp_sv)) {
131 28           IV i= SvIV(cmp_sv);
132 28 50         cmp_id= (i < 1 || i > CMP_MAX || i == CMP_SUB)? -1 : (int) i;
    50          
    50          
133             }
134 20 50         else if (SvPOK(cmp_sv)) {
135             STRLEN len;
136 20           const char *str= SvPV(cmp_sv, len);
137 20 100         if (len > 4 && foldEQ(str, "CMP_", 4)) {
    100          
138 1           str += 4;
139 1           len -= 4;
140             }
141 30 100         cmp_id= (len == 3 && foldEQ(str, "INT", 3))? CMP_INT
142 41 100         : (len == 3 && foldEQ(str, "STR", 3))? CMP_STR
    50          
143 22 100         : (len == 4 && foldEQ(str, "UTF8", 4))? CMP_STR // back-compat name
    0          
144 20 50         : (len == 4 && foldEQ(str, "PERL", 4))? CMP_PERL
    0          
145 21 50         : (len == 5 && foldEQ(str, "FLOAT", 5))? CMP_FLOAT
    50          
146 20 100         : (len == 6 && foldEQ(str, "MEMCMP", 6))? CMP_MEMCMP
    0          
147 25 50         : (len == 8 && foldEQ(str, "FOLDCASE", 8))? CMP_FOLDCASE
    100          
148 20 100         : (len == 8 && foldEQ(str, "NUMSPLIT", 8))? CMP_NUMSPLIT
    50          
149 10 100         : (len ==17 && foldEQ(str, "NUMSPLIT_FOLDCASE", 17))? CMP_NUMSPLIT_FOLDCASE
    50          
150             //: (len == 7 && foldEQ(str, "SUB", 3))? CMP_SUB can only be requested by a CV*
151 4 50         : -1;
152             }
153 56           return cmp_id;
154             }
155              
156 16           static const char * get_cmp_name(int cmp_id) {
157 16           switch (cmp_id) {
158 3           case CMP_PERL: return "CMP_PERL";
159 3           case CMP_INT: return "CMP_INT";
160 1           case CMP_FLOAT: return "CMP_FLOAT";
161 1           case CMP_STR: return "CMP_STR";
162 0           case CMP_SUB: return "CMP_SUB";
163 1           case CMP_MEMCMP: return "CMP_MEMCMP";
164 3           case CMP_NUMSPLIT: return "CMP_NUMSPLIT";
165 1           case CMP_FOLDCASE: return "CMP_FOLDCASE";
166 3           case CMP_NUMSPLIT_FOLDCASE: return "CMP_NUMSPLIT_FOLDCASE";
167 0           default: return NULL;
168             }
169             }
170              
171             #define GET_EQ 0
172             #define GET_GE 1
173             #define GET_LE 2
174             #define GET_GT 3
175             #define GET_LT 4
176             #define GET_NEXT 5
177             #define GET_PREV 6
178             #define GET_EQ_LAST 7
179             #define GET_LE_LAST 8
180             #define GET_OR_ADD 9
181             #define GET_MAX 9
182              
183 61           static int parse_lookup_mode(SV *mode_sv) {
184 61           int mode= -1;
185 61 50         if (SvIOK(mode_sv)) {
186 61           IV i= SvIV(mode_sv);
187 61 50         mode= (i < 0 || i > GET_MAX)? -1 : (int) i;
    50          
188 0 0         } else if (SvPOK(mode_sv)) {
189             STRLEN len;
190 0           char* mode_str= SvPV(mode_sv, len);
191 0 0         if (len > 4 && foldEQ(mode_str, "GET_", 4)) {
    0          
192 0           mode_str+= 4;
193 0           len -= 4;
194             }
195             // Allow alternate syntax of "==" etc, 'eq' etc, or any of the official constant names
196 0           switch (mode_str[0]) {
197 0 0         case '<': mode= len == 1? GET_LT : len == 2 && mode_str[1] == '='? GET_LE : -1; break;
    0          
    0          
198 0 0         case '>': mode= len == 1? GET_GT : len == 2 && mode_str[1] == '='? GET_GE : -1; break;
    0          
    0          
199 0 0         case '=': mode= len == 2 && mode_str[1] == '='? GET_EQ : -1; break;
    0          
200 0 0         case '-': mode= len == 2 && mode_str[1] == '-'? GET_PREV : -1; break;
    0          
201 0 0         case '+': mode= len == 2 && mode_str[1] == '+'? GET_NEXT : -1; break;
    0          
202 0           case 'E': case 'e':
203 0 0         mode= len == 2 && (mode_str[1] == 'q' || mode_str[1] == 'Q')? GET_EQ
    0          
204 0 0         : len == 7 && foldEQ(mode_str, "EQ_LAST", 7)? GET_EQ_LAST
    0          
205 0 0         : -1;
206 0           break;
207 0           case 'G': case 'g':
208 0 0         mode= len == 2 && (mode_str[1] == 't' || mode_str[1] == 'T')? GET_GT
    0          
209 0 0         : len == 2 && (mode_str[1] == 'e' || mode_str[1] == 'E')? GET_GE
    0          
    0          
210 0 0         : -1;
211 0           break;
212 0           case 'L': case 'l':
213 0 0         mode= len == 2 && (mode_str[1] == 't' || mode_str[1] == 'T')? GET_LT
    0          
214 0 0         : len == 2 && (mode_str[1] == 'e' || mode_str[1] == 'E')? GET_LE
    0          
    0          
215 0 0         : len == 7 && foldEQ(mode_str, "LE_LAST", 7)? GET_LE_LAST
    0          
216 0 0         : -1;
217 0           break;
218 0 0         case 'P': case 'p': mode= foldEQ(mode_str, "PREV", 4)? GET_PREV : -1; break;
219 0 0         case 'N': case 'n': mode= foldEQ(mode_str, "NEXT", 4)? GET_NEXT : -1; break;
220 0 0         case 'o': case 'O': mode= foldEQ(mode_str, "OR_ADD", 6)? GET_OR_ADD : -1; break;
221             }
222             }
223 61           return mode;
224             }
225              
226 0           static SV* make_aligned_buffer(SV *sv, size_t size, int align) {
227             char *p;
228             STRLEN len;
229              
230 0 0         if (!sv)
231 0           sv= newSVpvn("", 0);
232 0 0         else if (!SvPOK(sv))
233 0           sv_setpvs(sv, "");
234 0           p= SvPV_force(sv, len);
235 0 0         if (len < size || ((intptr_t)p) & (align-1)) {
    0          
236 0 0         SvGROW(sv, size+align-1);
    0          
237 0           SvCUR_set(sv, size);
238 0           p= SvPVX(sv);
239 0 0         if ((intptr_t)p & (align-1)) {
240 0           sv_chop(sv, p + align - ((intptr_t)p & (align-1)));
241 0           SvCUR_set(sv, size);
242             }
243             }
244 0           return sv;
245             }
246              
247             struct dllist_node {
248             struct dllist_node *prev, *next;
249             };
250              
251             #define INSERT_TREND_TRIGGER 3
252             #define INSERT_TREND_CAP 5
253              
254             // Struct attached to each instance of Tree::RB::XS
255             struct TreeRBXS {
256             SV *owner; // points to Tree::RB::XS internal HV (not ref)
257             TreeRBXS_cmp_fn *compare; // internal compare function. Always set and never changed.
258             TreeRBXS_xform_fn *transform; // internal key-transformation function, applied before key comparisons.
259             SV *compare_callback; // user-supplied compare. May be NULL, but can never be changed.
260             int key_type; // must always be set and never changed
261             int compare_fn_id; // indicates which compare is in use, for debugging
262             bool allow_duplicates; // flag to affect behavior of insert. may be changed.
263             bool allowed_duplicates; // was a duplicate ever inserted? helps optimize put()
264             bool compat_list_get; // flag to enable full compat with Tree::RB's list context behavior
265             bool track_recent; // flag to automatically add new nodes to the recent-list
266             bool lookup_updates_recent; // whether 'lookup' and 'get' move a node to the front of the recent-list
267             bool keys_in_recent_order; // whether the 'keys' method iterates recent order instead of sort order
268             rbtree_node_t root_sentinel; // parent-of-root, used by rbtree implementation.
269             rbtree_node_t leaf_sentinel; // dummy node used by rbtree implementation.
270             struct TreeRBXS_iter *hashiter;// iterator used for TIEHASH
271             struct dllist_node recent; // insertion order tracking
272             size_t recent_count; // number of nodes being LRU-tracked
273             IV recent_limit; // maximum allowed recent-tracked nodes
274             bool hashiterset; // true if the hashiter has been set manually with hseek
275             struct TreeRBXS_item
276             *prev_inserted_item; // optimize adjacent inserts by tracking previous insert
277             int prev_inserted_trend; // number of consecutive adjacent inserts
278             bool prev_inserted_dup; // whether previous insert was an allow_duplicate case
279             };
280              
281             static void TreeRBXS_init(struct TreeRBXS *tree, SV *owner);
282             static void TreeRBXS_clear(struct TreeRBXS *tree);
283             static void TreeRBXS_assert_structure(struct TreeRBXS *tree);
284             struct TreeRBXS_iter * TreeRBXS_get_hashiter(struct TreeRBXS *tree);
285             static struct TreeRBXS_item *TreeRBXS_find_item(struct TreeRBXS *tree, struct TreeRBXS_item *key, int mode);
286             static bool TreeRBXS_is_member(struct TreeRBXS *tree, struct TreeRBXS_item *item);
287             static void TreeRBXS_recent_insert_before(struct TreeRBXS *tree, struct TreeRBXS_item *item, struct dllist_node *node_after);
288             static void TreeRBXS_recent_prune(struct TreeRBXS *tree, struct TreeRBXS_item *item);
289             static void TreeRBXS_truncate_recent(struct TreeRBXS *tree, IV lim, SV **nodes_out);
290             static void TreeRBXS_destroy(struct TreeRBXS *tree);
291              
292             #define TreeRBXS_get_root(tree) ((tree)->root_sentinel.left)
293             #define TreeRBXS_get_count(tree) ((tree)->root_sentinel.left->count)
294              
295             #define OFS_TreeRBXS_FIELD_root_sentinel ( ((char*) &(((struct TreeRBXS*)(void*)10000)->root_sentinel)) - ((char*)10000) )
296             #define GET_TreeRBXS_FROM_root_sentinel(node) ((struct TreeRBXS*) (((char*)node) - OFS_TreeRBXS_FIELD_root_sentinel))
297              
298             #define OFS_TreeRBXS_item_FIELD_rbnode ( ((char*) &(((struct TreeRBXS_item *)(void*)10000)->rbnode)) - ((char*)10000) )
299             #define GET_TreeRBXS_item_FROM_rbnode(node) ((struct TreeRBXS_item*) (((char*)node) - OFS_TreeRBXS_item_FIELD_rbnode))
300              
301             #define OFS_TreeRBXS_item_FIELD_recent ( ((char*) &(((struct TreeRBXS_item *)(void*)10000)->recent)) - ((char*)10000) )
302             #define GET_TreeRBXS_item_FROM_recent(node) ((struct TreeRBXS_item*) (((char*)node) - OFS_TreeRBXS_item_FIELD_recent))
303              
304             // Struct attached to each instance of Tree::RB::XS::Node
305             // I named it 'item' instead of 'node' to prevent confusion with the actual
306             // rbtree_node_t used by the underlying library.
307             struct TreeRBXS_item {
308             rbtree_node_t rbnode; // actual red/black left/right/color/parent/count fields
309             SV *owner; // points to Tree::RB::XS::Node internal SV (not ref), or NULL if not wrapped
310             union itemkey_u { // key variations are overlapped to save space
311             IV ikey;
312             NV nkey;
313             const char *ckey;
314             SV *svkey;
315             } keyunion;
316             struct TreeRBXS_iter *iter; // linked list of iterators who reference this item
317             struct dllist_node recent; // doubly linked list of insertion order tracking
318             SV *value; // value will be set unless struct is just used as a search key
319             size_t key_type: 3,
320             orig_key_stored: 1,
321             #if SIZE_MAX == 0xFFFFFFFF
322             #define CKEYLEN_MAX ((((size_t)1)<<28)-1)
323             ckeylen: 28;
324             #else
325             #define CKEYLEN_MAX ((((size_t)1)<<60)-1)
326             ckeylen: 60;
327             #endif
328             char extra[];
329             };
330              
331 20213           static SV* GET_TreeRBXS_item_ORIG_KEY(struct TreeRBXS_item *item) {
332 20213           SV *k= NULL;
333 20213 50         if (item->orig_key_stored)
334 20213           memcpy(&k, item->extra, sizeof(SV*));
335 20213           return k;
336             }
337             #define SET_TreeRBXS_item_ORIG_KEY(item, key) memcpy(item->extra, &key, sizeof(SV*))
338             #define GET_TreeRBXS_stack_item_ORIG_KEY(item) ((item)->owner)
339             #define SET_TreeRBXS_stack_item_ORIG_KEY(item, key) ((item)->owner= (key))
340             #define IS_ITEM_KEY_STORED_IN_EXTRA(item) ((item)->key_type == KEY_TYPE_USTR || (item)->key_type == KEY_TYPE_BSTR)
341             #define ITEM_EXTRA_BYTES_NEEDED(stack_item) ( \
342             ((stack_item)->orig_key_stored ? sizeof(SV*) : 0) \
343             +(IS_ITEM_KEY_STORED_IN_EXTRA(stack_item)? (stack_item)->ckeylen : 0) )
344              
345             static void TreeRBXS_init_tmp_item(struct TreeRBXS_item *item, struct TreeRBXS *tree, SV *key, SV *value);
346             static struct TreeRBXS_item * TreeRBXS_new_item_from_tmp_item(struct TreeRBXS_item *src);
347             static struct TreeRBXS_item * TreeRBXS_item_realloc_extra(struct TreeRBXS_item *item, size_t new_extra);
348             static void TreeRBXS_item_set_key(struct TreeRBXS_item **item_p, struct TreeRBXS_item *new_key);
349             static struct TreeRBXS* TreeRBXS_item_get_tree(struct TreeRBXS_item *item);
350             static void TreeRBXS_item_advance_all_iters(struct TreeRBXS_item* item, int flags);
351             static void TreeRBXS_item_detach_iter(struct TreeRBXS_item *item, struct TreeRBXS_iter *iter);
352             static void TreeRBXS_item_detach_owner(struct TreeRBXS_item* item);
353             static void TreeRBXS_item_detach_tree(struct TreeRBXS_item* item, struct TreeRBXS *tree);
354             static void TreeRBXS_item_clear(struct TreeRBXS_item* item, struct TreeRBXS *tree);
355             static void TreeRBXS_item_free(struct TreeRBXS_item *item);
356             static SV* TreeRBXS_wrap_item(struct TreeRBXS_item *item);
357              
358             struct TreeRBXS_iter {
359             struct TreeRBXS *tree;
360             SV *owner;
361             struct TreeRBXS_iter *next_iter;
362             struct TreeRBXS_item *item;
363             int reverse: 1, recent: 1;
364             };
365              
366             static void TreeRBXS_iter_rewind(struct TreeRBXS_iter *iter);
367             static void TreeRBXS_iter_set_item(struct TreeRBXS_iter *iter, struct TreeRBXS_item *item);
368             static void TreeRBXS_iter_advance(struct TreeRBXS_iter *iter, IV ofs);
369             static void TreeRBXS_iter_free(struct TreeRBXS_iter *iter);
370              
371             /* Definitions for XS MAGIC */
372             static int TreeRBXS_magic_free(pTHX_ SV* sv, MAGIC* mg);
373             static int TreeRBXS_item_magic_free(pTHX_ SV* sv, MAGIC* mg);
374             static int TreeRBXS_iter_magic_free(pTHX_ SV* sv, MAGIC *mg);
375              
376             #ifdef USE_ITHREADS
377             static int TreeRBXS_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param);
378             #else
379             #define TreeRBXS_magic_dup 0
380             #endif
381              
382             #ifdef MGf_LOCAL
383             #define MAGIC_LOCAL_NULL ,0
384             #else
385             #define MAGIC_LOCAL_NULL
386             #endif
387              
388             // magic table for Tree::RB::XS
389             static MGVTBL TreeRBXS_magic_vt= {
390             0, 0, 0, 0, TreeRBXS_magic_free, 0, TreeRBXS_magic_dup MAGIC_LOCAL_NULL
391             };
392              
393             // magic table for Tree::RB::XS::Node
394             static MGVTBL TreeRBXS_item_magic_vt= {
395             0, 0, 0, 0, TreeRBXS_item_magic_free, 0, TreeRBXS_magic_dup MAGIC_LOCAL_NULL
396             };
397              
398             static MGVTBL TreeRBXS_iter_magic_vt= {
399             0, 0, 0, 0, TreeRBXS_iter_magic_free, 0, TreeRBXS_magic_dup MAGIC_LOCAL_NULL
400             };
401              
402              
403 111           static void TreeRBXS_init(struct TreeRBXS *tree, SV *owner) {
404 111           memset(tree, 0, sizeof(struct TreeRBXS));
405 111           tree->owner= owner;
406 111           rbtree_init_tree(&tree->root_sentinel, &tree->leaf_sentinel);
407 111           tree->recent.next= &tree->recent;
408 111           tree->recent.prev= &tree->recent;
409             /* defaults, which can be overridden by _init_tree */
410 111           tree->key_type= KEY_TYPE_ANY;
411 111           tree->compare_fn_id= CMP_PERL;
412 111           tree->recent_limit= -1;
413 111           }
414              
415 3           static void TreeRBXS_clear(struct TreeRBXS *tree) {
416 3           tree->prev_inserted_item= NULL;
417 3           tree->prev_inserted_trend= 0;
418 3           tree->prev_inserted_dup= false;
419 3           tree->allowed_duplicates= false;
420 3           rbtree_clear(&tree->root_sentinel, (void (*)(void *, void *)) &TreeRBXS_item_clear,
421             -OFS_TreeRBXS_item_FIELD_rbnode, tree);
422 3           tree->recent.prev= &tree->recent;
423 3           tree->recent.next= &tree->recent;
424 3           tree->recent_count= 0;
425 3           }
426              
427 19           static void TreeRBXS_assert_structure(struct TreeRBXS *tree) {
428             int err;
429             rbtree_node_t *node;
430             struct TreeRBXS_item *item;
431             struct TreeRBXS_iter *iter;
432              
433 19 50         if (!tree) croak("tree is NULL");
434 19 50         if (!tree->owner) croak("no owner");
435 19 50         if (tree->key_type < 0 || tree->key_type > KEY_TYPE_MAX) croak("bad key_type");
    50          
436 19 50         if (!tree->compare) croak("no compare function");
437 19 50         if ((err= rbtree_check_structure(&tree->root_sentinel, (int(*)(void*,void*,void*)) tree->compare, tree, -OFS_TreeRBXS_item_FIELD_rbnode)))
438 0           croak("tree structure damaged: %d", err);
439 19 50         if (TreeRBXS_get_count(tree)) {
440 19           node= rbtree_node_left_leaf(tree->root_sentinel.left);
441 500029 100         while (node) {
442 500010           item= GET_TreeRBXS_item_FROM_rbnode(node);
443 500010 50         if (item->key_type != tree->key_type)
444 0           croak("node key_type doesn't match tree");
445 500010 50         if (!item->value)
446 0           croak("node value SV lost");
447 500010 50         if (item->iter) {
448 0           iter= item->iter;
449 0 0         while (iter) {
450 0 0         if (!iter->owner) croak("Iterator lacks owner reference");
451 0 0         if (iter->item != item) croak("Iterator referenced by wrong item");
452 0           iter= iter->next_iter;
453             }
454             }
455 500010           node= rbtree_node_next(node);
456             }
457             }
458 19 50         if (!tree->recent_count) {
459 19 50         if (tree->recent.prev != &tree->recent || tree->recent.next != &tree->recent)
    50          
460 0           croak("recent_count = 0, but list contains nodes");
461             } else {
462 0 0         if (tree->recent.prev == &tree->recent || tree->recent.next == &tree->recent)
    0          
463 0           croak("recent_count > 0, but list is empty");
464 0 0         if (tree->recent_limit >= 0 && tree->recent_count > tree->recent_limit)
    0          
465 0           croak("recent_count > recent_limit");
466             }
467             //warn("Tree is healthy");
468 19           }
469              
470 79           struct TreeRBXS_iter * TreeRBXS_get_hashiter(struct TreeRBXS *tree) {
471             // This iterator is owned by the tree. All other iterators would hold a reference to the tree.
472 79 100         if (!tree->hashiter) {
473 5           Newxz(tree->hashiter, 1, struct TreeRBXS_iter);
474 5           tree->hashiter->tree= tree;
475 5           tree->hashiter->recent= tree->keys_in_recent_order;
476             }
477 79           return tree->hashiter;
478             }
479              
480             /* For insert/put, there needs to be a node created before it can be
481             * inserted. But if the insert fails, the item needs cleaned up.
482             * This initializes a temporary incomplete item on the stack that can be
483             * used for searching without the expense of allocating buffers etc.
484             * The temporary item does not require any destructor/cleanup.
485             * (and, the destructor *must not* be called on the stack item)
486             */
487 501470           static void TreeRBXS_init_tmp_item(struct TreeRBXS_item *item, struct TreeRBXS *tree, SV *key, SV *value) {
488 501470           STRLEN len= 0;
489              
490             // all fields should start NULL just to be safe
491 501470           memset(item, 0, sizeof(*item));
492             // copy key type from tree
493 501470           item->key_type= tree->key_type;
494             // If there's a transform function, apply that first
495 501470 100         if (tree->transform) {
496 20079           SET_TreeRBXS_stack_item_ORIG_KEY(item, key); // temporary storage for original key
497 20079           item->orig_key_stored= 1;
498 20079           key= tree->transform(tree, key); // returns a mortal SV
499             }
500 481391           else item->orig_key_stored= 0;
501              
502             // set up the keys.
503 501470           item->ckeylen= 0;
504 501470           switch (item->key_type) {
505 140948           case KEY_TYPE_ANY:
506 140948           case KEY_TYPE_CLAIM: item->keyunion.svkey= key; break;
507 110231           case KEY_TYPE_INT: item->keyunion.ikey= SvIV(key); break;
508 110060           case KEY_TYPE_FLOAT: item->keyunion.nkey= SvNV(key); break;
509             // STR and BSTR assume that the 'key' SV has a longer lifespan than the use of the tmp item,
510             // and directly reference the PV pointer. The insert and search algorithms should not be
511             // calling into Perl for their entire execution.
512 30154           case KEY_TYPE_USTR:
513 30154           item->keyunion.ckey= SvPVutf8(key, len);
514             if (0)
515             case KEY_TYPE_BSTR:
516 110077           item->keyunion.ckey= SvPVbyte(key, len);
517             // the ckeylen is a bit field, so can't go the full range of int
518 140231 50         if (len > CKEYLEN_MAX)
519 0           croak("String length %ld exceeds maximum %ld for optimized key_type", (long)len, (long)CKEYLEN_MAX);
520 140231           item->ckeylen= len;
521 140231           break;
522 0           default:
523 0           croak("BUG: un-handled key_type");
524             }
525 501470           item->value= value;
526 501470           }
527              
528             /* Alter the key of 'item' to be the same as the key of 'new_value', possibly
529             * reallocating item and possibly moving it to a new location in the tree.
530             * The item pointer might change if the memory got reallocated.
531             * The 'new_value' must be a "stack item" whch has not actually been allocated.
532             */
533 47           void TreeRBXS_item_set_key(struct TreeRBXS_item **item_p, struct TreeRBXS_item *new_value) {
534 47           struct TreeRBXS_item *item= *item_p;
535 47           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
536 47           int cmp= tree->compare(tree, new_value, item);
537 47 100         if (cmp != 0) {
538 44 100         rbtree_node_t *node= cmp < 0? rbtree_node_prev(&item->rbnode) : rbtree_node_next(&item->rbnode);
539 44           struct TreeRBXS_item *neighbor= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
540 44 50         int neighbor_cmp= neighbor? tree->compare(tree, new_value, neighbor) : 0;
541 44 100         IV new_extra= ITEM_EXTRA_BYTES_NEEDED(new_value);
    50          
542 44 100         if (IS_ITEM_KEY_STORED_IN_EXTRA(item)) {
    50          
543 30 50         IV cur_extra= ITEM_EXTRA_BYTES_NEEDED(item);
    0          
544 30 100         if (new_extra > cur_extra) {
545 2           item= TreeRBXS_item_realloc_extra(item, new_extra);
546 2           *item_p= item; /* pass back to caller as well */
547             }
548             /* it is now safe to memcpy() the key from new_value to item */
549             }
550             /* If the new key compares the same direction to the neighbor as it compared
551             to the old key, then the node needs to move across the neighbor and we need
552             to prune/insert to get it there. We also need to run this code if the key
553             is equal to the neighbor and duplicates are not allowed. */
554 44 100         if ((cmp > 0 && neighbor_cmp > 0) || (cmp < 0 && neighbor_cmp < 0)
    100          
    100          
    100          
555 38 100         || (neighbor_cmp == 0 && !tree->allow_duplicates)
    50          
556             ) {
557             rbtree_node_t * search[2];
558             bool found_identical;
559             /* Advance any iterators pointing at this node */
560 39 50         if (item->iter)
561 0           TreeRBXS_item_advance_all_iters(item, 0);
562             /* cancel the insert optimization if it pointed to this node */
563 39 50         if (tree->prev_inserted_item == item) {
564 0           tree->prev_inserted_item= NULL;
565 0           tree->prev_inserted_trend= 0;
566             }
567             /* This can't re-use the standard insertion code for nodes because that makes
568             * assumptions that the tree is changing size, where in this case the size
569             * remains the same. Also this code intentionally doesn't modify the 'recent'
570             * order.
571             *
572             * In the spirit of preserving order, make sure this element inserts closest
573             * to the boundary_item of any duplicates, so use rbtree_find_all to get the
574             * leftmost/rightmost match, or else the nearest node to insert under.
575             *
576             * Also, wait until the last minute to perform the prune operation and key
577             * value replacement so that if there is a perl exception in a compare function
578             * we don't leak the node or corrupt the tree.
579             */
580 39           found_identical= rbtree_find_all(
581             &tree->root_sentinel,
582             new_value,
583 39           (int(*)(void*,void*,void*)) tree->compare,
584             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
585             &search[0], &search[1], NULL);
586             /* remove */
587 39           rbtree_node_prune(&item->rbnode);
588             /* re-insert */
589 39 100         if (found_identical) {
590             /* insert-before leftmost, or insert-after rightmost */
591 36 100         if (cmp > 0)
592 30           rbtree_node_insert_before(search[0], &item->rbnode);
593             else
594 6           rbtree_node_insert_after(search[1], &item->rbnode);
595             /* remove conflicting nodes, if not permitted */
596 36 50         if (!tree->allow_duplicates) {
597 36           rbtree_node_t *rm_node= search[0];
598             do {
599 36           struct TreeRBXS_item *rm_item= GET_TreeRBXS_item_FROM_rbnode(rm_node);
600 36 50         rm_node= (rm_node == search[1])? NULL : rbtree_node_next(rm_node);
601 36 50         ASSUME(rm_item != item);
602 36           TreeRBXS_item_detach_tree(rm_item, tree);
603 36 50         } while (rm_node);
604             }
605             }
606 3 50         else if (search[0])
607 3           rbtree_node_insert_after(search[0], &item->rbnode);
608             else
609 0           rbtree_node_insert_before(search[1], &item->rbnode);
610             }
611             /* alter key */
612 44           switch (item->key_type) {
613 14           case KEY_TYPE_ANY:
614             case KEY_TYPE_CLAIM:
615 14           SvREFCNT_dec(item->keyunion.svkey);
616 14 50         if (item->key_type == KEY_TYPE_CLAIM)
617 0           item->keyunion.svkey= SvREFCNT_inc(new_value->keyunion.svkey);
618             else
619 14           item->keyunion.svkey= newSVsv(new_value->keyunion.svkey);
620 14           SvREADONLY_on(item->keyunion.svkey);
621 14           break;
622 0           case KEY_TYPE_INT:
623 0           item->keyunion.ikey= new_value->keyunion.ikey;
624 0           break;
625 0           case KEY_TYPE_FLOAT:
626 0           item->keyunion.nkey= new_value->keyunion.nkey;
627 0           break;
628 30           case KEY_TYPE_USTR:
629             case KEY_TYPE_BSTR:
630             /* the buffer length was prepared above */
631 30           memcpy((char*)item->keyunion.ckey, new_value->keyunion.ckey, new_value->ckeylen);
632 30           item->ckeylen= new_value->ckeylen;
633 30           break;
634 0           default:
635 0           croak("BUG: un-handled key_type %d", new_value->key_type);
636             }
637             }
638 47 100         if (item->orig_key_stored) {
639             /* This is the feature for transformed keys which records the original SV
640             separate from the one used for node comparisons. So, this needs to run
641             regardless of whether the keys compared equal above. */
642 16 50         ASSUME(new_value->orig_key_stored);
643 16           SV *orig= GET_TreeRBXS_item_ORIG_KEY(item);
644 16           SvREFCNT_dec(orig);
645             /* make a copy of the incoming SV */
646 16           orig= newSVsv(GET_TreeRBXS_stack_item_ORIG_KEY(new_value));
647 16           SvREADONLY_on(orig);
648 16           SET_TreeRBXS_item_ORIG_KEY(item, orig);
649             }
650 47           }
651              
652             /* When insert has decided that the temporary node is permitted to be inserted,
653             * this function allocates a real item struct with its own reference counts
654             * and buffer data, etc.
655             */
656 500984           static struct TreeRBXS_item * TreeRBXS_new_item_from_tmp_item(struct TreeRBXS_item *src) {
657             struct TreeRBXS_item *dst;
658 500984 100         bool is_buffered_key= IS_ITEM_KEY_STORED_IN_EXTRA(src);
    100          
659             /* If the item references a string that is not managed by a SV,
660             copy that into the space at the end of the allocated block.
661             Also, if 'owner' is set, it is holding the original SV key
662             which the stack item was initialized from, and also means that
663             a transform function is in effect.
664             The node needs to hold onto the original key, which gets stored
665             at the end of the buffer area */
666 641086 100         if (src->orig_key_stored || is_buffered_key) {
    100          
667             size_t keyptr_len, strbuf_len;
668 140102 50         strbuf_len= is_buffered_key? src->ckeylen+1 : 0;
669 140102           keyptr_len= src->orig_key_stored? sizeof(SV*) : 0;
670 140102           Newxc(dst, sizeof(struct TreeRBXS_item) + keyptr_len + strbuf_len, char, struct TreeRBXS_item);
671 140102           memset(dst, 0, sizeof(struct TreeRBXS_item));
672 140102 100         if (keyptr_len) {
673             // make a copy of the SV, unless requested to take ownership of keys
674 20032           SV *kept= GET_TreeRBXS_stack_item_ORIG_KEY(src);
675 20032 50         kept= src->key_type == KEY_TYPE_CLAIM? SvREFCNT_inc(kept) : newSVsv(kept);
676 20032           SvREADONLY_on(kept);
677             // I don't want to bother aligning this since it's seldomly accessed anyway.
678 20032           SET_TreeRBXS_item_ORIG_KEY(dst, kept);
679             }
680 140102 50         if (is_buffered_key) {
681 140102           char *dst_buf= dst->extra + keyptr_len;
682 140102           memcpy(dst_buf, src->keyunion.ckey, src->ckeylen);
683 140102           dst_buf[src->ckeylen]= '\0';
684 140102           dst->keyunion.ckey= dst_buf;
685 140102           dst->ckeylen= src->ckeylen;
686             }
687             else {
688 0           switch (src->key_type) {
689             // when the key has been transformed, it is a mortal pointer and waiting to be claimed
690             // so TYPE_ANY and TYPE_CLAIM do the same thing here.
691 0           case KEY_TYPE_ANY:
692             case KEY_TYPE_CLAIM:
693 0           dst->keyunion.svkey= src->keyunion.svkey;
694 0           SvREADONLY_on(dst->keyunion.svkey);
695 0           break;
696 0           case KEY_TYPE_INT: dst->keyunion.ikey= src->keyunion.ikey; break;
697 0           case KEY_TYPE_FLOAT: dst->keyunion.nkey= src->keyunion.nkey; break;
698 0           default:
699 0           croak("BUG: un-handled key_type %d", src->key_type);
700             }
701             }
702             }
703             else {
704 360882           Newxz(dst, 1, struct TreeRBXS_item);
705 360882           switch (src->key_type) {
706 130766           case KEY_TYPE_ANY: dst->keyunion.svkey= newSVsv(src->keyunion.svkey);
707             if (0)
708 10000           case KEY_TYPE_CLAIM: dst->keyunion.svkey= SvREFCNT_inc(src->keyunion.svkey);
709 140766           SvREADONLY_on(dst->keyunion.svkey);
710 140766           break;
711 110108           case KEY_TYPE_INT: dst->keyunion.ikey= src->keyunion.ikey; break;
712 110008           case KEY_TYPE_FLOAT: dst->keyunion.nkey= src->keyunion.nkey; break;
713 0           default:
714 0           croak("BUG: un-handled key_type %d", src->key_type);
715             }
716             }
717 500984           dst->orig_key_stored= src->orig_key_stored;
718 500984           dst->key_type= src->key_type;
719 500984           dst->value= newSVsv(src->value);
720 500984           return dst;
721             }
722              
723             /* Get the tree pointer for an item, or NULL if the item is no longer in a tree.
724             * This is an O(log(N)) operation. It could be made constant, but then the node
725             * would be 8 bytes bigger...
726             */
727 175           static struct TreeRBXS* TreeRBXS_item_get_tree(struct TreeRBXS_item *item) {
728 175           rbtree_node_t *node= rbtree_node_rootsentinel(&item->rbnode);
729 175 100         return node? GET_TreeRBXS_FROM_root_sentinel(node) : NULL;
730             }
731              
732 2           static bool TreeRBXS_is_member(struct TreeRBXS *tree, struct TreeRBXS_item *item) {
733 2           rbtree_node_t *node= &item->rbnode;
734 6 50         while (node && node->parent)
    100          
735 4           node= node->parent;
736 2           return node == &tree->root_sentinel;
737             }
738              
739             /* Change the allocated size of the item. If the pointer changes, also trace
740             * down everything pointing to the old memory location and redirect it to the
741             * new one.
742             */
743             static struct TreeRBXS_item *
744 2           TreeRBXS_item_realloc_extra(struct TreeRBXS_item *orig_item, size_t new_extra) {
745 2           rbtree_node_t *orig_rbnode= &orig_item->rbnode;
746 2           rbtree_node_t *root_sentinel= rbtree_node_rootsentinel(orig_rbnode);
747 2 50         struct TreeRBXS *tree= root_sentinel? GET_TreeRBXS_FROM_root_sentinel(root_sentinel) : NULL;
748 2           struct dllist_node *orig_llnode= &orig_item->recent;
749 2           IV orig_ckey_ofs= orig_item->keyunion.ckey - orig_item->extra; /* this may contain garbage depending on key_type */
750              
751             struct TreeRBXS_item *item= (struct TreeRBXS_item*)
752 2           saferealloc(orig_item, sizeof(struct TreeRBXS_item) + new_extra);
753 2 50         if (item != orig_item) {
754             /* if the rbnode was in the tree, update parent and children pointers */
755 2 50         if (tree) {
756 2 50         if (item->rbnode.left->count) {
757 2 50         ASSUME(item->rbnode.left->parent == orig_rbnode);
758 2           item->rbnode.left->parent= &item->rbnode;
759             }
760 2 50         if (item->rbnode.right->count) {
761 2 50         ASSUME(item->rbnode.right->parent == orig_rbnode);
762 2           item->rbnode.right->parent= &item->rbnode;
763             }
764 2 50         if (item->rbnode.parent == root_sentinel) {
765 2 50         ASSUME(root_sentinel->left == orig_rbnode);
766 2           root_sentinel->left= &item->rbnode;
767             }
768 0 0         else if (item->rbnode.parent->left == orig_rbnode)
769 0           item->rbnode.parent->left= &item->rbnode;
770             else {
771 0 0         ASSUME(item->rbnode.parent->right == orig_rbnode);
772 0           item->rbnode.parent->right= &item->rbnode;
773             }
774             /* if member of the 'recent' linked list, update next and prev */
775 2 50         if (item->recent.next) {
776 2 50         ASSUME(item->recent.next->prev == orig_llnode);
777 2 50         ASSUME(item->recent.prev->next == orig_llnode);
778 2           item->recent.next->prev= &item->recent;
779 2           item->recent.prev->next= &item->recent;
780             }
781             /* other possible pointers are the tree's insert optimization */
782 2 50         if (tree->prev_inserted_item == orig_item)
783 0           tree->prev_inserted_item= item;
784             }
785             /* update every iterator which pointed to orig_item to point to new item */
786 2 50         if (item->iter) {
787 0           struct TreeRBXS_iter *cur= item->iter;
788 0 0         for (; cur; cur= cur->next_iter) {
789 0 0         ASSUME(cur->item == orig_item);
790 0           cur->item= item;
791             }
792             }
793             /* If a perl object points to this via MAGIC, update that pointer */
794 2 50         if (item->owner) {
795 2           MAGIC* magic= mg_findext(item->owner, PERL_MAGIC_ext, &TreeRBXS_item_magic_vt);
796 2 50         ASSUME(magic != NULL);
797 2 50         ASSUME(magic->mg_ptr == (char*) orig_item);
798 2           magic->mg_ptr= (char*) item;
799             }
800             /* and finally, update ckey (for the relevant key types) which points into extra[] */
801 2 50         if (IS_ITEM_KEY_STORED_IN_EXTRA(item)) {
    0          
802 2 50         ASSUME(orig_ckey_ofs >= 0 && orig_ckey_ofs <= sizeof(void*));
    50          
803 2           item->keyunion.ckey= item->extra + orig_ckey_ofs;
804             }
805             }
806 2           return item;
807             }
808              
809 500985           static void TreeRBXS_item_free(struct TreeRBXS_item *item) {
810             //warn("TreeRBXS_item_free");
811 500985 100         switch (item->key_type) {
812 140767           case KEY_TYPE_ANY:
813 140767           case KEY_TYPE_CLAIM: SvREFCNT_dec(item->keyunion.svkey); break;
814             }
815 500985 100         if (item->orig_key_stored) {
816 20032           SV *tmp= GET_TreeRBXS_item_ORIG_KEY(item);
817 20032           SvREFCNT_dec(tmp);
818             }
819 500985 50         if (item->value)
820 500985           SvREFCNT_dec(item->value);
821 500985           Safefree(item);
822 500985           }
823              
824             /* Detach the C-struct TreeRBXS_item from the Perl object wrapping it (owner).
825             * If the item was no longer referenced by the tree, either, this deletes the item.
826             */
827 203           static void TreeRBXS_item_detach_owner(struct TreeRBXS_item* item) {
828             //warn("TreeRBXS_item_detach_owner");
829             /* the MAGIC of owner doens't need changed because the only time this gets called
830             is when something else is taking care of that. */
831             //if (item->owner != NULL) {
832             // TreeRBXS_set_magic_item(item->owner, NULL);
833             //}
834 203           item->owner= NULL;
835             /* The tree is the other 'owner' of the node. If the item is not in the tree,
836             then this was the last reference, and it needs freed. */
837 203 100         if (!rbtree_node_is_in_tree(&item->rbnode))
838 25           TreeRBXS_item_free(item);
839 203           }
840              
841             /* Simple linked-list deletion of an iterator from the list pointing to this item.
842             */
843 1659           static void TreeRBXS_item_detach_iter(struct TreeRBXS_item *item, struct TreeRBXS_iter *iter) {
844             struct TreeRBXS_iter **cur;
845              
846             // Linked-list remove
847 16813 50         for (cur= &item->iter; *cur; cur= &((*cur)->next_iter)) {
848 16813 100         if (*cur == iter) {
849 1659           *cur= iter->next_iter;
850 1659           iter->next_iter= NULL;
851 1659           iter->item= NULL;
852 1659           return;
853             }
854             }
855 0           croak("BUG: iterator not found in item's linked list");
856             }
857              
858             /* Reset an iterator to a fresh iteration of whatever direction it was configured to iterate.
859             */
860 15           static void TreeRBXS_iter_rewind(struct TreeRBXS_iter *iter) {
861 15           struct TreeRBXS *tree= iter->tree;
862             struct TreeRBXS_item *item;
863             rbtree_node_t *root;
864             struct dllist_node *lnode;
865 15 100         if (iter->recent) {
866 3 50         lnode= iter->reverse? tree->recent.prev : tree->recent.next;
867 3 50         item= (lnode == &tree->recent)? NULL : GET_TreeRBXS_item_FROM_recent(lnode);
868             }
869             else {
870 12           root= TreeRBXS_get_root(tree);
871 12           rbtree_node_t *node= iter->reverse
872 2           ? rbtree_node_right_leaf(root)
873 12 100         : rbtree_node_left_leaf(root);
874 12           item= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
875             }
876 15           TreeRBXS_iter_set_item(iter, item);
877 15           }
878              
879             /* Point an iterator at a new item, or NULL.
880             */
881 1930           static void TreeRBXS_iter_set_item(struct TreeRBXS_iter *iter, struct TreeRBXS_item *item) {
882 1930 100         if (iter->item == item)
883 13           return;
884              
885 1917 100         if (iter->item)
886 1640           TreeRBXS_item_detach_iter(iter->item, iter);
887              
888 1917 100         if (item) {
889             // If this is a 'recent' iterator, it must be attached to a recent-tracked item
890 1692 100         if (iter->recent && !item->recent.next)
    50          
891 0           croak("BUG: Attempt to bind recent-iterator to non-recent-tracked item");
892 1692           iter->item= item;
893             // linked-list insert
894 1692           iter->next_iter= item->iter;
895 1692           item->iter= iter;
896             }
897             }
898              
899             /* Advance an iterator by a number of steps (ofs) in the configured direction of
900             * travel for that iterator. i.e. negative ofs on a reverse iterator iterates forward.
901             */
902 1667           static void TreeRBXS_iter_advance(struct TreeRBXS_iter *iter, IV ofs) {
903             rbtree_node_t *node;
904 1667           struct TreeRBXS_item *item= iter->item;
905             struct dllist_node *cur, *end;
906              
907 1667 50         if (!iter->tree)
908 0           croak("BUG: iterator lost tree");
909             // Special logic for when iterating insertion order
910 1667 100         if (iter->recent) {
911 103           end= &iter->tree->recent;
912             // Stepping backward from end of iteration?
913 103 100         if (ofs < 0 && !item) {
    50          
914 0 0         cur= iter->reverse? iter->tree->recent.prev : iter->tree->recent.next;
915 0           ++ofs;
916             } else
917 103           cur= &item->recent;
918             // after here, ofs is the direction of travel
919 103 100         if (iter->reverse)
920 16           ofs= -ofs;
921 103 100         if (ofs > 0) {
922 174 100         while (ofs-- > 0 && cur && cur != end)
    50          
    50          
923 88           cur= cur->next;
924 17 50         } else if (ofs < 0) {
925 34 100         while (ofs++ < 0 && cur && cur != end)
    50          
    50          
926 17           cur= cur->prev;
927             }
928 103 50         item= cur && cur != end? GET_TreeRBXS_item_FROM_recent(cur) : NULL;
    100          
929 103           TreeRBXS_iter_set_item(iter, item);
930             }
931             // Most common case
932 1564 100         else if (ofs == 1) {
933             // nothing to do at end of iteration
934 810 100         if (item) {
935 798           node= &item->rbnode;
936 798 100         node= iter->reverse? rbtree_node_prev(node) : rbtree_node_next(node);
937 798           TreeRBXS_iter_set_item(iter, node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL);
938             }
939             }
940             else {
941             size_t pos, newpos, cnt;
942             // More advanced case falls back to by-index, since the log(n) of indexes is likely
943             // about the same as a few hops forward or backward, and because reversing from EOF
944             // means there isn't a current node to step from anyway.
945 754           cnt= TreeRBXS_get_count(iter->tree);
946             // rbtree measures index in size_t, but this function applies a signed offset to it
947             // of possibly a different word length. Also, clamp overflows to the ends of the
948             // range of nodes and don't wrap.
949 754           pos= !item? cnt
950 1491 100         : !iter->reverse? rbtree_node_index(&item->rbnode)
951             // For reverse iterators, swap the scale so that math goes upward
952 737 100         : cnt - 1 - rbtree_node_index(&item->rbnode);
953 754 100         if (ofs > 0) {
954 750 100         newpos= (UV)ofs < (cnt-pos)? pos + ofs : cnt;
955             } else {
956 4           size_t o= (size_t) -ofs;
957 4 50         newpos= (pos < o)? 0 : pos - o;
958             }
959             // swap back for reverse iterators
960 754 100         if (iter->reverse) newpos= cnt - 1 - newpos;
961 754           node= rbtree_node_child_at_index(TreeRBXS_get_root(iter->tree), (size_t)newpos);
962 754           TreeRBXS_iter_set_item(iter, node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL);
963             }
964 1667           }
965              
966             /* Advance all iterators for a given item one step in their respective directions of travel.
967             *
968             * This is an optimized version of calling _iter_advance(item,1) on each iterator of an item.
969             * This has O(log(N) + IterCount) complexity instead of O(log(N) * IterCount),
970             * which could matter for the case where deleting many nodes has resulted in many iterators
971             * piled up on the same node.
972             */
973             #define ONLY_ADVANCE_RECENT 1
974 18           static void TreeRBXS_item_advance_all_iters(struct TreeRBXS_item* item, int flags) {
975             rbtree_node_t *node;
976             struct dllist_node *lnode;
977 18           struct TreeRBXS_item *next_item= NULL, *prev_item= NULL,
978 18           *next_recent= NULL, *prev_recent= NULL;
979             struct TreeRBXS_iter *iter, *next;
980            
981             // Dissolve a linked list to move the iters to the previous or next item's linked list
982 88 100         for (iter= item->iter, item->iter= NULL; iter; iter= next) {
983 70           next= iter->next_iter;
984 70 100         if (iter->recent) { // iterating insertion order?
985 8 100         if (iter->reverse) { // newest to oldest?
986 3 50         if (!prev_recent) {
987 3           lnode= item->recent.prev;
988 3 50         if (lnode == NULL || lnode == &iter->tree->recent) {
    100          
989 1           iter->item= NULL;
990 1           iter->next_iter= NULL;
991 1           continue;
992             }
993 2           prev_recent= GET_TreeRBXS_item_FROM_recent(lnode);
994             }
995 2           iter->item= prev_recent;
996             // linked list add head node
997 2           iter->next_iter= prev_recent->iter;
998 2           prev_recent->iter= iter;
999             }
1000             else { // oldest to newest
1001 5 100         if (!next_recent) {
1002 3           lnode= item->recent.next;
1003 3 50         if (lnode == NULL || lnode == &iter->tree->recent) {
    50          
1004 0           iter->item= NULL;
1005 0           iter->next_iter= NULL;
1006 0           continue;
1007             }
1008 3           next_recent= GET_TreeRBXS_item_FROM_recent(lnode);
1009             }
1010 5           iter->item= next_recent;
1011 5           iter->next_iter= next_recent->iter;
1012 5           next_recent->iter= iter;
1013             }
1014             }
1015 62 100         else if (flags & ONLY_ADVANCE_RECENT) {
1016             // only advancing recent-list iterators, so key-order iterator needs to stay at this item.
1017 3           iter->next_iter= item->iter;
1018 3           item->iter= iter;
1019             }
1020             else { // iterating key order
1021 59 100         if (iter->reverse) { // iterating high to low
1022 31 100         if (!prev_item) {
1023 22           node= rbtree_node_prev(&item->rbnode);
1024 22 100         if (!node) {
1025             // end of iteration
1026 17           iter->item= NULL;
1027 17           iter->next_iter= NULL;
1028 17           continue;
1029             }
1030 5           prev_item= GET_TreeRBXS_item_FROM_rbnode(node);
1031             }
1032 14           iter->item= prev_item;
1033             // linked list add head node
1034 14           iter->next_iter= prev_item->iter;
1035 14           prev_item->iter= iter;
1036             }
1037             else { // iterating low to high
1038 28 100         if (!next_item) {
1039 18           node= rbtree_node_next(&item->rbnode);
1040 18 100         if (!node) {
1041             // end of iteration
1042 9           iter->item= NULL;
1043 9           iter->next_iter= NULL;
1044 9           continue;
1045             }
1046 9           next_item= GET_TreeRBXS_item_FROM_rbnode(node);
1047             }
1048 19           iter->item= next_item;
1049             // linked list add head node
1050 19           iter->next_iter= next_item->iter;
1051 19           next_item->iter= iter;
1052             }
1053             }
1054             }
1055 18           }
1056              
1057             /* Disconnect an item from the tree, gracefully
1058             */
1059 97           static void TreeRBXS_item_detach_tree(struct TreeRBXS_item* item, struct TreeRBXS *tree) {
1060             //warn("TreeRBXS_item_detach_tree");
1061             //warn("detach tree %p %p key %d", item, tree, (int) item->keyunion.ikey);
1062 97 50         if (rbtree_node_is_in_tree(&item->rbnode)) {
1063 97 100         if (tree->prev_inserted_item == item) {
1064 14           tree->prev_inserted_item= NULL;
1065 14           tree->prev_inserted_trend= 0;
1066             }
1067             // If any iterator points to this node, move it to the following node.
1068 97 100         if (item->iter)
1069 16           TreeRBXS_item_advance_all_iters(item, 0);
1070             // If the node was part of LRU cache, cancel that
1071 97 100         if (item->recent.next)
1072 25           TreeRBXS_recent_prune(tree, item);
1073 97           rbtree_node_prune(&item->rbnode);
1074             }
1075             /* The item could be owned by a tree or by a Node/Iterator, or both.
1076             If the tree releases the reference, the Node/Iterator will be the owner.
1077             Else the tree was the only owner, and the node needs freed */
1078 97 100         if (!item->owner)
1079 85           TreeRBXS_item_free(item);
1080 97           }
1081              
1082             /* Callback when clearing the entire tree.
1083             * This is like _detach_tree, but doesn't need to clean up relation to other nodes.
1084             */
1085 500887           static void TreeRBXS_item_clear(struct TreeRBXS_item* item, struct TreeRBXS *tree) {
1086 500887           struct TreeRBXS_iter *iter= item->iter, *next;
1087             // Detach all iterators from this node.
1088 500893 100         while (iter) {
1089 6           next= iter->next_iter;
1090 6           iter->item= NULL;
1091 6           iter->next_iter= NULL;
1092 6           iter= next;
1093             }
1094 500887           item->iter= NULL;
1095 500887           item->recent.next= NULL;
1096 500887           item->recent.prev= NULL;
1097 500887 50         if (rbtree_node_is_in_tree(&item->rbnode))
1098 0           memset(&item->rbnode, 0, sizeof(item->rbnode));
1099             // If the item is still referenced by the perl Node object, don't delete it.
1100 500887 100         if (!item->owner)
1101 500875           TreeRBXS_item_free(item);
1102 500887           }
1103              
1104 262           static void TreeRBXS_iter_free(struct TreeRBXS_iter *iter) {
1105 262 100         if (iter->item)
1106 19           TreeRBXS_item_detach_iter(iter->item, iter);
1107 262 50         if (iter->tree) {
1108 262 100         if (iter->tree->hashiter == iter)
1109 5           iter->tree->hashiter= NULL;
1110             else
1111 257           SvREFCNT_dec(iter->tree->owner);
1112             }
1113 262           Safefree(iter);
1114 262           }
1115              
1116 111           static void TreeRBXS_destroy(struct TreeRBXS *tree) {
1117             //warn("TreeRBXS_destroy");
1118 111           rbtree_clear(&tree->root_sentinel, (void (*)(void *, void *)) &TreeRBXS_item_clear, -OFS_TreeRBXS_item_FIELD_rbnode, tree);
1119 111 100         if (tree->compare_callback)
1120 8           SvREFCNT_dec(tree->compare_callback);
1121 111 100         if (tree->hashiter)
1122 5           TreeRBXS_iter_free(tree->hashiter);
1123 111           }
1124              
1125             // This gets used in two places, but I don't want to make it a function.
1126             #define TREERBXS_INSERT_ITEM_AT_NODE(tree, item, parent_node, direction) \
1127             do { \
1128             if (!(parent_node)) /* empty tree */ \
1129             rbtree_node_insert_before(&(tree)->root_sentinel, &(item)->rbnode); \
1130             else if ((direction) > 0) \
1131             rbtree_node_insert_after((parent_node), &(item)->rbnode); \
1132             else \
1133             rbtree_node_insert_before((parent_node), &(item)->rbnode); \
1134             if ((tree)->track_recent) \
1135             TreeRBXS_recent_insert_before((tree), (item), &(tree)->recent); \
1136             } while (0)
1137              
1138 374           static struct TreeRBXS_item *TreeRBXS_find_item(struct TreeRBXS *tree, struct TreeRBXS_item *stack_item, int mode) {
1139             struct TreeRBXS_item *item;
1140 374           rbtree_node_t *node= NULL;
1141             int cmp;
1142 374           bool step= false;
1143              
1144             // Need to ensure we find the *first* matching node for a key,
1145             // to deal with the case of duplicate keys.
1146 374           node= rbtree_find_nearest(
1147             &tree->root_sentinel,
1148             stack_item, // The item *is* the key that gets passed to the compare function
1149 374           (int(*)(void*,void*,void*)) tree->compare,
1150             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
1151             &cmp);
1152 374 100         if (node && cmp == 0) {
    100          
1153             // Found an exact match. First and last are the range of nodes matching.
1154 327           switch (mode) {
1155 8           case GET_LT:
1156             case GET_PREV:
1157 8           step= true;
1158 312           case GET_EQ:
1159             case GET_OR_ADD:
1160             case GET_GE:
1161             case GET_LE:
1162             // make sure it is the first of nodes with same key
1163 312 100         if (tree->allowed_duplicates)
1164 2           node= rbtree_find_leftmost_samekey(node, (int(*)(void*,void*,void*)) tree->compare,
1165             tree, -OFS_TreeRBXS_item_FIELD_rbnode);
1166 312 100         if (step)
1167 8           node= rbtree_node_prev(node);
1168 312           break;
1169 10           case GET_GT:
1170             case GET_NEXT:
1171 10           step= true;
1172 15           case GET_EQ_LAST:
1173             case GET_LE_LAST:
1174             // make sure it is the last of nodes with same key
1175 15 100         if (tree->allowed_duplicates)
1176 1           node= rbtree_find_rightmost_samekey(node, (int(*)(void*,void*,void*)) tree->compare,
1177             tree, -OFS_TreeRBXS_item_FIELD_rbnode);
1178 15 100         if (step)
1179 10           node= rbtree_node_next(node);
1180 15           break;
1181 0           default: croak("BUG: unhandled mode");
1182             }
1183             } else {
1184             // Didn't find an exact match. First and last are the bounds of what would have matched.
1185 47           switch (mode) {
1186 27           case GET_EQ:
1187             case GET_EQ_LAST:
1188             case GET_PREV:
1189             case GET_NEXT:
1190 27           node= NULL; break;
1191 7           case GET_GE:
1192             case GET_GT:
1193 7 50         if (node && cmp > 0)
    100          
1194 2           node= rbtree_node_next(node);
1195 7           break;
1196 7           case GET_LE:
1197             case GET_LE_LAST:
1198             case GET_LT:
1199 7 50         if (node && cmp < 0)
    100          
1200 6           node= rbtree_node_prev(node);
1201 7           break;
1202 6           case GET_OR_ADD:
1203 6           item= TreeRBXS_new_item_from_tmp_item(stack_item);
1204 6 100         TREERBXS_INSERT_ITEM_AT_NODE(tree, item, node, cmp);
    50          
    50          
1205 6           return item;
1206 0           default: croak("BUG: unhandled mode");
1207             }
1208             }
1209 368           return node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
1210             }
1211              
1212             struct TreeRBXS_item *
1213 501010           TreeRBXS_insert_item(struct TreeRBXS *tree, struct TreeRBXS_item *stack_item, bool overwrite, SV **oldval_out) {
1214             struct TreeRBXS_item *item;
1215             rbtree_node_t *hint, *tmpnode, *first, *last;
1216             int cmp;
1217             /* If newly inserted items have been adjacent to prev_inserted_item 3 or more times in a row,
1218             It is worth comparing them with that first. This optimization results in nearly linear
1219             insertion time when the keys are pre-sorted. */
1220 501010 100         if (tree->prev_inserted_trend >= INSERT_TREND_TRIGGER) {
1221 500671 100         if (tree->prev_inserted_trend > INSERT_TREND_CAP)
1222 470541           tree->prev_inserted_trend= INSERT_TREND_CAP;
1223 500671           hint= &tree->prev_inserted_item->rbnode;
1224 500671           cmp= tree->compare(tree, stack_item, tree->prev_inserted_item);
1225 500671 100         if (cmp == 0) {
1226 14           ++tree->prev_inserted_trend;
1227 14 100         if (overwrite) {
1228 1 50         if (tree->prev_inserted_dup) {
1229             /* the 'hint' provided needs to be the parent-most node of the duplicates */
1230 1           while (hint->parent != &tree->root_sentinel
1231 2 100         && tree->compare(tree, stack_item, GET_TreeRBXS_item_FROM_rbnode(hint->parent)) == 0)
    50          
1232 1           hint= hint->parent;
1233 1           goto overwrite_multi;
1234             }
1235             else
1236 0           goto overwrite_single;
1237 13 50         } else if (tree->allow_duplicates)
1238 13           goto insert_new_duplicate;
1239             else
1240 0           return NULL;
1241             }
1242 500657 100         else if (cmp > 0) {
1243 500607           tmpnode= rbtree_node_next(hint);
1244 500607 100         if (!tmpnode || tree->compare(tree, stack_item, GET_TreeRBXS_item_FROM_rbnode(tmpnode)) < 0) {
    100          
1245 485618           ++tree->prev_inserted_trend;
1246 485618           goto insert_relative;
1247             }
1248             // else it broke the trend and needs inserted normally.
1249             }
1250             else {
1251 50           tmpnode= rbtree_node_prev(hint);
1252 50 100         if (!tmpnode || tree->compare(tree, stack_item, GET_TreeRBXS_item_FROM_rbnode(tmpnode)) > 0) {
    100          
1253 12           ++tree->prev_inserted_trend;
1254 12           goto insert_relative;
1255             }
1256             // else it broke the trend and needs inserted normally.
1257             }
1258 15027           --tree->prev_inserted_trend;
1259             }
1260 15366           hint= rbtree_find_nearest(
1261             &tree->root_sentinel,
1262             stack_item, // The item *is* the key that gets passed to the compare function
1263 15366           (int(*)(void*,void*,void*)) tree->compare,
1264             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
1265             &cmp);
1266 15366 100         if (hint && cmp == 0) {
    100          
1267 52 100         if (overwrite) {
1268             // In case of multiple matches, find all
1269 25 100         if (tree->allowed_duplicates) {
1270 2           overwrite_multi:
1271 3 50         if (!rbtree_find_all(
1272             hint, stack_item,
1273 3           (int(*)(void*,void*,void*)) tree->compare,
1274             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
1275             &first, &last, NULL)
1276             )
1277 0           croak("BUG");
1278             //warn("replacing %d matching keys with new value", (int)count);
1279             // prune every node that follows 'first'
1280 9 100         while (last != first) {
1281 6           item= GET_TreeRBXS_item_FROM_rbnode(last);
1282 6           last= rbtree_node_prev(last);
1283 6           TreeRBXS_item_detach_tree(item, tree);
1284             }
1285 3           hint= first;
1286             }
1287             /* overwrite the value of the node */
1288 23           overwrite_single:
1289 26           item= GET_TreeRBXS_item_FROM_rbnode(hint);
1290 26           sv_2mortal(item->value);
1291 26 100         if (oldval_out)
1292 23           *oldval_out= item->value; // return the old value
1293 26           item->value= newSVsv(stack_item->value); // store new copy of supplied param
1294             /* If the tree is applying a transform to the items, the new key might not be identical
1295             to the old, even though the transformed keys are equal. (i.e. different case)
1296             So, store the new key in its place. */
1297 26 100         if (item->orig_key_stored) {
1298 6           SV *orig_key= GET_TreeRBXS_item_ORIG_KEY(item);
1299 6           SV *new_key= GET_TreeRBXS_stack_item_ORIG_KEY(stack_item);
1300 6 50         if (sv_cmp(orig_key, new_key)) {
1301 6           SvREFCNT_dec(orig_key);
1302 6           orig_key= newSVsv(new_key);
1303 6           SvREADONLY_on(orig_key);
1304 6           SET_TreeRBXS_item_ORIG_KEY(item, orig_key);
1305             }
1306             }
1307 26 100         if (tree->track_recent || item->recent.next)
    50          
1308 1           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
1309 26           tree->prev_inserted_dup= false;
1310 27 100         } else if (tree->allow_duplicates) {
1311 21           hint= rbtree_find_rightmost_samekey(hint, (int(*)(void*,void*,void*)) tree->compare,
1312             tree, -OFS_TreeRBXS_item_FIELD_rbnode);
1313 34           insert_new_duplicate:
1314 34           item= TreeRBXS_new_item_from_tmp_item(stack_item);
1315 34           rbtree_node_insert_after(hint, &item->rbnode);
1316 34 100         if (tree->track_recent)
1317 8           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
1318 34           tree->allowed_duplicates= true;
1319 34           tree->prev_inserted_dup= true;
1320             } else {
1321 6           item= GET_TreeRBXS_item_FROM_rbnode(hint);
1322 6 100         if (item == tree->prev_inserted_item)
1323 3           ++tree->prev_inserted_trend;
1324 6           return NULL; // nothing inserted
1325             }
1326             }
1327             else {
1328 15314           insert_relative:
1329 500944           item= TreeRBXS_new_item_from_tmp_item(stack_item);
1330 500944 100         TREERBXS_INSERT_ITEM_AT_NODE(tree, item, hint, cmp);
    100          
    100          
1331 500944           tree->prev_inserted_dup= false;
1332             }
1333             // If trend logic is triggered above, this is already calculated. Else check adjacency.
1334 501004 100         if (tree->prev_inserted_trend < INSERT_TREND_TRIGGER && tree->prev_inserted_item) {
    100          
1335             // Check trend. Is item adjacent to 'prev'?
1336 242 100         if (item == tree->prev_inserted_item
1337 234 100         || item->rbnode.parent == &tree->prev_inserted_item->rbnode
1338 57 100         || item->rbnode.left == &tree->prev_inserted_item->rbnode
1339 50 100         || item->rbnode.right == &tree->prev_inserted_item->rbnode
1340             ) {
1341 203           ++tree->prev_inserted_trend;
1342 39 100         } else if (tree->prev_inserted_trend)
1343 24           --tree->prev_inserted_trend;
1344             }
1345 501004           tree->prev_inserted_item= item;
1346 501004           return item;
1347             }
1348              
1349             /* Mark the current tree item as the most recent, regardless of whether it was previously tracked.
1350             */
1351 111           static void TreeRBXS_recent_insert_before(struct TreeRBXS *tree, struct TreeRBXS_item *item, struct dllist_node *node_after) {
1352             struct dllist_node *node_before;
1353             // Not already before this node?
1354 111 100         if (item->recent.next != node_after) {
1355 110 100         if (item->recent.next) {
1356             // remove from linkedlist
1357 6           item->recent.prev->next= item->recent.next;
1358 6           item->recent.next->prev= item->recent.prev;
1359             } else
1360 104           ++tree->recent_count;
1361             // Add following 'node_before'
1362 110           node_before= node_after->prev;
1363 110           node_after->prev= &item->recent;
1364 110           node_before->next= &item->recent;
1365 110           item->recent.prev= node_before;
1366 110           item->recent.next= node_after;
1367             // prune oldest node if exceeded limit
1368 110 100         if (tree->recent_limit >= 0 && tree->recent_count > tree->recent_limit)
    100          
1369 14           TreeRBXS_truncate_recent(tree, tree->recent_limit, NULL);
1370             }
1371 111           }
1372              
1373             /* Stop recent-tracking for the tree item.
1374             * Users can call this at any time in order to remove certain nodes from consideration.
1375             */
1376 35           static void TreeRBXS_recent_prune(struct TreeRBXS *tree, struct TreeRBXS_item *item) {
1377 35 50         if (item->recent.next) {
1378             // Move recent-list iterators pointing to this node
1379 35 100         if (item->iter)
1380 2           TreeRBXS_item_advance_all_iters(item, ONLY_ADVANCE_RECENT);
1381 35           item->recent.prev->next= item->recent.next;
1382 35           item->recent.next->prev= item->recent.prev;
1383 35           item->recent.next= NULL;
1384 35           item->recent.prev= NULL;
1385 35           --tree->recent_count;
1386             }
1387 35           }
1388              
1389             /* Remove all nodes older than the newest 'lim'-count
1390             * The caller may optionally supply 'nodes_out' to receive mortal references
1391             * to the removed nodes. The caller must size it for (tree->recent_count - lim)
1392             */
1393 15           static void TreeRBXS_truncate_recent(struct TreeRBXS *tree, IV lim, SV **nodes_out) {
1394 15 50         if (lim >= 0 && tree->recent_count > lim) {
    50          
1395 15           IV i, n= tree->recent_count - lim;
1396 15           struct dllist_node *next, *cur= tree->recent.next;
1397 32 100         for (i= 0; i < n && cur && cur != &tree->recent; i++) {
    50          
    50          
1398 17           struct TreeRBXS_item *item= GET_TreeRBXS_item_FROM_recent(cur);
1399 17 50         if (nodes_out)
1400 0           nodes_out[i]= sv_2mortal(TreeRBXS_wrap_item(item));
1401 17           next= cur->next;
1402 17           TreeRBXS_item_detach_tree(item, tree);
1403 17           cur= next;
1404             }
1405 15 50         if (i != n)
1406 0           croak("BUG: recent_count inconsistent with length of linked list");
1407             }
1408 15           }
1409              
1410             /*----------------------------------------------------------------------------
1411             * Comparison Functions.
1412             * These conform to the rbtree_compare_fn signature of a context followed by
1413             * two "key" pointers. In this case, the keys are TreeRBXS_item structs
1414             * and the actual key field depends on the key_type of the node. However,
1415             * for speed, the key_type is assumed to have been chosen correctly for the
1416             * comparison function during _init
1417             */
1418              
1419             // Compare integers which were both already decoded from the original SVs
1420 221070           static int TreeRBXS_cmp_int(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1421             //warn(" int compare %p (%d) <=> %p (%d)", a, (int)a->keyunion.ikey, b, (int)b->keyunion.ikey);
1422 221070           IV diff= a->keyunion.ikey - b->keyunion.ikey;
1423 221070 100         return diff < 0? -1 : diff > 0? 1 : 0; /* shrink from IV to int might lose upper bits */
1424             }
1425              
1426             // Compare floats which were both already decoded from the original SVs
1427 220821           static int TreeRBXS_cmp_float(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1428 220821           NV diff= a->keyunion.nkey - b->keyunion.nkey;
1429 220821 100         return diff < 0? -1 : diff > 0? 1 : 0;
1430             }
1431              
1432             // Compare C strings using memcmp, on raw byte values. The strings have been pre-processed to
1433             // be comparable with memcmp, by case-folding, or making sure both are UTF-8, etc.
1434 672291           static int TreeRBXS_cmp_memcmp(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1435 672291           size_t alen= a->ckeylen, blen= b->ckeylen;
1436 672291           int cmp= memcmp(a->keyunion.ckey, b->keyunion.ckey, alen < blen? alen : blen);
1437 672291 100         return cmp? cmp : alen < blen? -1 : alen > blen? 1 : 0;
    100          
1438             }
1439              
1440 40253           static int cmp_numsplit(
1441             const char *apos, const char *alim, bool a_utf8,
1442             const char *bpos, const char *blim, bool b_utf8
1443             ) {
1444             const char *amark, *bmark;
1445             size_t alen, blen;
1446             int cmp;
1447              
1448 40308 100         while (apos < alim && bpos < blim) {
    100          
1449             // Step forward as long as both strings are identical
1450 40417 100         while (apos < alim && bpos < blim && *apos == *bpos && !isdigit(*apos))
    50          
    100          
    100          
1451 124           apos++, bpos++;
1452             // find the next start of digits along the strings
1453 40293           amark= apos;
1454 40636 100         while (apos < alim && !isdigit(*apos)) apos++;
    100          
1455 40293           bmark= bpos;
1456 40487 100         while (bpos < blim && !isdigit(*bpos)) bpos++;
    100          
1457 40293           alen= apos - amark;
1458 40293           blen= bpos - bmark;
1459             // compare the non-digit portions found in each string
1460 40293 100         if (alen || blen) {
    100          
1461             // If one of the non-digit spans was length=0, then we are comparing digits (or EOF)
1462             // with string, and digits sort first.
1463 92 100         if (alen == 0) return -1;
1464 82 100         if (blen == 0) return 1;
1465             // else compare the portions in common.
1466             #if PERL_VERSION_GE(5,14,0)
1467 36 50         if (a_utf8 != b_utf8) {
1468 0           cmp= a_utf8? -bytes_cmp_utf8((const U8*) bmark, blen, (const U8*) amark, alen)
1469 0 0         : bytes_cmp_utf8((const U8*) amark, alen, (const U8*) bmark, blen);
1470 0 0         if (cmp) return cmp;
1471             } else
1472             #endif
1473             {
1474 36           cmp= memcmp(amark, bmark, alen < blen? alen : blen);
1475 36 50         if (cmp) return cmp;
1476 0 0         if (alen < blen) return -1;
1477 0 0         if (alen > blen) return -1;
1478             }
1479             }
1480             // If one of the strings ran out of characters, it is the lesser one.
1481 40201 100         if (!(apos < alim && bpos < blim)) break;
    50          
1482             // compare the digit portions found in each string
1483             // Find the start of nonzero digits
1484 40387 50         while (apos < alim && *apos == '0') apos++;
    100          
1485 40325 50         while (bpos < blim && *bpos == '0') bpos++;
    100          
1486 40197           amark= apos;
1487 40197           bmark= bpos;
1488             // find the first differing digit
1489 147629 100         while (apos < alim && bpos < blim && *apos == *bpos && isdigit(*apos))
    100          
    100          
    100          
1490 107432           apos++, bpos++;
1491             // If there are more digits to consider beyond the first mismatch (or EOF) then need to
1492             // find the end of the digits and see which number was longer.
1493 40197 100         if ((apos < alim && isdigit(*apos)) || (bpos < blim && isdigit(*bpos))) {
    100          
    100          
    100          
1494 40142 100         if (apos == alim) return -1;
1495 40141 50         if (bpos == blim) return 1;
1496             // If the strings happen to be the same length, this will be the deciding character
1497 40141           cmp= *apos - *bpos;
1498             // find the end of digits
1499 88660 100         while (apos < alim && isdigit(*apos)) apos++;
    100          
1500 88645 100         while (bpos < blim && isdigit(*bpos)) bpos++;
    100          
1501             // Whichever number is longer is greater
1502 40141           alen= apos - amark;
1503 40141           blen= bpos - bmark;
1504 40141 100         if (alen < blen) return -1;
1505 40089 100         if (alen > blen) return 1;
1506             // Else they're the same length, and the 'cmp' captured earlier is the answer.
1507 40022           return cmp;
1508             }
1509             // Else they're equal, continue to the next component.
1510             }
1511             // One or both of the strings ran out of characters
1512 19 100         if (bpos < blim) return -1;
1513 18 100         if (apos < alim) return 1;
1514 4           return 0;
1515             }
1516              
1517 40186           static int TreeRBXS_cmp_numsplit(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1518             const char *apos, *alim;
1519             const char *bpos, *blim;
1520             size_t alen, blen;
1521 40186           bool a_utf8= false, b_utf8= false;
1522              
1523 40186           switch (tree->key_type) {
1524 20062           case KEY_TYPE_USTR:
1525 20062           a_utf8= b_utf8= true;
1526 20124           case KEY_TYPE_BSTR:
1527 20124           apos= a->keyunion.ckey; alim= apos + a->ckeylen;
1528 20124           bpos= b->keyunion.ckey; blim= bpos + b->ckeylen;
1529 20124           break;
1530 20062           case KEY_TYPE_ANY:
1531             case KEY_TYPE_CLAIM:
1532             #if PERL_VERSION_LT(5,14,0)
1533             // before 5.14, need to force both to utf8 if either are utf8
1534             if (SvUTF8(a->keyunion.svkey) || SvUTF8(b->keyunion.svkey)) {
1535             apos= SvPVutf8(a->keyunion.svkey, alen);
1536             bpos= SvPVutf8(b->keyunion.svkey, blen);
1537             a_utf8= b_utf8= true;
1538             } else
1539             #else
1540             // After 5.14, can compare utf8 with bytes without converting the buffer
1541 20062           a_utf8= SvUTF8(a->keyunion.svkey);
1542 20062           b_utf8= SvUTF8(b->keyunion.svkey);
1543             #endif
1544             {
1545 20062           apos= SvPV(a->keyunion.svkey, alen);
1546 20062           bpos= SvPV(b->keyunion.svkey, blen);
1547             }
1548 20062           alim= apos + alen;
1549 20062           blim= bpos + blen;
1550 20062           break;
1551 0           default: croak("BUG");
1552             }
1553 40186           return cmp_numsplit(apos, alim, a_utf8, bpos, blim, b_utf8);
1554             }
1555              
1556             // Compare SV items using Perl's 'cmp' operator
1557 95416           static int TreeRBXS_cmp_perl(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1558 95416           return sv_cmp(a->keyunion.svkey, b->keyunion.svkey);
1559             }
1560              
1561             // Compare SV items using a user-supplied perl callback
1562 221372           static int TreeRBXS_cmp_perl_cb(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1563             IV ret;
1564 221372           dSP;
1565 221372           ENTER;
1566             // There are a max of $tree_depth comparisons to do during an insert or search,
1567             // so should be safe to not free temporaries for a little bit.
1568 221372 50         PUSHMARK(SP);
1569 221372 50         EXTEND(SP, 2);
1570 221372           PUSHs(a->keyunion.svkey);
1571 221372           PUSHs(b->keyunion.svkey);
1572 221372           PUTBACK;
1573 221372 50         if (call_sv(tree->compare_callback, G_SCALAR) != 1)
1574 0           croak("stack assertion failed");
1575 221372           SPAGAIN;
1576 221372           ret= POPi;
1577 221372           PUTBACK;
1578             // FREETMPS;
1579 221372           LEAVE;
1580 221372 100         return (int)(ret < 0? -1 : ret > 0? 1 : 0);
1581             }
1582              
1583             // Equivalent of "return fc($key)" (case folding)
1584 20079           static SV* TreeRBXS_xform_fc(struct TreeRBXS *tree, SV *orig_key) {
1585             SV *folded_mortal;
1586 20079           dSP;
1587             /* Annoyingly, the 'fc' implementation is not packaged into the perl api
1588             * as a callable function, so I just have to invoke the perl op itself :-(
1589             * For 5.16 and onward I can call CORE::fc, but before that I even need
1590             * to wrap the op in my own sub. (see XS.pm)
1591             */
1592 20079           ENTER;
1593 20079 50         PUSHMARK(SP);
1594 20079 50         XPUSHs(orig_key);
1595 20079           PUTBACK;
1596             #if PERL_VERSION_GE(5,16,0)
1597 20079           call_pv("CORE::fc", G_SCALAR);
1598             #else
1599             call_pv("Tree::RB::XS::_fc_impl", G_SCALAR);
1600             #endif
1601 20079           SPAGAIN;
1602 20079           folded_mortal= POPs;
1603 20079           PUTBACK;
1604 20079           LEAVE;
1605 20079           return folded_mortal;
1606             }
1607              
1608             /*------------------------------------------------------------------------------------
1609             * Definitions of Perl MAGIC that attach C structs to Perl SVs
1610             * All instances of Tree::RB::XS have a magic-attached struct TreeRBXS
1611             * All instances of Tree::RB::XS::Node have a magic-attached struct TreeRBXS_item
1612             */
1613              
1614             // destructor for Tree::RB::XS
1615 111           static int TreeRBXS_magic_free(pTHX_ SV* sv, MAGIC* mg) {
1616 111 50         if (mg->mg_ptr) {
1617 111           TreeRBXS_destroy((struct TreeRBXS*) mg->mg_ptr);
1618 111           Safefree(mg->mg_ptr);
1619 111           mg->mg_ptr= NULL;
1620             }
1621 111           return 0; // ignored anyway
1622             }
1623              
1624             // destructor for Tree::RB::XS::Node
1625 203           static int TreeRBXS_item_magic_free(pTHX_ SV* sv, MAGIC* mg) {
1626 203 50         if (mg->mg_ptr) {
1627 203           TreeRBXS_item_detach_owner((struct TreeRBXS_item*) mg->mg_ptr);
1628 203           mg->mg_ptr= NULL;
1629             }
1630 203           return 0;
1631             }
1632              
1633             // destructor for Tree::RB::XS::Iter
1634 257           static int TreeRBXS_iter_magic_free(pTHX_ SV* sv, MAGIC *mg) {
1635 257 50         if (mg->mg_ptr)
1636 257           TreeRBXS_iter_free((struct TreeRBXS_iter*) mg->mg_ptr);
1637 257           return 0;
1638             }
1639              
1640             #ifdef USE_ITHREADS
1641             /* Function that tells ithread users they can't clone the tree
1642             * (patches welcome, but it's a hard problem with needing to reconnect the
1643             * items and iterators that might be held as perl objects)
1644             */
1645             static int TreeRBXS_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
1646             croak("This object cannot be shared between threads");
1647             return 0;
1648             };
1649             #endif
1650              
1651              
1652             // Return the TreeRBXS struct attached to a Perl object via MAGIC.
1653             // The 'obj' should be a reference to a blessed SV.
1654             // Use AUTOCREATE to attach magic and allocate a struct if it wasn't present.
1655             // Use OR_DIE for a built-in croak() if the return value would be NULL.
1656 501531           static struct TreeRBXS* TreeRBXS_get_magic_tree(SV *obj, int flags) {
1657             SV *sv;
1658             MAGIC* magic;
1659             struct TreeRBXS *tree;
1660 501531 50         if (!sv_isobject(obj)) {
1661 0 0         if (flags & OR_DIE)
1662 0           croak("Not an object");
1663 0           return NULL;
1664             }
1665 501531           sv= SvRV(obj);
1666 501531 100         if (SvMAGICAL(sv) && (magic= mg_findext(sv, PERL_MAGIC_ext, &TreeRBXS_magic_vt)))
    50          
1667 501420           return (struct TreeRBXS*) magic->mg_ptr;
1668              
1669 111 50         if (flags & AUTOCREATE) {
1670 111           Newxz(tree, 1, struct TreeRBXS);
1671 111           TreeRBXS_init(tree, sv);
1672 111           magic= sv_magicext(sv, NULL, PERL_MAGIC_ext, &TreeRBXS_magic_vt, (const char*) tree, 0);
1673             #ifdef USE_ITHREADS
1674             magic->mg_flags |= MGf_DUP;
1675             #endif
1676 111           return tree;
1677             }
1678 0 0         else if (flags & OR_DIE)
1679 0           croak("Object lacks 'struct TreeRBXS' magic");
1680 0           return NULL;
1681             }
1682              
1683             // Return the TreeRBXS_item that was attached to a perl object via MAGIC.
1684             // The 'obj' should be a reference to a blessed magical SV.
1685 728           static struct TreeRBXS_item* TreeRBXS_get_magic_item(SV *obj, int flags) {
1686             SV *sv;
1687             MAGIC* magic;
1688              
1689 728 100         if (!sv_isobject(obj)) {
1690 34 50         if (flags & OR_DIE)
1691 0           croak("Not an object");
1692 34           return NULL;
1693             }
1694 694           sv= SvRV(obj);
1695 694 50         if (SvMAGICAL(sv) && (magic= mg_findext(sv, PERL_MAGIC_ext, &TreeRBXS_item_magic_vt)))
    100          
1696 452           return (struct TreeRBXS_item*) magic->mg_ptr;
1697              
1698 242 50         if (flags & OR_DIE)
1699 0           croak("Object lacks 'struct TreeRBXS_item' magic");
1700 242           return NULL;
1701             }
1702              
1703             // Return existing Node object, or create a new one.
1704             // Returned SV is a reference with active refcount, which is what the typemap
1705             // wants for returning a "struct TreeRBXS_item*" to perl-land
1706 296           static SV* TreeRBXS_wrap_item(struct TreeRBXS_item *item) {
1707             SV *obj;
1708             MAGIC *magic;
1709             // Since this is used in typemap, handle NULL gracefully
1710 296 100         if (!item)
1711 75           return &PL_sv_undef;
1712             // If there is already a node object, return a new reference to it.
1713 221 100         if (item->owner)
1714 21           return newRV_inc(item->owner);
1715             // else create a node object
1716 200           item->owner= newSV(0);
1717 200           obj= newRV_noinc(item->owner);
1718 200           sv_bless(obj, gv_stashpv("Tree::RB::XS::Node", GV_ADD));
1719 200           magic= sv_magicext(item->owner, NULL, PERL_MAGIC_ext, &TreeRBXS_item_magic_vt, (const char*) item, 0);
1720             #ifdef USE_ITHREADS
1721             magic->mg_flags |= MGf_DUP;
1722             #else
1723             (void)magic; // suppress warning
1724             #endif
1725 200           return obj;
1726             }
1727              
1728 926           static SV* TreeRBXS_item_wrap_key(struct TreeRBXS_item *item) {
1729 926 100         if (!item)
1730 24           return &PL_sv_undef;
1731 902 100         if (item->orig_key_stored) {
1732 159           SV *tmp= GET_TreeRBXS_item_ORIG_KEY(item);
1733 159           return SvREFCNT_inc(tmp);
1734             }
1735 743           switch (item->key_type) {
1736 497           case KEY_TYPE_ANY:
1737 497           case KEY_TYPE_CLAIM: return SvREFCNT_inc(item->keyunion.svkey);
1738 81           case KEY_TYPE_INT: return newSViv(item->keyunion.ikey);
1739 5           case KEY_TYPE_FLOAT: return newSVnv(item->keyunion.nkey);
1740 159           case KEY_TYPE_USTR: return newSVpvn_flags(item->keyunion.ckey, item->ckeylen, SVf_UTF8);
1741 1           case KEY_TYPE_BSTR: return newSVpvn(item->keyunion.ckey, item->ckeylen);
1742 0           default: croak("BUG: un-handled key_type");
1743             }
1744             }
1745              
1746             // Can't figure out how to create new CV instances on the fly...
1747             /*
1748             static SV* TreeRBXS_wrap_iter(pTHX_ struct TreeRBXS_iter *iter) {
1749             SV *obj;
1750             CV *iter_next_cv;
1751             MAGIC *magic;
1752             // Since this is used in typemap, handle NULL gracefully
1753             if (!iter)
1754             return &PL_sv_undef;
1755             // If there is already a node object, return a new reference to it.
1756             if (iter->owner)
1757             return newRV_inc(iter->owner);
1758             // else create an iterator
1759             iter_next_cv= get_cv("Tree::RB::XS::Iter::next", 0);
1760             if (!iter_next_cv) croak("BUG: can't find Iter->next");
1761             obj= newRV_noinc((SV*)cv_clone(iter_next_cv));
1762             sv_bless(obj, gv_stashpv("Tree::RB::XS::Iter", GV_ADD));
1763             magic= sv_magicext(SvRV(obj), NULL, PERL_MAGIC_ext, &TreeRBXS_iter_magic_vt, (const char*) iter, 0);
1764             #ifdef USE_ITHREADS
1765             magic->mg_flags |= MGf_DUP;
1766             #else
1767             (void)magic; // suppress warning
1768             #endif
1769             return obj;
1770             }
1771             */
1772              
1773             // Return the TreeRBXS_iter struct attached to a Perl object via MAGIC.
1774             // The 'obj' should be a reference to a blessed SV.
1775             // Use AUTOCREATE to attach magic and allocate a struct if it wasn't present.
1776             // Use OR_DIE for a built-in croak() if the return value would be NULL.
1777 2210           static struct TreeRBXS_iter* TreeRBXS_get_magic_iter(SV *obj, int flags) {
1778             SV *sv;
1779             MAGIC* magic;
1780             struct TreeRBXS_iter *iter;
1781 2210 50         if (!sv_isobject(obj)) {
1782 0 0         if (flags & OR_DIE)
1783 0           croak("Not an object");
1784 0           return NULL;
1785             }
1786 2210           sv= SvRV(obj);
1787 2210 50         if (SvMAGICAL(sv) && (magic= mg_findext(sv, PERL_MAGIC_ext, &TreeRBXS_iter_magic_vt)))
    100          
1788 1696           return (struct TreeRBXS_iter*) magic->mg_ptr;
1789              
1790 514 100         if (flags & AUTOCREATE) {
1791 257           Newxz(iter, 1, struct TreeRBXS_iter);
1792 257           magic= sv_magicext(sv, NULL, PERL_MAGIC_ext, &TreeRBXS_iter_magic_vt, (const char*) iter, 0);
1793             #ifdef USE_ITHREADS
1794             magic->mg_flags |= MGf_DUP;
1795             #endif
1796 257           iter->owner= sv;
1797 257           return iter;
1798             }
1799 257 50         else if (flags & OR_DIE)
1800 0           croak("Object lacks 'struct TreeRBXS_iter' magic");
1801 257           return NULL;
1802             }
1803              
1804             #define FUNCTION_IS_LVALUE(x) function_is_lvalue(aTHX_ stash, #x)
1805 65           static void function_is_lvalue(pTHX_ HV *stash, const char *name) {
1806             CV *method_cv;
1807             GV *method_gv;
1808 65 50         if (!(method_gv= gv_fetchmethod(stash, name))
1809 65 50         || !(method_cv= GvCV(method_gv)))
1810 0           croak("Missing method %s", name);
1811 65           CvLVALUE_on(method_cv);
1812 65           }
1813              
1814             #define EXPORT_ENUM(x) newCONSTSUB(stash, #x, new_enum_dualvar(aTHX_ x, newSVpvs_share(#x)))
1815 348           static SV * new_enum_dualvar(pTHX_ IV ival, SV *name) {
1816 348 50         SvUPGRADE(name, SVt_PVNV);
1817 348           SvIV_set(name, ival);
1818 348           SvIOK_on(name);
1819 348           SvREADONLY_on(name);
1820 348           return name;
1821             }
1822              
1823             /* Return an SV array of an AV.
1824             * Returns NULL if it wasn't an AV or arrayref.
1825             */
1826 39           static SV** unwrap_array(SV *array, IV *len) {
1827             AV *av;
1828             SV **vec;
1829             IV n;
1830 39 50         if (array && SvTYPE(array) == SVt_PVAV)
    100          
1831 3           av= (AV*) array;
1832 36 50         else if (array && SvROK(array) && SvTYPE(SvRV(array)) == SVt_PVAV)
    50          
    50          
1833 36           av= (AV*) SvRV(array);
1834             else
1835 0           return NULL;
1836 39           n= av_len(av) + 1;
1837 39           vec= AvARRAY(av);
1838             /* tied arrays and non-allocated empty arrays return NULL */
1839 39 100         if (!vec) {
1840 1 50         if (n == 0) /* don't return a NULL for an empty array, but doesn't need to be a real pointer */
1841 0           vec= (SV**) 8;
1842             else {
1843             /* in case of a tied array, extract the elements into a temporary buffer */
1844             IV i;
1845 1 50         Newx(vec, n, SV*);
1846 1           SAVEFREEPV(vec);
1847 11 100         for (i= 0; i < n; i++) {
1848 10           SV **el= av_fetch(av, i, 0);
1849 10 50         vec[i]= el? *el : NULL;
1850             }
1851             }
1852             }
1853 39 50         if (len) *len= n;
1854 39           return vec;
1855             }
1856              
1857             /* Initialize a new tree from settings in a list of perl SVs
1858             *
1859             * This functions expects that 'tree' has been suitably initialized so that if an attribute
1860             * does not occur in the list, it has already received a sensible default value.
1861             * The attr_list is assumed to be an array of mortal SVs such that they can be re-ordered
1862             * or removed freely, so that _init_tree can return the list of un-consumed attributes.
1863             * This means that attr_list should *NOT* be AvARRAY of an AV.
1864             * Returns the number of unknown attributes, which have been re-packed in the list.
1865             */
1866 111           IV init_tree_from_attr_list(
1867             struct TreeRBXS *tree,
1868             SV **attr_list,
1869             IV attr_list_len
1870             ) {
1871             IV i, out_i;
1872 111           SV *key_type_sv= NULL,
1873 111           *compare_fn_sv= NULL,
1874 111           *kv_sv= NULL,
1875 111           *keys_sv= NULL,
1876 111           *values_sv= NULL,
1877 111           *recent_sv= NULL,
1878 111           *hashiter_sv= NULL;
1879             int key_type;
1880 111           IV nodecount= 0;
1881             struct TreeRBXS_item *item, stack_item;
1882             rbtree_node_t *node;
1883              
1884             /* Begin iterating arguments, and store the SVs we know in variables, and put the
1885             * rest into the object hash. */
1886 249 100         for (i= out_i= 0; i < attr_list_len; i += 2) {
1887 138           SV *key= attr_list[i], *val;
1888             STRLEN len;
1889 138           const char *attrname= SvPV(key, len);
1890             /* every attribute needs a value */
1891 138 50         if (i + 1 == attr_list_len)
1892 0           croak("No value provided for %s", attrname);
1893 138           val= attr_list[i+1];
1894 138 50         if (!SvOK(val))
1895 0           val= NULL; /* prefer NULLs for unset attributes */
1896 138           switch (len) {
1897 27 50         case 2: if (strcmp("kv", attrname) == 0) { kv_sv= val; break; }
1898 0           else goto keep_unknown;
1899 5 50         case 4: if (strcmp("keys", attrname) == 0) { keys_sv= val; break; }
1900 0           else goto keep_unknown;
1901 3 100         case 6: if (strcmp("values", attrname) == 0) { values_sv= val; break; }
1902 1 50         else if (strcmp("recent", attrname) == 0) { recent_sv= val; break; }
1903 0           else goto keep_unknown;
1904 24 50         case 8: if (strcmp("key_type", attrname) == 0) { key_type_sv= val; break; }
1905 0 0         else if (strcmp("hashiter", attrname) == 0) { hashiter_sv= val; break; }
1906 0           else goto keep_unknown;
1907 56 50         case 10: if (strcmp("compare_fn", attrname) == 0) { compare_fn_sv= val; break; }
1908 0           else goto keep_unknown;
1909 10 100         case 12: if (strcmp("track_recent", attrname) == 0) { tree->track_recent= val && SvTRUE(val); break; }
    50          
    50          
1910 1 50         else if (strcmp("recent_limit", attrname) == 0) {
1911 1 50         tree->recent_limit= val && SvOK(val) && SvIV(val) >= 0? SvIV(val) : -1;
    50          
    50          
1912 1           break;
1913             }
1914 0           else goto keep_unknown;
1915 0 0         case 15: if (strcmp("compat_list_get", attrname) == 0) { tree->compat_list_get= val && SvTRUE(val); break; }
    0          
    0          
1916 0           else goto keep_unknown;
1917 10 50         case 16: if (strcmp("allow_duplicates", attrname) == 0) { tree->allow_duplicates= val && SvTRUE(val); break; }
    50          
    50          
1918 0           else goto keep_unknown;
1919 3 50         case 20: if (strcmp("keys_in_recent_order", attrname) == 0) { tree->keys_in_recent_order= val && SvTRUE(val); break; }
    50          
    50          
1920 0           else goto keep_unknown;
1921 0 0         case 21: if (strcmp("lookup_updates_recent", attrname) == 0) { tree->lookup_updates_recent= val && SvTRUE(val); break; }
    0          
    0          
1922              
1923 0           default: keep_unknown:
1924             /* unknown attribute. Re-pack it into the list */
1925 0 0         if (i > out_i) {
1926 0           attr_list[out_i]= attr_list[i];
1927 0           attr_list[out_i+1]= attr_list[i+1];
1928             }
1929 0           out_i += 2;
1930             }
1931             }
1932              
1933             // parse key type and compare_fn
1934 111 100         if (key_type_sv) {
1935 24           key_type= parse_key_type(key_type_sv);
1936 24 50         if (key_type < 0)
1937 0           croak("invalid key_type %s", SvPV_nolen(key_type_sv));
1938 24           tree->key_type= key_type;
1939             }
1940 87           else key_type= tree->key_type;
1941            
1942 111 100         if (compare_fn_sv) {
1943 56           int cmp_id= parse_cmp_fn(compare_fn_sv);
1944 56 50         if (cmp_id < 0)
1945 0           croak("invalid compare_fn %s", SvPV_nolen(compare_fn_sv));
1946 56           tree->compare_fn_id= cmp_id;
1947 55 100         } else if (key_type_sv) {
1948 21           tree->compare_fn_id=
1949             key_type == KEY_TYPE_INT? CMP_INT
1950 31 100         : key_type == KEY_TYPE_FLOAT? CMP_FLOAT
1951 17 100         : key_type == KEY_TYPE_BSTR? CMP_MEMCMP
1952 11 100         : key_type == KEY_TYPE_USTR? CMP_STR
1953 4 100         : key_type == KEY_TYPE_ANY? CMP_PERL /* use Perl's cmp operator */
1954             : key_type == KEY_TYPE_CLAIM? CMP_PERL
1955             : CMP_PERL;
1956             }
1957              
1958 111           switch (tree->compare_fn_id) {
1959 8           case CMP_SUB:
1960 8 50         if (!compare_fn_sv || !SvRV(compare_fn_sv) || SvTYPE(SvRV(compare_fn_sv)) != SVt_PVCV)
    50          
    50          
1961 0           croak("Can't set compare_fn to CMP_SUB without supplying a coderef");
1962 8           tree->compare_callback= compare_fn_sv;
1963 8           SvREFCNT_inc(tree->compare_callback);
1964 8 50         if (key_type != KEY_TYPE_CLAIM) tree->key_type= KEY_TYPE_ANY;
1965 8           tree->compare= TreeRBXS_cmp_perl_cb;
1966 8           break;
1967 12           case CMP_FOLDCASE:
1968 12           tree->transform= TreeRBXS_xform_fc;
1969 18           case CMP_STR:
1970 18           tree->key_type= KEY_TYPE_USTR;
1971 18           tree->compare= TreeRBXS_cmp_memcmp;
1972 18           break;
1973 38           case CMP_PERL:
1974 38 100         if (key_type != KEY_TYPE_CLAIM) tree->key_type= KEY_TYPE_ANY;
1975 38           tree->compare= TreeRBXS_cmp_perl;
1976 38           break;
1977 25           case CMP_INT:
1978 25           tree->key_type= KEY_TYPE_INT;
1979 25           tree->compare= TreeRBXS_cmp_int;
1980 25           break;
1981 7           case CMP_FLOAT:
1982 7           tree->key_type= KEY_TYPE_FLOAT;
1983 7           tree->compare= TreeRBXS_cmp_float;
1984 7           break;
1985 6           case CMP_MEMCMP:
1986 6           tree->key_type= KEY_TYPE_BSTR;
1987 6           tree->compare= TreeRBXS_cmp_memcmp;
1988 6           break;
1989 4           case CMP_NUMSPLIT_FOLDCASE:
1990 4           tree->key_type= KEY_TYPE_USTR;
1991 4           tree->transform= TreeRBXS_xform_fc;
1992 4           tree->compare= TreeRBXS_cmp_numsplit;
1993 4           break;
1994 5           case CMP_NUMSPLIT:
1995 5 100         if (key_type != KEY_TYPE_USTR && key_type != KEY_TYPE_ANY && key_type != KEY_TYPE_CLAIM)
    100          
    50          
1996 1           tree->key_type= KEY_TYPE_BSTR;
1997 5           tree->compare= TreeRBXS_cmp_numsplit;
1998 5           break;
1999 0           default:
2000 0           croak("BUG: unhandled cmp_id");
2001             }
2002              
2003             /* if keys and/or values supplied... */
2004 111 100         if (kv_sv || keys_sv || values_sv) {
    100          
    50          
2005 32           SV **key_vec= NULL, **key_lim, **val_vec= NULL;
2006 32           IV num_kv, key_step= 0, val_step= 0;
2007 32           bool track_recent= tree->track_recent;
2008            
2009 32 100         if (kv_sv) {
2010 27 50         if (keys_sv || values_sv)
    50          
2011 0           croak("'kv' cannot be specified at the same time as 'keys' or 'values'");
2012 27 50         if (!(key_vec= unwrap_array(kv_sv, &num_kv)))
2013 0           croak("'kv' must be an arrayref");
2014 27 50         if (num_kv & 1)
2015 0           croak("Odd number of elements in 'kv' array");
2016 27           num_kv >>= 1;
2017 27           val_vec= key_vec+1;
2018 27           key_step= val_step= 2;
2019             }
2020 32 100         if (keys_sv) {
2021 5 50         if (!(key_vec= unwrap_array(keys_sv, &num_kv)))
2022 0           croak("'keys' must be an arrayref");
2023 5           key_step= 1;
2024             }
2025 32 100         if (values_sv) {
2026             IV nvals;
2027 2 50         if (!key_vec)
2028 0           croak("'values' can't be specified without 'keys'");
2029 2 50         if (!(val_vec= unwrap_array(values_sv, &nvals)))
2030 0           croak("'values' must be an arrayref");
2031 2 50         if (nvals != num_kv)
2032 0           croak("Length of 'values' array (%ld) does not match keys (%ld)", (long)nvals, (long)num_kv);
2033 2           val_step= 1;
2034             }
2035             /* If recent list is about to be overwritten, don't track any insertions */
2036 32 100         if (recent_sv)
2037 1           tree->track_recent= false;
2038 186 100         for (key_lim= key_vec + num_kv * key_step; key_vec < key_lim; key_vec += key_step, val_vec += val_step) {
2039 154 100         TreeRBXS_init_tmp_item(&stack_item, tree, (*key_vec? *key_vec : &PL_sv_undef), (val_vec? *val_vec : &PL_sv_undef));
    50          
2040 154           TreeRBXS_insert_item(tree, &stack_item, !tree->allow_duplicates, NULL);
2041             }
2042             /* restore tracking setting */
2043 32           tree->track_recent= track_recent;
2044             /* might not equal num_kv if there were duplicates */
2045 32           nodecount= TreeRBXS_get_count(tree);
2046             }
2047             /* user wants to initialize the linked list of track_recent */
2048 111 100         if (recent_sv) {
2049             IV i, idx, n;
2050 1           SV **rvec= unwrap_array(recent_sv, &n);
2051 1 50         if (!rvec) croak("'recent' must be an arrayref");
2052 24 100         for (i= 0; i < n; i++) {
2053 23 50         if (!looks_like_integer(rvec[i]))
2054 0           croak("Elements of 'recent' must be integers");
2055 23           idx= SvIV(rvec[i]);
2056 23 50         if (idx < 0 || idx >= nodecount)
    50          
2057 0           croak("Element in 'recent' (%ld) is out of bounds (0-%ld)", (long)idx, (long)(nodecount-1));
2058 23           node= rbtree_node_child_at_index(TreeRBXS_get_root(tree), idx);
2059 23 50         if (!node) croak("BUG: access node[idx]");
2060 23           item= GET_TreeRBXS_item_FROM_rbnode(node);
2061 23           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
2062             }
2063             }
2064             /* hashiter_sv restores the state of tied-hash iteration */
2065 111 50         if (hashiter_sv) {
2066 0           struct TreeRBXS_iter *iter= TreeRBXS_get_hashiter(tree);
2067             IV idx;
2068 0 0         if (!looks_like_integer(hashiter_sv))
2069 0           croak("Expected integer for 'hashiter'");
2070 0           idx= SvIV(hashiter_sv);
2071 0 0         if (idx < 0 || idx >= nodecount)
    0          
2072 0           croak("'hashiter' value out of bounds");
2073 0           node= rbtree_node_child_at_index(TreeRBXS_get_root(tree), idx);
2074 0 0         if (!node) croak("BUG: access node[idx]");
2075 0           item= GET_TreeRBXS_item_FROM_rbnode(node);
2076 0           TreeRBXS_iter_set_item(iter, item);
2077             }
2078              
2079             /* return number of attribute (k,v) remaining in the supplied list */
2080 111           return out_i;
2081             }
2082              
2083 7           int get_integer_version() {
2084 7           SV *version= get_sv("Tree::RB::XS::VERSION", 0);
2085 7 50         if (!version || !SvOK(version))
    50          
2086 0           croak("$Tree::RB::XS::VERSION is not defined");
2087 7           return (int)(SvNV(version) * 1000000);
2088             }
2089              
2090             /*----------------------------------------------------------------------------
2091             * Tree Methods
2092             */
2093              
2094             MODULE = Tree::RB::XS PACKAGE = Tree::RB::XS
2095              
2096             void
2097             new(obj_or_pkg, ...)
2098             SV *obj_or_pkg
2099             ALIAS:
2100             Tree::RB::XS::TIEHASH = 0
2101             Tree::RB::XS::_init_tree = 1
2102             INIT:
2103 108           struct TreeRBXS *tree= NULL;
2104 108           SV *objref= NULL, **attr_list;
2105 108           HV *obj_hv= NULL, *pkg= NULL;
2106             IV n_unknown, i, attr_len;
2107             PPCODE:
2108 108 50         if (sv_isobject(obj_or_pkg) && SvTYPE(SvRV(obj_or_pkg)) == SVt_PVHV) {
    0          
2109 0           objref= obj_or_pkg;
2110 0           obj_hv= (HV*) SvRV(objref);
2111             }
2112 108 50         else if (SvPOK(obj_or_pkg) && (pkg= gv_stashsv(obj_or_pkg, 0))) {
    50          
2113 108 50         if (!sv_derived_from(obj_or_pkg, "Tree::RB::XS"))
2114 0           croak("Package %s does not derive from Tree:RB::XS", SvPV_nolen(obj_or_pkg));
2115 108           obj_hv= newHV();
2116 108           objref= sv_2mortal(newRV_noinc((SV*)obj_hv));
2117 108           sv_bless(objref, pkg);
2118 108           ST(0)= objref;
2119             }
2120             else
2121 0 0         croak("%s: first arg must be package name or blessed object", ix == 1? "_init_tree":"new");
2122            
2123             /* Special cases for 'new': it can be compare_fn, or a hashref */
2124 108 100         if (items == 2) {
2125 4           SV *first= ST(1);
2126             /* non-ref means a compare_fn constant, likewise for coderef */
2127 4 100         if (!SvROK(first) || SvTYPE(SvRV(first)) == SVt_PVCV) {
    50          
2128 3           Newx(attr_list, 2, SV*);
2129 3           SAVEFREEPV(attr_list);
2130 3           attr_list[0]= newSVpvs("compare_fn");
2131 3           attr_list[1]= first;
2132 3           attr_len= 2;
2133             }
2134 1 50         else if (SvTYPE(SvRV(first)) == SVt_PVHV) {
2135 1           HV *attrhv= (HV*) SvRV(first);
2136 1           IV n= hv_iterinit(attrhv);
2137             HE *ent;
2138 1           attr_len= n*2;
2139 1 50         Newx(attr_list, attr_len, SV*);
2140 1           SAVEFREEPV(attr_list);
2141 1           i= 0;
2142 4 100         while ((ent= hv_iternext(attrhv)) && i < attr_len) {
    50          
2143 3           attr_list[i++]= hv_iterkeysv(ent);
2144 3           attr_list[i++]= hv_iterval(attrhv, ent);
2145             }
2146             }
2147 0           else croak("Expected compare_fn constant, coderef, hashref, or key/value pairs");
2148             } else {
2149 104           attr_list= PL_stack_base+ax+1;
2150 104           attr_len= items - 1;
2151             }
2152              
2153             /* Upgrade this object to have TreeRBXS struct attached magically */
2154 108           tree= TreeRBXS_get_magic_tree(objref, AUTOCREATE|OR_DIE);
2155 108 50         if (tree->owner != (SV*) obj_hv)
2156 0           croak("Tree was already initialized");
2157              
2158 108           n_unknown= init_tree_from_attr_list(tree, attr_list, attr_len);
2159 108 50         if (n_unknown) {
2160             /* if called by the public constructor, throw an error */
2161 0 0         if (ix == 0)
2162 0           croak("Unknown attribute %s", SvPV_nolen(attr_list[0]));
2163             /* else return them to caller. They might already be in the stack. */
2164 0 0         if (attr_list != PL_stack_base+ax+1) {
2165 0 0         EXTEND(SP, n_unknown);
    0          
2166 0 0         for (i= 0; i < n_unknown; i++)
2167 0           ST(i+1)= attr_list[i];
2168             }
2169             }
2170 108 50         i= ix == 0? 1 : n_unknown;
2171 108           XSRETURN(i);
2172              
2173             void
2174             _assert_structure(tree)
2175             struct TreeRBXS *tree
2176             CODE:
2177 19           TreeRBXS_assert_structure(tree);
2178              
2179             void
2180             STORABLE_freeze(tree, cloning)
2181             struct TreeRBXS *tree
2182             bool cloning
2183             INIT:
2184 4           IV nodecount= TreeRBXS_get_count(tree);
2185 4           rbtree_node_t *node= rbtree_node_left_leaf(TreeRBXS_get_root(tree));
2186             struct TreeRBXS_item *item;
2187 4           int i, cmp_id= tree->compare_fn_id, key_type= tree->key_type,
2188 4           flags, version= get_integer_version();
2189             unsigned char sb[14];
2190 4           AV *attrs= newAV();
2191 4           SV *attrs_ref= sv_2mortal(newRV_noinc((SV*) attrs));
2192 4           HV *treehv= (HV*) tree->owner;
2193             HE *pos;
2194             PPCODE:
2195             /* dump out the contents of the hashref, which is empty unless set by subclass */
2196 4           hv_iterinit(treehv);
2197 4 50         while ((pos= hv_iternext(treehv))) {
2198 0           av_push(attrs, SvREFCNT_inc(hv_iterkeysv(pos)));
2199 0           av_push(attrs, newSVsv(hv_iterval(treehv, pos)));
2200             }
2201 4 50         if (tree->recent_limit >= 0) {
2202 0           av_push(attrs, newSVpvs("recent_limit"));
2203 0           av_push(attrs, newSViv(tree->recent_limit));
2204             }
2205              
2206             /* Build lists of keys and values */
2207 4 100         if (nodecount) {
2208             AV *keys_av, *values_av;
2209 2           SV *keys= NULL, *values= NULL;
2210             //IV *keys_ivec;
2211             //NV *keys_nvec;
2212             //bool all_one= true, all_undef= true;
2213 2           values_av= newAV();
2214 2           values= sv_2mortal(newRV_noinc((SV*) values_av));
2215 2           av_extend(values_av, nodecount-1);
2216             /* decide whether keys will be an AV, or packed in a buffer */
2217             //if (key_type == KEY_TYPE_INT) {
2218             // /* allocate a buffer of ints */
2219             // svtmp= make_aligned_buffer(NULL, sizeof(IV)*nodecount, sizeof(IV));
2220             // keys_ivec= (IV*) SvPVX(svtmp);
2221             // keys= newRV_noinc(svtmp);
2222             //} else if (key_type == KEY_TYPE_FLOAT) {
2223             // /* allocate a buffer of NV */
2224             // svtmp= make_aligned_buffer(NULL, sizeof(NV)*nodecount, sizeof(NV));
2225             // keys_nvec= (NV*) SvPVX(svtmp);
2226             // keys= newRV_noinc(svtmp);
2227             //} else {
2228 2           keys_av= newAV();
2229 2           keys= sv_2mortal(newRV_noinc((SV*) keys_av));
2230 2           av_extend(keys_av, nodecount-1);
2231             //}
2232             /* Now fill the key and value arrays */
2233 36 100         for (i= 0; i < nodecount && node; i++, node=rbtree_node_next(node)) {
    50          
2234 34           item= GET_TreeRBXS_item_FROM_rbnode(node);
2235             /* I think I need to populate this array with the exact SV from the tree so that
2236             * Storable can recognize repeat references that might live in the larger graph.
2237             * In normal circumstances the correct thing here is to newSVsv() so that
2238             * the array items aren't shared. */
2239 34           av_push(values_av, SvREFCNT_inc(item->value));
2240             /* for packed keys, write the existing buffer. else create a new SV */
2241             //switch (key_type) {
2242             //case KEY_TYPE_INT:
2243             // keys_ivec[i]= item->keyunion.ikey;
2244             // break;
2245             //case KEY_TYPE_FLOAT:
2246             // keys_nvec[i]= item->keyunion.nkey;
2247             // break;
2248             //case KEY_TYPE_ANY:
2249             //case KEY_TYPE_CLAIM: av_push(keys_av, SvREFCNT_inc(item->keyunion.svkey)); break;
2250             //default:
2251 34           av_push(keys_av, TreeRBXS_item_wrap_key(item));
2252             //}
2253             }
2254             /* sanity-check: ensure loop ended at expected count */
2255 2 50         if (i < nodecount) croak("BUG: too few nodes in tree");
2256 2 50         if (node) croak("BUG: too many nodes in tree");
2257             /* optimize key storage */
2258             //if (key_type == KEY_TYPE_INT) {
2259             // key_bits=
2260             //}
2261 2           av_push(attrs, newSVpvs("keys"));
2262 2           av_push(attrs, SvREFCNT_inc(keys));
2263 2           av_push(attrs, newSVpvs("values"));
2264 2           av_push(attrs, SvREFCNT_inc(values));
2265              
2266             /* if any nodes belong to the recent-list, emit that as an array of node indices */
2267 2 100         if (tree->recent_count) {
2268 1           struct dllist_node *root= &tree->recent, *pos= tree->recent.next;
2269 1           AV *recent_av= newAV();
2270 1           av_push(attrs, newSVpvs("recent"));
2271 1           av_push(attrs, newRV_noinc((SV*) recent_av));
2272 1           av_extend(recent_av, tree->recent_count-1);
2273 24 100         for (i= tree->recent_count; i > 0 && pos != root; --i, pos= pos->next) {
    50          
2274 23           item= GET_TreeRBXS_item_FROM_recent(pos);
2275 23           av_push(recent_av, newSViv(rbtree_node_index(&item->rbnode)));
2276             }
2277             /* sanity check */
2278 1 50         if (i != 0) croak("BUG: too few recent-tracked nodes");
2279 1 50         if (pos != root) croak("BUG: too many recent-tracked nodes");
2280             }
2281             /* and finally, the optional built-in iterator for when the tree is tied to a hash */
2282 2 50         if (tree->hashiter && tree->hashiter->item) {
    0          
2283             /* only need to initialize it if it is pointing somewhere other than the first
2284             * tree node. */
2285 0           IV pointing_at= rbtree_node_index(&tree->hashiter->item->rbnode);
2286 0 0         if (pointing_at) {
2287 0           av_push(attrs, newSVpvs("hashiter"));
2288 0           av_push(attrs, newSViv(pointing_at));
2289             }
2290             }
2291             }
2292             /* Attempting to clone a tree with a user-supplied callback comparison function will
2293             * fail, because Storable won't encode coderefs by default. This makes sense for
2294             * process-to-process serialization, but for dclone() I want to enable it so long as
2295             * the coderef is a global function. */
2296 4 100         if (cmp_id == CMP_SUB) {
2297 2 50         if (!cloning)
2298 0           croak("Can't serialize a Tree::RB::XS with a custom comparison coderef");
2299             else {
2300 2           CV *cv= (CV*) SvRV(tree->compare_callback);
2301 2           GV *gv= CvGV(cv), *re_gv;
2302 2           HV *stash= GvSTASH(gv);
2303 2           const char *name= GvNAME(gv);
2304            
2305 2 50         if (!stash || !name || !(re_gv= gv_fetchmethod(stash, name)) || GvCV(re_gv) != cv)
    50          
    100          
    50          
2306 1 50         croak("Comparison function (%s::%s) for Tree::RB::XS instance cannot be serialized unless it exists as a global package function",
    50          
    50          
    50          
    50          
    0          
    50          
    50          
2307             stash? HvNAME(stash) : "NULL",
2308             name? name : "NULL"
2309             );
2310 1           av_push(attrs, newSVpvs("compare_fn"));
2311 1 50         av_push(attrs, newSVpvf("%s::%s", HvNAME(stash), name));
    50          
    50          
    0          
    50          
    50          
2312             }
2313             }
2314              
2315 3 50         if (version < 0 || version > 0x7FFFFFFF)
2316 0           croak("BUG: version out of bounds");
2317 3           sb[0]= version & 0xFF;
2318 3           sb[1]= (version >> 8) & 0xFF;
2319 3           sb[2]= (version >> 16) & 0xFF;
2320 3           sb[3]= (version >> 24) & 0xFF;
2321 3 50         if (key_type < 1 || key_type > 255)
    50          
2322 0           croak("BUG: key_type out of bounds");
2323 3           sb[4]= key_type & 0xFF;
2324 3           sb[5]= (key_type >> 8) & 0xFF;
2325 3 50         if (cmp_id < 1 || cmp_id > 255)
    50          
2326 0           croak("BUG: compare_fn outof bounds");
2327 3           sb[6]= cmp_id & 0xFF;
2328 3           sb[7]= (cmp_id >> 8) & 0xFF;
2329 6           flags= (tree->allow_duplicates? 1 : 0)
2330 3 50         | (tree->compat_list_get? 2 : 0)
2331 3 100         | (tree->track_recent? 4 : 0)
2332 3 50         | (tree->lookup_updates_recent? 8 : 0)
2333 3 50         | (tree->keys_in_recent_order? 0x10 : 0);
2334 3           sb[8]= flags & 0xFF;
2335 3           sb[9]= (flags >> 8) & 0xFF;
2336              
2337 3 50         EXTEND(SP, 2);
2338 3           ST(0)= sv_2mortal(newSVpvn((char*)sb, 10));
2339 3           ST(1)= attrs_ref;
2340 3           XSRETURN(2);
2341              
2342             void
2343             STORABLE_thaw(objref, cloning, serialized, attrs)
2344             SV *objref
2345             bool cloning
2346             SV *serialized
2347             AV *attrs
2348             INIT:
2349 3           struct TreeRBXS *tree= NULL;
2350             int version, cmp_id, key_type, flags, i;
2351             IV attr_len, n_unknown;
2352             const unsigned char *sb;
2353             STRLEN sb_len;
2354 3           SV **attr_vec= unwrap_array((SV*)attrs, &attr_len), **attr_list, *tmpsv;
2355             PPCODE:
2356 3 50         if (!SvROK(objref) || SvTYPE(SvRV(objref)) != SVt_PVHV)
    50          
2357 0           croak("Expected blessed hashref as first argument");
2358 3           tree= TreeRBXS_get_magic_tree(objref, AUTOCREATE|OR_DIE);
2359 3 50         if (tree->owner != SvRV(objref))
2360 0           croak("Tree was already initialized");
2361            
2362             /* unpack serialized fields */
2363 3           sb= (const unsigned char*) SvPV(serialized, sb_len);
2364 3 50         if (sb_len < 10)
2365 0           croak("Expected at least 10 bytes of serialized data");
2366 3           version= sb[0] + (sb[1] << 8) + (sb[2] << 16) + (sb[3] << 24); // 4 bytes LE
2367 3           key_type= sb[4] + (sb[5] << 8); // 2 bytes LE
2368 3           cmp_id= sb[6] + (sb[7] << 8); // 2 bytes LE
2369 3           flags= sb[8] + (sb[9] << 8); // 2 bytes LE
2370              
2371 3 50         if (version <= 0)
2372 0           croak("Invalid serialized version");
2373 3 50         if (version > get_integer_version())
2374 0           croak("Attempt to deserialize Tree::RB::XS from a newer version");
2375             /* STORABLE_freeze lists nodes exactly as they were, so alllow duplicates if present,
2376             * regardless of the final state of the allow_duplicates attribute. */
2377 3           tree->allow_duplicates= /* flags & 1 */ true; /* corrected below */
2378 3           tree->compat_list_get= flags & 2;
2379 3           tree->track_recent= flags & 4;
2380 3           tree->lookup_updates_recent= flags & 8;
2381 3           tree->keys_in_recent_order= flags & 0x10;
2382              
2383 3 50         if (key_type <= 0 || key_type > KEY_TYPE_MAX)
    50          
2384 0           croak("Invalid serialized key_type");
2385 3           tree->key_type= key_type;
2386              
2387 3 50         if (cmp_id <= 0 || cmp_id > CMP_MAX)
    50          
2388 0           croak("Invalid serialized compare_fn");
2389 3           tree->compare_fn_id= cmp_id;
2390             /* These two comparison function codes imply a transform function */
2391 3 50         if (cmp_id == CMP_FOLDCASE || cmp_id == CMP_NUMSPLIT_FOLDCASE)
    50          
2392 0           tree->transform= TreeRBXS_xform_fc;
2393              
2394             /* attr_vec gets modified, so make a copy of attrs' AvARRAY */
2395 3 50         Newx(attr_list, attr_len, SV*);
2396 3           SAVEFREEPV(attr_list);
2397 3           memcpy(attr_list, attr_vec, sizeof(SV*) * attr_len);
2398              
2399             /* If the comparison function is a coderef, try to look up the name of the function */
2400 3 100         if (cmp_id == CMP_SUB) {
2401 1 50         if (!cloning) croak("compare_fn lookup is forbidden unless cloning");
2402             /* look for attribute compare_fn, which is probably the final one */
2403 1 50         for (i= attr_len-2; i >= 0; i-= 2) {
2404 1 50         if (strcmp(SvPV_nolen(attr_list[i]), "compare_fn") == 0) {
2405             /* replace function name with coderef */
2406 1           GV *gv= gv_fetchsv(attr_list[i+1], 0, SVt_PVCV);
2407 1 50         if (gv && GvCV(gv))
    50          
2408 1           attr_list[i+1]= sv_2mortal(newRV_inc((SV*) GvCV(gv)));
2409             else
2410 0           croak("Can't find function %s", SvPV_nolen(attr_list[i+1]));
2411 1           break;
2412             }
2413             }
2414 1 50         if (i < 0)
2415 0           croak("No compare_fn name found in serialized data");
2416             }
2417              
2418 3           n_unknown= init_tree_from_attr_list(tree, attr_list, attr_len);
2419 3 50         if (n_unknown) {
2420 0           HV *obj= (HV*) SvRV(objref);
2421             /* store leftovers into the hashref of the object */
2422 0 0         for (i= 0; i < n_unknown-1; i += 2)
2423 0 0         if (!hv_store_ent(obj, attr_list[i], (tmpsv= newSVsv(attr_list[i+1])), 0))
2424 0           sv_2mortal(tmpsv);
2425             }
2426 3           tree->allow_duplicates= flags & 1; /* delayed for reason above */
2427 3           XSRETURN(0);
2428              
2429             void
2430             key_type(tree)
2431             struct TreeRBXS *tree
2432             INIT:
2433 20           int kt= tree->key_type;
2434             PPCODE:
2435 20           ST(0)= sv_2mortal(new_enum_dualvar(aTHX_ kt, newSVpv(get_key_type_name(kt), 0)));
2436 20           XSRETURN(1);
2437              
2438             void
2439             compare_fn(tree)
2440             struct TreeRBXS *tree
2441             INIT:
2442 18           int id= tree->compare_fn_id;
2443             PPCODE:
2444 18           ST(0)= id == CMP_SUB? tree->compare_callback
2445 18 100         : sv_2mortal(new_enum_dualvar(aTHX_ id, newSVpv(get_cmp_name(id), 0)));
2446 18           XSRETURN(1);
2447              
2448             void
2449             allow_duplicates(tree, allow= NULL)
2450             struct TreeRBXS *tree
2451             SV* allow
2452             PPCODE:
2453 15 100         if (items > 1) {
2454 4           tree->allow_duplicates= SvTRUE(allow);
2455             // ST(0) is $self, so let it be the return value
2456             } else {
2457 11           ST(0)= sv_2mortal(newSViv(tree->allow_duplicates? 1 : 0));
2458             }
2459 15           XSRETURN(1);
2460              
2461             void
2462             compat_list_get(tree, allow= NULL)
2463             struct TreeRBXS *tree
2464             SV* allow
2465             PPCODE:
2466 2 50         if (items > 1) {
2467 0           tree->compat_list_get= SvTRUE(allow);
2468             // ST(0) is $self, so let it be the return value
2469             } else {
2470 2 50         ST(0)= tree->compat_list_get? &PL_sv_yes : &PL_sv_no;
2471             }
2472 2           XSRETURN(1);
2473              
2474             void
2475             track_recent(tree, enable= NULL)
2476             struct TreeRBXS *tree
2477             SV* enable
2478             PPCODE:
2479 2 50         if (items > 1) {
2480 0           tree->track_recent= SvTRUE(enable);
2481             // ST(0) is $self, so let it be the return value
2482             } else {
2483 2 50         ST(0)= tree->track_recent? &PL_sv_yes : &PL_sv_no;
2484             }
2485 2           XSRETURN(1);
2486              
2487             void
2488             lookup_updates_recent(tree, enable= NULL)
2489             struct TreeRBXS *tree
2490             SV *enable
2491             PPCODE:
2492 3 100         if (items > 1) {
2493 1           tree->lookup_updates_recent= SvTRUE(enable);
2494             //ST(0) is $self, so let it be the return value
2495             } else {
2496 2 50         ST(0)= tree->lookup_updates_recent? &PL_sv_yes : &PL_sv_no;
2497             }
2498 3           XSRETURN(1);
2499              
2500             void
2501             keys_in_recent_order(tree, enable= NULL)
2502             struct TreeRBXS *tree
2503             SV *enable
2504             PPCODE:
2505 1 50         if (items > 1) {
2506 1           tree->keys_in_recent_order= SvTRUE(enable);
2507             //ST(0) is $self, so let it be the return value
2508             } else {
2509 0 0         ST(0)= tree->keys_in_recent_order? &PL_sv_yes : &PL_sv_no;
2510             }
2511 1           XSRETURN(1);
2512              
2513             IV
2514             size(tree)
2515             struct TreeRBXS *tree
2516             CODE:
2517 56 50         RETVAL= TreeRBXS_get_count(tree);
2518             OUTPUT:
2519             RETVAL
2520              
2521             IV
2522             recent_count(tree)
2523             struct TreeRBXS *tree;
2524             CODE:
2525 9 50         RETVAL= tree->recent_count;
2526             OUTPUT:
2527             RETVAL
2528              
2529             void
2530             recent_limit(tree, newval=NULL)
2531             struct TreeRBXS *tree
2532             SV *newval
2533             PPCODE:
2534 4 100         if (newval) {
2535 2 100         if (!SvOK(newval)) {
2536 1           tree->recent_limit= -1;
2537 1 50         } else if (!looks_like_number(newval) || SvIV(newval) < 0) {
    50          
2538 0           croak("Expected non-negative integer");
2539             } else {
2540 1           tree->recent_limit= SvIV(newval);
2541 1           TreeRBXS_truncate_recent(tree, tree->recent_limit, NULL);
2542             }
2543             //ST(0) is $self, so let it be the return value
2544             } else {
2545 2           ST(0)= tree->recent_limit < 0? &PL_sv_undef
2546 2 100         : sv_2mortal(newSViv(tree->recent_limit));
2547             }
2548 4           XSRETURN(1);
2549              
2550             void
2551             _insert_optimization_debug(tree)
2552             struct TreeRBXS *tree;
2553             PPCODE:
2554 1 50         EXTEND(SP, 5);
2555 1           PUSHs(sv_2mortal(newSViv(tree->prev_inserted_trend)));
2556 1           PUSHs(sv_2mortal(newSViv(INSERT_TREND_TRIGGER)));
2557 1           PUSHs(sv_2mortal(newSViv(INSERT_TREND_CAP)));
2558 1           PUSHs(sv_2mortal(newSViv(tree->prev_inserted_dup? 1 : 0)));
2559              
2560             void
2561             insert(tree, key, val=&PL_sv_undef)
2562             struct TreeRBXS *tree
2563             SV *key
2564             SV *val
2565             ALIAS:
2566             Tree::RB::XS::put = 1
2567             Tree::RB::XS::STORE = 1
2568             Tree::RB::XS::insert_as_node = 2
2569             Tree::RB::XS::put_as_node = 3
2570             INIT:
2571             struct TreeRBXS_item stack_item, *inserted;
2572 500341           SV *oldval= NULL;
2573             PPCODE:
2574             //TreeRBXS_assert_structure(tree);
2575 500341 50         if (!SvOK(key))
2576 0           croak("Can't use undef as a key");
2577 500341           TreeRBXS_init_tmp_item(&stack_item, tree, key, val);
2578 500341           inserted= TreeRBXS_insert_item(tree, &stack_item, (ix & 1), &oldval);
2579 100133 100         ST(0)= ix == 0? sv_2mortal(newSViv(inserted? rbtree_node_index(&inserted->rbnode) : -1))
2580 1300751 100         : ix == 1? (oldval? oldval : &PL_sv_undef)
    100          
2581 800410 100         : (inserted? sv_2mortal(TreeRBXS_wrap_item(inserted)) : &PL_sv_undef);
    100          
2582 500341           XSRETURN(1);
2583              
2584             IV
2585             insert_multi(tree, ...)
2586             struct TreeRBXS *tree
2587             ALIAS:
2588             Tree::RB::XS::put_multi = 1
2589             INIT:
2590             struct TreeRBXS_item stack_item, *inserted;
2591 4           AV *av= NULL;
2592             SV *key, *val, *oldval, **el;
2593 4           int added= 0, i, lim;
2594             CODE:
2595             // Is there exactly one element which is an un-blessed arrayref?
2596 4 50         if (items == 2 && SvROK(ST(1)) && SvTYPE(SvRV(ST(1))) == SVt_PVAV && !sv_isobject(ST(1))) {
    0          
    0          
    0          
2597 0           av= (AV*) SvRV(ST(1));
2598 0           i= 0;
2599 0           lim= av_len(av)+1;
2600             } else {
2601 4           i= 1;
2602 4           lim= items;
2603             }
2604             // Iterate either the array or the stack
2605 519 100         while (i < lim) {
2606 515           val= &PL_sv_undef;
2607             // either iterating an array, or iterating the stack
2608 515 50         if (av) {
2609 0           el= av_fetch(av, i, 0);
2610 0 0         if (!el) croak("Tree->insert_multi does not support tied or sparse arrays");
2611 0           key= *el;
2612 0           i++;
2613 0 0         if (i < lim) {
2614 0           el= av_fetch(av, i, 0);
2615 0 0         if (!el) croak("Tree->insert_multi does not support tied or sparse arrays");
2616 0           val= *el;
2617 0           i++;
2618             }
2619             } else {
2620 515           key= ST(i);
2621 515 50         if (++i < lim) {
2622 515           val= ST(i);
2623 515           i++;
2624             }
2625             }
2626 515 50         if (!SvOK(key))
2627 0           croak("Can't use undef as a key");
2628 515           TreeRBXS_init_tmp_item(&stack_item, tree, key, val);
2629 515           oldval= NULL;
2630 515           inserted= TreeRBXS_insert_item(tree, &stack_item, ix == 1, &oldval);
2631             // Count the newly added nodes. For insert, that is the number of non-null 'inserted'.
2632             // For put, that is the number of inserts that did not return an old value.
2633 515 100         if (ix == 0? (inserted != NULL) : (oldval == NULL))
    100          
2634 507           ++added;
2635             }
2636 4 50         RETVAL= added;
2637             OUTPUT:
2638             RETVAL
2639              
2640             IV
2641             exists(tree, ...)
2642             struct TreeRBXS *tree
2643             ALIAS:
2644             Tree::RB::XS::EXISTS = 1
2645             INIT:
2646             struct TreeRBXS_item stack_item;
2647             rbtree_node_t *node;
2648             SV *key;
2649             int i, cmp;
2650 4           size_t count, total= 0;
2651             CODE:
2652             (void) ix; /* unused */
2653 8 100         for (i= 1; i < items; i++) {
2654 4           key= ST(i);
2655 4           TreeRBXS_init_tmp_item(&stack_item, tree, key, &PL_sv_undef);
2656 4 100         if (tree->allowed_duplicates) {
2657 1           count= 0;
2658 1           rbtree_find_all(&tree->root_sentinel,
2659             &stack_item,
2660 1           (int(*)(void*,void*,void*)) tree->compare,
2661             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2662             NULL, NULL, &count);
2663 1           total += count;
2664             } else {
2665 3           node= rbtree_find_nearest(
2666             &tree->root_sentinel,
2667             &stack_item,
2668 3           (int(*)(void*,void*,void*)) tree->compare,
2669             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2670             &cmp);
2671 3 100         if (node && cmp == 0)
    50          
2672 2           ++total;
2673             }
2674             }
2675 4 50         RETVAL= total;
2676             OUTPUT:
2677             RETVAL
2678              
2679             void
2680             get(tree, key, mode_sv= NULL)
2681             struct TreeRBXS *tree
2682             SV *key
2683             SV *mode_sv
2684             ALIAS:
2685             Tree::RB::XS::get_node = 0x00
2686             Tree::RB::XS::get_key = 0x01
2687             Tree::RB::XS::key = 0x01
2688             Tree::RB::XS::FETCH = 0x02
2689             Tree::RB::XS::lookup = 0x03
2690             Tree::RB::XS::get = 0x04
2691             Tree::RB::XS::get_node_ge = 0x10
2692             Tree::RB::XS::get_key_ge = 0x11
2693             Tree::RB::XS::get_node_le = 0x20
2694             Tree::RB::XS::get_key_le = 0x21
2695             Tree::RB::XS::get_node_gt = 0x30
2696             Tree::RB::XS::get_key_gt = 0x31
2697             Tree::RB::XS::get_node_lt = 0x40
2698             Tree::RB::XS::get_key_lt = 0x41
2699             Tree::RB::XS::get_node_last = 0x70
2700             Tree::RB::XS::get_node_le_last = 0x80
2701             Tree::RB::XS::get_or_add = 0x92
2702             INIT:
2703             struct TreeRBXS_item stack_item, *item;
2704 368           int mode= 0, n= 0;
2705             PPCODE:
2706 368 50         if (!SvOK(key))
2707 0           croak("Can't use undef as a key");
2708             // Extract the comparison enum from ix, or read it from mode_sv
2709 368 100         if (ix >> 4) {
2710 6           mode= (ix >> 4);
2711 6           ix &= 0xF;
2712 6 50         if (mode_sv)
2713 0           croak("extra get-mode argument");
2714             } else {
2715 362 100         mode= mode_sv? parse_lookup_mode(mode_sv) : GET_EQ;
2716 362 50         if (mode < 0)
2717 0           croak("Invalid lookup mode %s", SvPV_nolen(mode_sv));
2718             }
2719             // In "full compatibility mode", 'get' is identical to 'lookup' and depends on list context.
2720             // In scalar context, they both become the same as FETCH
2721 368 100         if (ix >= 3)
2722 249 100         ix= (GIMME_V == G_SCALAR || (ix == 4 && !tree->compat_list_get))? 2 : 3;
    100          
    50          
2723             // From here,
2724             // ix = 0 : return node
2725             // ix = 1 : return key
2726             // ix = 2 : return value
2727             // ix = 3 : return (value, node)
2728              
2729             // create a fake item to act as a search key
2730 368           TreeRBXS_init_tmp_item(&stack_item, tree, key, &PL_sv_undef);
2731 368           item= TreeRBXS_find_item(tree, &stack_item, mode);
2732 368 100         if (item) {
2733 337 100         if (tree->lookup_updates_recent)
2734 1           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
2735 337 50         if (GIMME_V == G_VOID)
2736 0           n= 0;
2737 337 100         else if (ix <= 1) {
2738 48 100         if (ix == 0) { // return node
2739 38           ST(0)= sv_2mortal(TreeRBXS_wrap_item(item));
2740 38           n= 1;
2741             } else { // return key
2742 10           ST(0)= sv_2mortal(TreeRBXS_item_wrap_key(item));
2743 10           n= 1;
2744             }
2745 289 100         } else if (ix == 2) { // return value
2746 273           ST(0)= item->value;
2747 273           n= 1;
2748             } else { // return (value, node)
2749 16           ST(0)= item->value;
2750 16           ST(1)= sv_2mortal(TreeRBXS_wrap_item(item));
2751 16           n= 2;
2752             }
2753             } else {
2754 31           ST(0)= &PL_sv_undef;
2755 31           n= (ix == 3)? 0 : 1; // empty list, else single undef
2756             }
2757 368           XSRETURN(n);
2758              
2759             void
2760             get_all(tree, key)
2761             struct TreeRBXS *tree
2762             SV *key
2763             INIT:
2764             struct TreeRBXS_item stack_item, *item;
2765             rbtree_node_t *first;
2766             size_t count, i;
2767             PPCODE:
2768 1 50         if (!SvOK(key))
2769 0           croak("Can't use undef as a key");
2770 1           TreeRBXS_init_tmp_item(&stack_item, tree, key, &PL_sv_undef);
2771 1 50         if (rbtree_find_all(
2772             &tree->root_sentinel,
2773             &stack_item,
2774 1           (int(*)(void*,void*,void*)) tree->compare,
2775             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2776             &first, NULL, &count)
2777             ) {
2778 1 50         EXTEND(SP, count);
2779 7 100         for (i= 0; i < count; i++) {
2780 6           item= GET_TreeRBXS_item_FROM_rbnode(first);
2781 6           ST(i)= item->value;
2782 6 50         if (tree->lookup_updates_recent)
2783 0           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
2784 6           first= rbtree_node_next(first);
2785             }
2786             } else
2787 0           count= 0;
2788 1           XSRETURN(count);
2789              
2790             IV
2791             delete(tree, key1, key2= NULL)
2792             struct TreeRBXS *tree
2793             SV *key1
2794             SV *key2
2795             INIT:
2796             struct TreeRBXS_item stack_item, *item;
2797             rbtree_node_t *first, *last, *node;
2798             size_t count, i;
2799             CODE:
2800 24 50         if (!SvOK(key1))
2801 0           croak("Can't use undef as a key");
2802 24           RETVAL= 0;
2803 24 50         if ((item= TreeRBXS_get_magic_item(key1, 0))) {
2804 0 0         if (!TreeRBXS_is_member(tree, item))
2805 0           croak("Node does not belong to this tree");
2806             }
2807             else {
2808 24           TreeRBXS_init_tmp_item(&stack_item, tree, key1, &PL_sv_undef);
2809 24 100         if (rbtree_find_all(
2810             &tree->root_sentinel,
2811             &stack_item,
2812 24           (int(*)(void*,void*,void*)) tree->compare,
2813             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2814             &first, &last, &count)
2815             ) {
2816 21 100         if (key2)
2817 1           last= NULL;
2818             }
2819             else {
2820             // Didn't find any matches. But if range is given, then start deleting
2821             // from the node following the key
2822 3 100         if (key2) {
2823 2           first= last;
2824 2           last= NULL;
2825             }
2826             }
2827             }
2828             // If a range is given, and the first part of the range found a node,
2829             // look for the end of the range.
2830 24 100         if (key2 && first) {
    50          
2831 3 50         if ((item= TreeRBXS_get_magic_item(key2, 0))) {
2832 0 0         if (!TreeRBXS_is_member(tree, item))
2833 0           croak("Node does not belong to this tree");
2834             }
2835             else {
2836 3           TreeRBXS_init_tmp_item(&stack_item, tree, key2, &PL_sv_undef);
2837 3 100         if (rbtree_find_all(
2838             &tree->root_sentinel,
2839             &stack_item,
2840 3           (int(*)(void*,void*,void*)) tree->compare,
2841             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2842             &node, &last, NULL)
2843             ) {
2844             // first..last is ready to be deleted
2845             } else {
2846             // didn't match, so 'node' holds the final element before the key
2847 2           last= node;
2848             }
2849             }
2850             // Ensure that first comes before last
2851 3 50         if (last && rbtree_node_index(first) > rbtree_node_index(last))
    100          
2852 1           last= NULL;
2853             }
2854             // Delete the nodes if constructed a successful range
2855 24           i= 0;
2856 24 50         if (first && last) {
    100          
2857             do {
2858 25           item= GET_TreeRBXS_item_FROM_rbnode(first);
2859 25 100         first= (first == last)? NULL : rbtree_node_next(first);
2860 25           TreeRBXS_item_detach_tree(item, tree);
2861 25           ++i;
2862 25 100         } while (first);
2863             }
2864 24 100         RETVAL= i;
2865             OUTPUT:
2866             RETVAL
2867              
2868             void
2869             rekey(tree, ...)
2870             struct TreeRBXS *tree
2871             INIT:
2872 23           struct TreeRBXS_item *min_item= NULL, *max_item= NULL;
2873 23           SV *min_sv= NULL, *max_sv= NULL, *offset_sv= NULL;
2874             int i;
2875             PPCODE:
2876             /* Look for parameters named:
2877             offset
2878             min
2879             max
2880             */
2881 56 100         for (i= 1; i < items; i++) {
2882             STRLEN len;
2883 33           char *opt_name= SvPV(ST(i), len);
2884 33 50         if (++i >= items)
2885 0           croak("Expected value for key '%s'", opt_name);
2886 33           switch (len) {
2887 10           case 3:
2888 10           switch (opt_name[1]) {
2889 3 50         case 'a': if (strncmp(opt_name, "max", len) == 0) { max_sv= ST(i); continue; }
2890 7 50         case 'i': if (strncmp(opt_name, "min", len) == 0) { min_sv= ST(i); continue; }
2891             }
2892 0           break;
2893 23           case 6:
2894 23 50         if (strncmp(opt_name, "offset", len) == 0) { offset_sv= ST(i); continue; }
2895             }
2896 0           croak("Unknown option '%s'", opt_name);
2897             }
2898             /* Nothing to do for an empty tree */
2899 23 100         if (!TreeRBXS_get_count(tree))
2900 1           goto done;
2901             /* Translate min/max from iterators or keys to the nearest affected nodes */
2902 22 100         if (min_sv) {
2903 7           min_item= TreeRBXS_get_magic_item(min_sv, 0);
2904 7 100         if (!min_item) {
2905             struct TreeRBXS_iter *it;
2906 6 100         if (SvROK(min_sv) && (it= TreeRBXS_get_magic_iter(min_sv, 0))) {
    50          
2907 1           min_item= it->item;
2908 1 50         if (!min_item) croak("Iterator for 'min' is not referencing a tree node");
2909             }
2910             else {
2911             struct TreeRBXS_item stack_item;
2912 5           TreeRBXS_init_tmp_item(&stack_item, tree, min_sv, &PL_sv_undef);
2913 5           min_item= TreeRBXS_find_item(tree, &stack_item, GET_GE);
2914             }
2915             }
2916             }
2917 22 100         if (max_sv) {
2918 3           max_item= TreeRBXS_get_magic_item(max_sv, 0);
2919 3 100         if (!max_item) {
2920             struct TreeRBXS_iter *it;
2921 2 100         if (SvROK(max_sv) && (it= TreeRBXS_get_magic_iter(max_sv, 0))) {
    50          
2922 1           max_item= it->item;
2923 1 50         if (!max_item) croak("Iterator for 'max' is not referencing a tree node");
2924             }
2925             else {
2926             struct TreeRBXS_item stack_item;
2927 1           TreeRBXS_init_tmp_item(&stack_item, tree, max_sv, &PL_sv_undef);
2928 1           max_item= TreeRBXS_find_item(tree, &stack_item, GET_LE);
2929             }
2930             }
2931             }
2932 22 50         if (offset_sv) {
2933 22           int intmode= 0, positive= 0;
2934             IV offset_iv;
2935             NV offset_nv;
2936 22           struct TreeRBXS_item *first_item= GET_TreeRBXS_item_FROM_rbnode(rbtree_node_left_leaf(TreeRBXS_get_root(tree)));
2937 22           struct TreeRBXS_item *last_item= GET_TreeRBXS_item_FROM_rbnode(rbtree_node_right_leaf(TreeRBXS_get_root(tree)));
2938             struct TreeRBXS_item *edge_item;
2939             rbtree_node_t *boundary_node;
2940 22           rbtree_node_t* (*node_seek[2])(rbtree_node_t *)= { rbtree_node_prev, rbtree_node_next };
2941             /* offset can only be used for integer or floating point keys */
2942 22 100         if (tree->key_type == KEY_TYPE_INT) {
2943 18           intmode= 1;
2944             /* I could create a whole branch of UV comparisons and handling, but I don't feel like it */
2945 18 100         if (SvUOK(offset_sv) && SvUV(offset_sv) > IV_MAX)
    50          
2946 1           croak("Unsigned values larger than can fit in a signed IV are not supported. Patches welcome.");
2947 17           offset_iv= SvIV(offset_sv);
2948 17 50         if (offset_iv == 0)
2949 0           goto done;
2950             else
2951 17           positive= offset_iv > 0? 1 : 0;
2952             /* For integers, ensure there isn't an overflow */
2953 17 100         if (positive) {
2954 11 100         IV max_key= (max_item? max_item : last_item)->keyunion.ikey;
2955 11 100         if (IV_MAX - offset_iv < max_key)
2956 1           croak("Integer overflow when adding this offset (%"IVdf") to the maximum key (%"IVdf")", offset_iv, max_key);
2957             } else {
2958 6 100         IV min_key= (min_item? min_item : first_item)->keyunion.ikey;
2959 6 100         if (IV_MIN - offset_iv > min_key)
2960 1           croak("Integer overflow when adding this offset (%"IVdf") to the maximum key (%"IVdf")", offset_iv, min_key);
2961             }
2962             }
2963 4 100         else if (tree->key_type == KEY_TYPE_FLOAT) {
2964 2           offset_nv= SvNV(offset_sv);
2965 2 50         if (offset_nv == 0.0)
2966 0           goto done;
2967             else
2968 2           positive= offset_nv > 0? 1 : 0;
2969             }
2970 2           else croak("Option 'offset' may only be used on trees with integer or floating point numeric keys");
2971              
2972             /* If keys are increasing, compare max modified vs. node to the right of that.
2973             * If keys are decreasing, compare min modified vs. node to the left of that.
2974             * To prevent redundant code, use the 'positive' flag to indicate rightward
2975             * or leftward comparisons. The 'edge_item' is the one that needs checked for
2976             * collisions with its stationary neighbors, the first of thich is 'boundary_item'..
2977             */
2978 17 100         edge_item= positive? max_item : min_item;
2979 17 100         if (edge_item && (boundary_node= node_seek[positive](&edge_item->rbnode))) {
    100          
2980 4           void (*node_insert[2])(rbtree_node_t *parent, rbtree_node_t *child)=
2981             { rbtree_node_insert_before, rbtree_node_insert_after };
2982             struct TreeRBXS_item
2983 4           *boundary_item= GET_TreeRBXS_item_FROM_rbnode(boundary_node),
2984 1 50         *final_item= (positive? (min_item? min_item : first_item)
2985 5 100         : (max_item? max_item : last_item)),
    50          
2986             stack_item;
2987 4           TreeRBXS_init_tmp_item(&stack_item, tree, &PL_sv_no, &PL_sv_undef);
2988 5           while (1) {
2989 9 50         if (intmode) {
2990 9           IV newval= edge_item->keyunion.ikey + offset_iv;
2991 17 100         if (positive? (newval < boundary_item->keyunion.ikey)
    100          
2992 8           : (newval > boundary_item->keyunion.ikey)
2993 4           ) break; /* no longer overlappig boundary */
2994 5           stack_item.keyunion.ikey= newval;
2995             } else {
2996 0           NV newval= edge_item->keyunion.nkey + offset_nv;
2997 0 0         if (positive? (newval < boundary_item->keyunion.nkey)
    0          
2998 0           : (newval > boundary_item->keyunion.nkey)
2999 0           ) break; /* no longer overlappig boundary */
3000 0           stack_item.keyunion.nkey= newval;
3001             }
3002             /* There is an overlap. Perform a prune + insert. This can't re-use
3003             * the standard insertion code for nodes because that makes assumptions
3004             * that the tree is changing size, where in this case the size remains
3005             * the same. Also this code intentionally doesn't modify the 'recent'
3006             * order.
3007             *
3008             * In the spirit of preserving order, make sure this element inserts closest
3009             * to the boundary_item of any duplicates, so use rbtree_find_all to get the
3010             * leftmost/rightmost match, or else the nearest node to insert under.
3011             *
3012             * Also, wait until the last minute to perform the prune operation so
3013             * that if there is a perl exception in a compare function we don't leak
3014             * the node.
3015             */
3016 5           rbtree_node_t *next= node_seek[1-positive](&edge_item->rbnode);
3017             rbtree_node_t *search[2];
3018 5           bool found_identical= rbtree_find_all(
3019             &tree->root_sentinel,
3020             &stack_item,
3021 5           (int(*)(void*,void*,void*)) tree->compare,
3022             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
3023             &search[0], &search[1], NULL);
3024             /* remove */
3025 5           rbtree_node_prune(&edge_item->rbnode);
3026             /* alter key */
3027 5 50         if (intmode) edge_item->keyunion.ikey= stack_item.keyunion.ikey;
3028 0           else edge_item->keyunion.nkey= stack_item.keyunion.nkey;
3029 5 100         if (found_identical) {
3030             /* insert-before leftmost, or insert-after rightmost */
3031 1           node_insert[1-positive](search[1-positive], &edge_item->rbnode);
3032             /* remove conflicting nodes, if not permitted */
3033 1 50         if (!tree->allow_duplicates) {
3034 1           rbtree_node_t *node= search[0];
3035             do {
3036 1           struct TreeRBXS_item *item= GET_TreeRBXS_item_FROM_rbnode(node);
3037 1 50         node= (node == search[1])? NULL : rbtree_node_next(node);
3038 1 50         if (item != edge_item)
3039 1           TreeRBXS_item_detach_tree(item, tree);
3040 1 50         } while (node);
3041             /* boundary item may have just been deleted */
3042 1           boundary_item= GET_TreeRBXS_item_FROM_rbnode(node_seek[positive](next));
3043             }
3044             }
3045 4 100         else if (search[0])
3046 3           rbtree_node_insert_after(search[0], &edge_item->rbnode);
3047             else
3048 1           rbtree_node_insert_before(search[1], &edge_item->rbnode);
3049 5 50         if (!next || edge_item == final_item)
    50          
3050 0           goto done;
3051 5           edge_item= GET_TreeRBXS_item_FROM_rbnode(next);
3052             }
3053             /* The loop above ends as soon as there is no more overlap, or skips to end of
3054             * function when there are no more nodes to move. The code below will continue
3055             * modifying keys under the assumption that nothing collides and the tree
3056             * structure remains unchanged.
3057             */
3058 4 100         *(positive? &max_item : &min_item)= edge_item;
3059             }
3060             {
3061 17 100         rbtree_node_t *node= min_item? &min_item->rbnode : &first_item->rbnode;
3062 17 100         rbtree_node_t *end= max_item? &max_item->rbnode : &last_item->rbnode;
3063             while (1) {
3064 41 100         if (intmode)
3065 36           GET_TreeRBXS_item_FROM_rbnode(node)->keyunion.ikey += offset_iv;
3066             else
3067 5           GET_TreeRBXS_item_FROM_rbnode(node)->keyunion.nkey += offset_nv;
3068 41 100         if (node == end) break;
3069 24           node= rbtree_node_next(node);
3070             }
3071             }
3072             }
3073             else {
3074             /* In the future, other modes of altering keys could be available */
3075 0           croak("offset not specified");
3076             }
3077            
3078 18           done:
3079 18           XSRETURN(1); /* return self for chaining */
3080              
3081             void
3082             truncate_recent(tree, max_count)
3083             struct TreeRBXS *tree
3084             IV max_count
3085             INIT:
3086             struct dllist_node *cur, *next;
3087             struct TreeRBXS_item *item;
3088 0 0         bool keep= !(GIMME_V == G_VOID || GIMME_V == G_SCALAR);
    0          
3089 0           IV i, n= 0;
3090             PPCODE:
3091             // prune oldest nodes if exceeded limit
3092 0 0         if (max_count >= 0 && tree->recent_count > max_count) {
    0          
3093 0           n= tree->recent_count - max_count;
3094 0 0         if (keep) {
3095 0 0         EXTEND(SP, n);
    0          
3096 0           TreeRBXS_truncate_recent(tree, max_count, &ST(0));
3097 0           XSRETURN(n);
3098             } else {
3099 0           TreeRBXS_truncate_recent(tree, max_count, NULL);
3100             }
3101             }
3102 0 0         if (GIMME_V == G_SCALAR)
3103 0           PUSHs(sv_2mortal(newSViv(n)));
3104             /* else return empty list */
3105              
3106             IV
3107             clear(tree)
3108             struct TreeRBXS *tree
3109             ALIAS:
3110             Tree::RB::XS::CLEAR = 1
3111             CODE:
3112             (void) ix; /* unused */
3113 3           RETVAL= TreeRBXS_get_count(tree);
3114 3           TreeRBXS_clear(tree);
3115             OUTPUT:
3116             RETVAL
3117              
3118             struct TreeRBXS_item *
3119             min_node(tree)
3120             struct TreeRBXS *tree
3121             INIT:
3122 23           rbtree_node_t *node= rbtree_node_left_leaf(TreeRBXS_get_root(tree));
3123             CODE:
3124 23           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3125             OUTPUT:
3126             RETVAL
3127              
3128             struct TreeRBXS_item *
3129             max_node(tree)
3130             struct TreeRBXS *tree
3131             INIT:
3132 10           rbtree_node_t *node= rbtree_node_right_leaf(TreeRBXS_get_root(tree));
3133             CODE:
3134 10           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3135             OUTPUT:
3136             RETVAL
3137              
3138             struct TreeRBXS_item *
3139             nth_node(tree, ofs)
3140             struct TreeRBXS *tree
3141             IV ofs
3142             INIT:
3143             rbtree_node_t *node;
3144             CODE:
3145 15 100         if (ofs < 0) ofs += TreeRBXS_get_count(tree);
3146 15           node= rbtree_node_child_at_index(TreeRBXS_get_root(tree), ofs);
3147 15           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3148             OUTPUT:
3149             RETVAL
3150              
3151             struct TreeRBXS_item *
3152             root_node(tree)
3153             struct TreeRBXS *tree
3154             CODE:
3155 8           RETVAL= !TreeRBXS_get_count(tree)? NULL
3156 8 50         : GET_TreeRBXS_item_FROM_rbnode(TreeRBXS_get_root(tree));
3157             OUTPUT:
3158             RETVAL
3159              
3160             struct TreeRBXS_item *
3161             oldest_node(tree, newval= NULL)
3162             struct TreeRBXS *tree
3163             struct TreeRBXS_item *newval
3164             CODE:
3165 13 100         if (newval) {
3166 1 50         if (!TreeRBXS_is_member(tree, newval))
3167 0           croak("Node does not belong to this tree");
3168 1           TreeRBXS_recent_insert_before(tree, newval, tree->recent.next);
3169             }
3170 13           RETVAL= tree->recent.next == &tree->recent? NULL
3171 13 100         : GET_TreeRBXS_item_FROM_recent(tree->recent.next);
3172             OUTPUT:
3173             RETVAL
3174              
3175             struct TreeRBXS_item *
3176             newest_node(tree, newval= NULL)
3177             struct TreeRBXS *tree
3178             struct TreeRBXS_item *newval
3179             CODE:
3180 19 100         if (newval) {
3181 1 50         if (!TreeRBXS_is_member(tree, newval))
3182 0           croak("Node does not belong to this tree");
3183 1           TreeRBXS_recent_insert_before(tree, newval, &tree->recent);
3184             }
3185 19           RETVAL= tree->recent.prev == &tree->recent? NULL
3186 19 100         : GET_TreeRBXS_item_FROM_recent(tree->recent.prev);
3187             OUTPUT:
3188             RETVAL
3189              
3190             void
3191             keys(tree)
3192             struct TreeRBXS *tree
3193             ALIAS:
3194             Tree::RB::XS::values = 1
3195             Tree::RB::XS::kv = 2
3196             Tree::RB::XS::reverse_keys = 4
3197             Tree::RB::XS::reverse_values = 5
3198             Tree::RB::XS::reverse_kv = 6
3199             INIT:
3200 80 100         size_t n_ret, i, node_count= tree->keys_in_recent_order? tree->recent_count : TreeRBXS_get_count(tree);
3201 80           bool reverse= (ix & 4),
3202 80           keys_only= (ix & 3) == 0,
3203 80           values_only= (ix & 3) == 1,
3204 80           want_kv= (ix & 3) == 2;
3205             PPCODE:
3206 80 50         if (GIMME_V == G_VOID) {
3207 0           n_ret= 0;
3208             }
3209 80 50         else if (GIMME_V == G_SCALAR) {
3210 0           n_ret= 1;
3211 0           ST(0)= sv_2mortal(newSViv(node_count));
3212             }
3213             else {
3214 80 100         n_ret= want_kv? node_count*2 : node_count;
3215 80 50         EXTEND(SP, n_ret);
3216 80 100         if (tree->keys_in_recent_order) {
3217 8 100         struct dllist_node *node= reverse? tree->recent.prev : tree->recent.next;
3218 8           struct dllist_node *limit= &tree->recent;
3219 8 100         if (keys_only) {
3220 11 100         for (i= 0; i < n_ret && node != limit; i++, node= reverse? node->prev : node->next)
    50          
3221 8 100         ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_recent(node)));
3222             }
3223 5 100         else if (values_only) {
3224 8 100         for (i= 0; i < n_ret && node != limit; i++, node= reverse? node->prev : node->next)
    50          
3225 6 100         ST(i)= GET_TreeRBXS_item_FROM_recent(node)->value;
3226             }
3227             else {
3228 11 100         for (i= 0; i < n_ret && node != limit; node= reverse? node->prev : node->next) {
    50          
3229 8           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_recent(node)));
3230 8           i++;
3231 8           ST(i)= GET_TreeRBXS_item_FROM_recent(node)->value;
3232 8 100         i++;
3233             }
3234             }
3235 8 50         if (node != limit) i++; // for error reporting
3236             }
3237             else {
3238 72 50         rbtree_node_t *node= (reverse? rbtree_node_right_leaf : rbtree_node_left_leaf)(TreeRBXS_get_root(tree));
3239 72 50         rbtree_node_t *(*step)(rbtree_node_t *)= reverse? rbtree_node_prev : rbtree_node_next;
3240 72 100         if (keys_only) {
3241 430 100         for (i= 0; i < n_ret && node; i++, node= step(node))
    50          
3242 374           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_rbnode(node)));
3243             }
3244 16 50         else if (values_only) {
3245 0 0         for (i= 0; i < n_ret && node; i++, node= step(node))
    0          
3246 0           ST(i)= GET_TreeRBXS_item_FROM_rbnode(node)->value;
3247             }
3248             else {
3249 72 100         for (i= 0; i < n_ret && node; node= step(node)) {
    50          
3250 56           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_rbnode(node)));
3251 56           i++;
3252 56           ST(i)= GET_TreeRBXS_item_FROM_rbnode(node)->value;
3253 56           i++;
3254             }
3255             }
3256 72 50         if (node) i++; // for error reporting
3257             }
3258 80 50         if (i != n_ret)
3259 0 0         croak("BUG: expected %ld nodes but found %ld",
3260             (long) node_count,
3261             (long) (want_kv? ((i+1)>>1) : i));
3262             }
3263 80           XSRETURN(n_ret);
3264              
3265             SV *
3266             FIRSTKEY(tree)
3267             struct TreeRBXS *tree
3268             INIT:
3269 15           struct TreeRBXS_iter *iter= TreeRBXS_get_hashiter(tree);
3270             CODE:
3271 15 100         if (tree->hashiterset)
3272 1           tree->hashiterset= false; // iter has 'hseek' applied, don't change it
3273             else {
3274 14           iter->recent= tree->keys_in_recent_order;
3275 14           TreeRBXS_iter_rewind(iter);
3276             }
3277 15           RETVAL= TreeRBXS_item_wrap_key(iter->item); // handles null by returning undef
3278             OUTPUT:
3279             RETVAL
3280              
3281             SV *
3282             NEXTKEY(tree, lastkey)
3283             struct TreeRBXS *tree
3284             SV *lastkey
3285             INIT:
3286 61           struct TreeRBXS_iter *iter= TreeRBXS_get_hashiter(tree);
3287             CODE:
3288 61 100         if (tree->hashiterset)
3289 2           tree->hashiterset= false; // iter has 'hseek' applied, don't change it
3290             else
3291 59           TreeRBXS_iter_advance(iter, 1);
3292 61           RETVAL= TreeRBXS_item_wrap_key(iter->item);
3293             (void)lastkey;
3294             OUTPUT:
3295             RETVAL
3296              
3297             void
3298             _set_hashiter(tree, item_sv, reverse)
3299             struct TreeRBXS *tree
3300             SV *item_sv
3301             bool reverse
3302             INIT:
3303 3           struct TreeRBXS_item *item= TreeRBXS_get_magic_item(item_sv, 0);
3304 3           struct TreeRBXS_iter *iter= TreeRBXS_get_hashiter(tree);
3305             PPCODE:
3306 3 100         if (item && (TreeRBXS_item_get_tree(item) != tree))
    50          
3307 0           croak("Node is not part of this tree");
3308 3           iter->reverse= reverse;
3309 3           iter->recent= tree->keys_in_recent_order;
3310 3           TreeRBXS_iter_set_item(iter, item);
3311 3 100         if (!item) TreeRBXS_iter_rewind(iter);
3312 3           tree->hashiterset= true;
3313 3           XSRETURN(0);
3314              
3315             SV *
3316             SCALAR(tree)
3317             struct TreeRBXS *tree
3318             CODE:
3319 6           RETVAL= newSViv(TreeRBXS_get_count(tree));
3320             OUTPUT:
3321             RETVAL
3322              
3323             void
3324             DELETE(tree, key)
3325             struct TreeRBXS *tree
3326             SV *key
3327             INIT:
3328             struct TreeRBXS_item stack_item, *item;
3329             rbtree_node_t *first, *last;
3330             PPCODE:
3331 3 50         if (!SvOK(key))
3332 0           croak("Can't use undef as a key");
3333 3           TreeRBXS_init_tmp_item(&stack_item, tree, key, &PL_sv_undef);
3334 3 100         if (rbtree_find_all(
3335             &tree->root_sentinel,
3336             &stack_item,
3337 3           (int(*)(void*,void*,void*)) tree->compare,
3338             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
3339             &first, &last, NULL)
3340             ) {
3341 2           ST(0)= sv_2mortal(SvREFCNT_inc(GET_TreeRBXS_item_FROM_rbnode(first)->value));
3342             do {
3343 2           item= GET_TreeRBXS_item_FROM_rbnode(first);
3344 2 50         first= (first == last)? NULL : rbtree_node_next(first);
3345 2           TreeRBXS_item_detach_tree(item, tree);
3346 2 50         } while (first);
3347             } else {
3348 1           ST(0)= &PL_sv_undef;
3349             }
3350 3           XSRETURN(1);
3351              
3352             IV
3353             cmp_numsplit(key_a, key_b)
3354             SV *key_a
3355             SV *key_b
3356             INIT:
3357             const char *apos, *bpos;
3358             STRLEN alen, blen;
3359 67           bool a_utf8= false, b_utf8= false;
3360             CODE:
3361             #if PERL_VERSION_LT(5,14,0)
3362             // before 5.14, need to force both to utf8 if either are utf8
3363             if (SvUTF8(key_a) || SvUTF8(key_b)) {
3364             apos= SvPVutf8(key_a, alen);
3365             bpos= SvPVutf8(key_b, blen);
3366             a_utf8= b_utf8= true;
3367             } else
3368             #else
3369             // After 5.14, can compare utf8 with bytes without converting the buffer
3370 67           a_utf8= SvUTF8(key_a);
3371 67           b_utf8= SvUTF8(key_b);
3372             #endif
3373             {
3374 67           apos= SvPV(key_a, alen);
3375 67           bpos= SvPV(key_b, blen);
3376             }
3377 67 100         RETVAL= cmp_numsplit(apos, apos+alen, a_utf8, bpos, bpos+blen, b_utf8);
3378             OUTPUT:
3379             RETVAL
3380              
3381             #-----------------------------------------------------------------------------
3382             # Node Methods
3383             #
3384              
3385             MODULE = Tree::RB::XS PACKAGE = Tree::RB::XS::Node
3386              
3387             SV *
3388             key(item, newval=NULL)
3389             struct TreeRBXS_item *item
3390             SV *newval
3391             CODE:
3392 202 100         if (newval) {
3393 47           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3394             struct TreeRBXS_item stack_item;
3395 47           TreeRBXS_init_tmp_item(&stack_item, tree, newval, &PL_sv_undef);
3396 47           TreeRBXS_item_set_key(&item, &stack_item);
3397             }
3398             /* semi-expensive if just setting key, so check for void context */
3399 404           RETVAL= (GIMME_V == G_VOID)? &PL_sv_undef
3400 202 100         : TreeRBXS_item_wrap_key(item);
3401             OUTPUT:
3402             RETVAL
3403              
3404             SV *
3405             value(item, newval=NULL)
3406             struct TreeRBXS_item *item
3407             SV *newval
3408             CODE:
3409 51 50         if (newval)
3410 0           sv_setsv(item->value, newval);
3411 51           RETVAL= SvREFCNT_inc_simple_NN(item->value);
3412             OUTPUT:
3413             RETVAL
3414              
3415             IV
3416             index(item)
3417             struct TreeRBXS_item *item
3418             CODE:
3419 0 0         RETVAL= rbtree_node_index(&item->rbnode);
3420             OUTPUT:
3421             RETVAL
3422              
3423             struct TreeRBXS_item *
3424             prev(item)
3425             struct TreeRBXS_item *item
3426             INIT:
3427 9           rbtree_node_t *node= rbtree_node_prev(&item->rbnode);
3428             CODE:
3429 9           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3430             OUTPUT:
3431             RETVAL
3432              
3433             struct TreeRBXS_item *
3434             next(item)
3435             struct TreeRBXS_item *item
3436             INIT:
3437 56           rbtree_node_t *node= rbtree_node_next(&item->rbnode);
3438             CODE:
3439 56           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3440             OUTPUT:
3441             RETVAL
3442              
3443             struct TreeRBXS_item *
3444             newer(item, newval=NULL)
3445             struct TreeRBXS_item *item
3446             struct TreeRBXS_item *newval
3447             INIT:
3448 12           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3449 12           struct dllist_node *next= item->recent.next;
3450             CODE:
3451 12 100         if (newval) {
3452 1 50         if (!next)
3453 0           croak("Can't insert relative to a node that isn't recent_tracked");
3454 1 50         if (!tree) croak("Node was removed from tree");
3455 1           TreeRBXS_recent_insert_before(tree, newval, next);
3456 1           next= &newval->recent;
3457             }
3458 17 50         RETVAL= (!next || !tree || next == &tree->recent)? NULL
    50          
3459 17 100         : GET_TreeRBXS_item_FROM_recent(next);
3460             OUTPUT:
3461             RETVAL
3462              
3463             struct TreeRBXS_item *
3464             older(item, newval=NULL)
3465             struct TreeRBXS_item *item
3466             struct TreeRBXS_item *newval
3467             INIT:
3468 14           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3469 14           struct dllist_node *prev= item->recent.prev;
3470             CODE:
3471 14 100         if (newval) {
3472 1 50         if (!prev)
3473 0           croak("Can't insert relative to a node that isn't recent_tracked");
3474 1 50         if (!tree) croak("Node was removed from tree");
3475 1           TreeRBXS_recent_insert_before(tree, newval, &item->recent);
3476 1           prev= &newval->recent;
3477             }
3478 21 50         RETVAL= (!prev || !tree || prev == &tree->recent)? NULL
    50          
3479 21 100         : GET_TreeRBXS_item_FROM_recent(prev);
3480             OUTPUT:
3481             RETVAL
3482              
3483             struct TreeRBXS_item *
3484             parent(item)
3485             struct TreeRBXS_item *item
3486             CODE:
3487 6 50         RETVAL= rbtree_node_is_in_tree(&item->rbnode) && item->rbnode.parent->count?
3488 6 100         GET_TreeRBXS_item_FROM_rbnode(item->rbnode.parent) : NULL;
3489             OUTPUT:
3490             RETVAL
3491              
3492             void
3493             tree(item)
3494             struct TreeRBXS_item *item
3495             INIT:
3496 15           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3497             PPCODE:
3498 15 100         ST(0)= tree && tree->owner? sv_2mortal(newRV_inc(tree->owner)) : &PL_sv_undef;
    50          
3499 15           XSRETURN(1);
3500              
3501             struct TreeRBXS_item *
3502             left(item)
3503             struct TreeRBXS_item *item
3504             CODE:
3505 20 100         RETVAL= rbtree_node_is_in_tree(&item->rbnode) && item->rbnode.left->count?
3506 20 100         GET_TreeRBXS_item_FROM_rbnode(item->rbnode.left) : NULL;
3507             OUTPUT:
3508             RETVAL
3509              
3510             struct TreeRBXS_item *
3511             right(item)
3512             struct TreeRBXS_item *item
3513             CODE:
3514 20 100         RETVAL= rbtree_node_is_in_tree(&item->rbnode) && item->rbnode.right->count?
3515 20 100         GET_TreeRBXS_item_FROM_rbnode(item->rbnode.right) : NULL;
3516             OUTPUT:
3517             RETVAL
3518              
3519             struct TreeRBXS_item *
3520             left_leaf(item)
3521             struct TreeRBXS_item *item
3522             INIT:
3523 2           rbtree_node_t *node= rbtree_node_left_leaf(&item->rbnode);
3524             CODE:
3525 2           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3526             OUTPUT:
3527             RETVAL
3528              
3529             struct TreeRBXS_item *
3530             right_leaf(item)
3531             struct TreeRBXS_item *item
3532             INIT:
3533 2           rbtree_node_t *node= rbtree_node_right_leaf(&item->rbnode);
3534             CODE:
3535 2           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3536             OUTPUT:
3537             RETVAL
3538              
3539             IV
3540             color(item)
3541             struct TreeRBXS_item *item
3542             CODE:
3543 5 100         RETVAL= item->rbnode.color;
3544             OUTPUT:
3545             RETVAL
3546              
3547             IV
3548             count(item)
3549             struct TreeRBXS_item *item
3550             CODE:
3551 5 50         RETVAL= item->rbnode.count;
3552             OUTPUT:
3553             RETVAL
3554              
3555             IV
3556             prune(item)
3557             struct TreeRBXS_item *item
3558             INIT:
3559 6           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3560             CODE:
3561 6           RETVAL= 0;
3562 6 100         if (tree) {
3563 5           TreeRBXS_item_detach_tree(item, tree);
3564 5           RETVAL= 1;
3565             }
3566             OUTPUT:
3567             RETVAL
3568              
3569             void
3570             recent_tracked(item, newval=NULL)
3571             struct TreeRBXS_item *item
3572             SV* newval
3573             INIT:
3574             struct TreeRBXS *tree;
3575             PPCODE:
3576 14 100         if (items > 1) {
3577 10           tree= TreeRBXS_item_get_tree(item);
3578 10 50         if (!tree) croak("Node was removed from tree");
3579 10 50         if (SvTRUE(newval))
3580 0           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
3581             else
3582 10           TreeRBXS_recent_prune(tree, item);
3583             // ST(0) is $self, so let it be the return value
3584             } else {
3585 4           ST(0)= sv_2mortal(newSViv(item->recent.next? 1 : 0));
3586             }
3587 14           XSRETURN(1);
3588              
3589             void
3590             mark_newest(item)
3591             struct TreeRBXS_item *item
3592             INIT:
3593 2           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3594             PPCODE:
3595 2 50         if (!tree) croak("Node was removed from tree");
3596 2           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
3597 2           XSRETURN(1);
3598              
3599             void
3600             mark_oldest(item)
3601             struct TreeRBXS_item *item
3602             INIT:
3603 0           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3604             PPCODE:
3605 0 0         if (!tree) croak("Node was removed from tree");
3606 0           TreeRBXS_recent_insert_before(tree, item, tree->recent.next);
3607 0           XSRETURN(1);
3608              
3609             void
3610             STORABLE_freeze(item, cloning)
3611             struct TreeRBXS_item *item
3612             bool cloning
3613             INIT:
3614 3           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3615             AV *av;
3616             PPCODE:
3617             (void) cloning; /* unused */
3618 3 100         if (tree) {
3619 2           ST(0)= sv_2mortal(newSViv(rbtree_node_index(&item->rbnode)));
3620 2           ST(1)= sv_2mortal(newRV_inc(tree->owner));
3621             } else {
3622 1           ST(0)= sv_2mortal(newSViv(-1));
3623 1           ST(1)= sv_2mortal(newRV_noinc((SV*)(av= newAV())));
3624 1           av_push(av, TreeRBXS_item_wrap_key(item));
3625 1           av_push(av, SvREFCNT_inc(item->value));
3626             }
3627 3           XSRETURN(2);
3628              
3629             void
3630             STORABLE_thaw(item_sv, cloning, idx, refs)
3631             SV *item_sv
3632             bool cloning
3633             IV idx
3634             SV *refs
3635             INIT:
3636             struct TreeRBXS *tree;
3637             struct TreeRBXS_item *item;
3638             rbtree_node_t *node;
3639             MAGIC *magic;
3640             PPCODE:
3641             (void) cloning; /* unused */
3642 3 100         if (idx == -1) {
3643             IV n;
3644 1           SV **svec= unwrap_array(refs, &n);
3645 1 50         if (!svec || n != 2 || !svec[0] || !svec[1])
    50          
    50          
    50          
3646 0           croak("Expected arrayref of (key,value)");
3647 1           Newx(item, 1, struct TreeRBXS_item);
3648 1           memset(item, 0, sizeof(*item));
3649 1           item->key_type= KEY_TYPE_ANY;
3650 1           item->keyunion.svkey= SvREFCNT_inc(svec[0]);
3651 1           item->value= SvREFCNT_inc(svec[1]);
3652             } else {
3653 2           tree= TreeRBXS_get_magic_tree(refs, OR_DIE);
3654 2 50         if (!(node= rbtree_node_child_at_index(TreeRBXS_get_root(tree), idx)))
3655 0           croak("Tree does not have element %ld", (long) idx);
3656 2           item= GET_TreeRBXS_item_FROM_rbnode(node);
3657 2 50         if (item->owner) {
3658 0 0         if (item->owner != SvRV(item_sv))
3659 0           croak("BUG: Storable deserialized tree node multiple times");
3660 0           return;
3661             }
3662             }
3663 3           item->owner= SvRV(item_sv);
3664 3           magic= sv_magicext(item->owner, NULL, PERL_MAGIC_ext, &TreeRBXS_item_magic_vt, (const char*) item, 0);
3665 3           #ifdef USE_ITHREADS
3666             magic->mg_flags |= MGf_DUP;
3667 3           #else
3668             (void)magic; // suppress warning
3669             #endif
3670             XSRETURN(0);
3671              
3672             #-----------------------------------------------------------------------------
3673             # Iterator methods
3674             #
3675              
3676             MODULE = Tree::RB::XS PACKAGE = Tree::RB::XS::Iter
3677              
3678             void
3679             _init(iter_sv, target, direction= 1)
3680             SV *iter_sv
3681             SV *target
3682             IV direction
3683             INIT:
3684 257           struct TreeRBXS_iter *iter2, *iter= TreeRBXS_get_magic_iter(iter_sv, AUTOCREATE|OR_DIE);
3685             struct TreeRBXS *tree;
3686 257           struct TreeRBXS_item *item= NULL;
3687 257           rbtree_node_t *node= NULL;
3688             struct dllist_node *lnode;
3689             PPCODE:
3690 257 50         if (iter->item || iter->tree)
    50          
3691 0           croak("Iterator is already initialized");
3692 257           switch (direction) {
3693 5           case -2: iter->recent= 1; iter->reverse= 1; break;
3694 110           case -1: iter->recent= 0; iter->reverse= 1; break;
3695 132           case 1: iter->recent= 0; iter->reverse= 0; break;
3696 10           case 2: iter->recent= 1; iter->reverse= 0; break;
3697 0           default: croak("Invalid direction code");
3698             }
3699              
3700             // target can be a tree, a node, or another iterator
3701 257 50         if ((iter2= TreeRBXS_get_magic_iter(target, 0))) {
3702             // use this direction unless overridden
3703 0 0         if (items < 2) {
3704 0           iter->reverse= iter2->reverse;
3705 0           iter->recent= iter2->recent;
3706             }
3707 0           tree= iter2->tree;
3708 0           item= iter2->item;
3709             }
3710 257 100         else if ((item= TreeRBXS_get_magic_item(target, 0))) {
3711 17           tree= TreeRBXS_item_get_tree(item);
3712             }
3713 240 50         else if ((tree= TreeRBXS_get_magic_tree(target, 0))) {
3714 240 100         if (iter->recent) {
3715 13 100         lnode= iter->reverse? tree->recent.prev : tree->recent.next;
3716 13           item= (lnode == &tree->recent)? NULL
3717 13 50         : GET_TreeRBXS_item_FROM_recent(lnode);
3718             }
3719             else {
3720 454           node= !TreeRBXS_get_count(tree)? NULL
3721 454 50         : iter->reverse? rbtree_node_right_leaf(TreeRBXS_get_root(tree))
3722 227 100         : rbtree_node_left_leaf(TreeRBXS_get_root(tree));
3723 227 50         if (node)
3724 227           item= GET_TreeRBXS_item_FROM_rbnode(node);
3725             }
3726             }
3727 257 50         if (!tree)
3728 0           croak("Can't iterate a node that isn't in the tree");
3729 257 100         if (iter->recent && item && !item->recent.next)
    50          
    50          
3730 0           croak("Can't perform insertion-order iteration on a node that isn't tracked");
3731 257           iter->tree= tree;
3732 257 50         if (tree->owner)
3733 257           SvREFCNT_inc(tree->owner);
3734 257           TreeRBXS_iter_set_item(iter, item);
3735 257           ST(0)= iter_sv;
3736 257           XSRETURN(1);
3737              
3738             SV *
3739             key(iter)
3740             struct TreeRBXS_iter *iter
3741             CODE:
3742             // wrap_key handles NULL items
3743 38           RETVAL= TreeRBXS_item_wrap_key(iter->item);
3744             OUTPUT:
3745             RETVAL
3746              
3747             SV *
3748             value(iter)
3749             struct TreeRBXS_iter *iter
3750             CODE:
3751 35 100         RETVAL= iter->item? SvREFCNT_inc_simple_NN(iter->item->value) : &PL_sv_undef;
3752             OUTPUT:
3753             RETVAL
3754              
3755             struct TreeRBXS_item *
3756             node(iter)
3757             struct TreeRBXS_iter *iter
3758             CODE:
3759 18           RETVAL= iter->item;
3760             OUTPUT:
3761             RETVAL
3762              
3763             SV *
3764             index(iter)
3765             struct TreeRBXS_iter *iter
3766             CODE:
3767 1 0         RETVAL= !iter->item || !rbtree_node_is_in_tree(&iter->item->rbnode)? &PL_sv_undef
3768 1 50         : newSViv(rbtree_node_index(&iter->item->rbnode));
3769             OUTPUT:
3770             RETVAL
3771              
3772             SV *
3773             tree(iter)
3774             struct TreeRBXS_iter *iter
3775             CODE:
3776 0 0         RETVAL= iter->tree && iter->tree->owner? newRV_inc(iter->tree->owner) : &PL_sv_undef;
    0          
3777             OUTPUT:
3778             RETVAL
3779              
3780             bool
3781             done(iter)
3782             struct TreeRBXS_iter *iter
3783             CODE:
3784 46 100         RETVAL= !iter->item;
3785             OUTPUT:
3786             RETVAL
3787              
3788             void
3789             next(iter, count_sv= NULL)
3790             struct TreeRBXS_iter *iter
3791             SV* count_sv
3792             ALIAS:
3793             Tree::RB::XS::Iter::next = 0
3794             Tree::RB::XS::Iter::next_key = 1
3795             Tree::RB::XS::Iter::next_keys = 1
3796             Tree::RB::XS::Iter::next_value = 2
3797             Tree::RB::XS::Iter::next_values = 2
3798             Tree::RB::XS::Iter::next_kv = 3
3799             INIT:
3800 46 100         size_t pos, n, nret, i, max_count= iter->recent? iter->tree->recent_count : TreeRBXS_get_count(iter->tree);
3801             IV request;
3802             rbtree_node_t *node;
3803 46 100         rbtree_node_t *(*step)(rbtree_node_t *)= iter->reverse? &rbtree_node_prev : rbtree_node_next;
3804             PPCODE:
3805 46 100         if (iter->item) {
3806 38           request= !count_sv? 1
3807 51 100         : ((SvPOK(count_sv) && *SvPV_nolen(count_sv) == '*')
    100          
    50          
3808 13 100         || (SvNOK(count_sv) && SvNV(count_sv) > (NV)PERL_INT_MAX))? max_count
    50          
3809 7           : SvIV(count_sv);
3810 38 100         if (request < 1) {
3811 1           nret= 0;
3812             }
3813             // A request for 1 is simpler because there is no need to count how many will be returned.
3814             // iter->item wasn't NULL so it is guaranteed to be 1.
3815 37 100         else if (GIMME_V == G_VOID) {
3816             // skip all the busywork if called in void context
3817             // (but still advance the iterator below)
3818 1           TreeRBXS_iter_advance(iter, request);
3819 1           nret= 0;
3820             }
3821 36 100         else if (request == 1 || iter->recent) { // un-optimized loop
    100          
3822 20 100         nret= ix == 3? request<<1 : request;
3823 20 50         EXTEND(SP, nret);
3824 106 100         for (i= 0; i < nret && iter->item; ) {
    50          
3825 6           ST(i++)= ix == 0? sv_2mortal(TreeRBXS_wrap_item(iter->item))
3826 166 100         : ix == 2? iter->item->value
3827 80 50         : sv_2mortal(TreeRBXS_item_wrap_key(iter->item));
3828 86 100         if (ix == 3)
3829 46           ST(i++)= iter->item->value;
3830 86           TreeRBXS_iter_advance(iter, 1);
3831             }
3832 20           nret= i;
3833             }
3834             else { // optimized loop, for iterating batches of tree nodes quickly
3835 16           pos= rbtree_node_index(&iter->item->rbnode);
3836             // calculate how many nodes will be returned
3837 16 100         n= iter->reverse? 1 + pos : max_count - pos;
3838 16 100         if (n > request) n= request;
3839 16           node= &iter->item->rbnode;
3840 16 100         nret= (ix == 3)? n<<1 : n;
3841 16 50         EXTEND(SP, nret); // EXTEND macro declares a temp 'ix' internally - GitHub #2
3842 16 100         if (ix == 0) {
3843 3 100         for (i= 0; i < nret && node; i++, node= step(node))
    50          
3844 2           ST(i)= sv_2mortal(TreeRBXS_wrap_item(GET_TreeRBXS_item_FROM_rbnode(node)));
3845             }
3846 15 100         else if (ix == 1) {
3847 20 100         for (i= 0; i < nret && node; i++, node= step(node))
    50          
3848 16           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_rbnode(node)));
3849             }
3850 11 100         else if (ix == 2) {
3851 96 100         for (i= 0; i < nret && node; i++, node= step(node))
    50          
3852 91           ST(i)= GET_TreeRBXS_item_FROM_rbnode(node)->value;
3853             }
3854             else {
3855 76 100         for (i= 0; i < nret && node; node= step(node)) {
    50          
3856 70           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_rbnode(node)));
3857 70           i++;
3858 70           ST(i)= GET_TreeRBXS_item_FROM_rbnode(node)->value;
3859 70           i++;
3860             }
3861             }
3862 16 50         if (i != nret)
3863 0 0         croak("BUG: expected %ld nodes but found %ld", (long) n, (long) (ix == 3? i>>1 : i));
3864 16           TreeRBXS_iter_advance(iter, n);
3865             }
3866 38           XSRETURN(nret);
3867             } else {
3868             // end of iteration, nothing to do
3869 8           ST(0)= &PL_sv_undef;
3870             // return the undef only if the user didn't specify a count
3871 8           XSRETURN(count_sv? 0 : 1);
3872             }
3873              
3874             bool
3875             step(iter, ofs= 1)
3876             struct TreeRBXS_iter *iter
3877             IV ofs
3878             CODE:
3879 1505           TreeRBXS_iter_advance(iter, ofs);
3880             // Return boolean whether the iterator points to an item
3881 1505 100         RETVAL= !!iter->item;
3882             OUTPUT:
3883             RETVAL
3884              
3885             void
3886             delete(iter)
3887             struct TreeRBXS_iter *iter
3888             PPCODE:
3889 5 50         if (iter->item) {
3890             // up the refcnt temporarily to make sure it doesn't get lost when item gets freed
3891 5           ST(0)= sv_2mortal(SvREFCNT_inc(iter->item->value));
3892             // pruning the item automatically moves iterators to next, including this iterator.
3893 5           TreeRBXS_item_detach_tree(iter->item, iter->tree);
3894             }
3895             else
3896 0           ST(0)= &PL_sv_undef;
3897 5           XSRETURN(1);
3898              
3899             void
3900             STORABLE_freeze(iter, cloning)
3901             struct TreeRBXS_iter *iter
3902             bool cloning
3903             INIT:
3904 0           char itertype= (iter->reverse? 1 : 0) | (iter->recent? 2 : 0);
3905             AV *refs;
3906             PPCODE:
3907             (void) cloning; /* unused */
3908 0           ST(0)= sv_2mortal(newSVpvn(&itertype, 1));
3909 0           ST(1)= sv_2mortal(newRV_noinc((SV*) (refs= newAV())));
3910 0           av_push(refs, newRV_inc(iter->tree->owner));
3911 0 0         av_push(refs, iter->item? newSViv(rbtree_node_index(&iter->item->rbnode)) : newSV(0));
3912 0           XSRETURN(2);
3913              
3914             #-----------------------------------------------------------------------------
3915             # Constants
3916             #
3917              
3918             BOOT:
3919 13           HV *stash= gv_stashpvn("Tree::RB::XS::Node", 18, 1);
3920 13           FUNCTION_IS_LVALUE(value);
3921            
3922 13           stash= gv_stashpvn("Tree::RB::XS", 12, 1);
3923 13           FUNCTION_IS_LVALUE(get);
3924 13           FUNCTION_IS_LVALUE(get_or_add);
3925 13           FUNCTION_IS_LVALUE(FETCH);
3926 13           FUNCTION_IS_LVALUE(lookup);
3927 13           EXPORT_ENUM(KEY_TYPE_ANY);
3928 13           EXPORT_ENUM(KEY_TYPE_INT);
3929 13           EXPORT_ENUM(KEY_TYPE_FLOAT);
3930 13           EXPORT_ENUM(KEY_TYPE_USTR);
3931 13           EXPORT_ENUM(KEY_TYPE_BSTR);
3932 13           EXPORT_ENUM(KEY_TYPE_CLAIM);
3933 13           EXPORT_ENUM(CMP_PERL);
3934 13           EXPORT_ENUM(CMP_INT);
3935 13           EXPORT_ENUM(CMP_FLOAT);
3936 13           EXPORT_ENUM(CMP_STR);
3937 13           EXPORT_ENUM(CMP_FOLDCASE);
3938 13           EXPORT_ENUM(CMP_MEMCMP);
3939 13           EXPORT_ENUM(CMP_NUMSPLIT);
3940 13           EXPORT_ENUM(CMP_NUMSPLIT_FOLDCASE);
3941 13           EXPORT_ENUM(GET_EQ);
3942 13           EXPORT_ENUM(GET_OR_ADD);
3943 13           EXPORT_ENUM(GET_EQ_LAST);
3944 13           EXPORT_ENUM(GET_GE);
3945 13           EXPORT_ENUM(GET_LE);
3946 13           EXPORT_ENUM(GET_LE_LAST);
3947 13           EXPORT_ENUM(GET_GT);
3948 13           EXPORT_ENUM(GET_LT);
3949 13           EXPORT_ENUM(GET_NEXT);
3950 13           EXPORT_ENUM(GET_PREV);
3951              
3952             PROTOTYPES: DISABLE