File Coverage

TreeRBXS.xs
Criterion Covered Total %
statement 1613 1940 83.1
branch 1097 1764 62.1
condition n/a
subroutine n/a
pod n/a
total 2710 3704 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 113           static void TreeRBXS_init(struct TreeRBXS *tree, SV *owner) {
404 113           memset(tree, 0, sizeof(struct TreeRBXS));
405 113           tree->owner= owner;
406 113           rbtree_init_tree(&tree->root_sentinel, &tree->leaf_sentinel);
407 113           tree->recent.next= &tree->recent;
408 113           tree->recent.prev= &tree->recent;
409             /* defaults, which can be overridden by _init_tree */
410 113           tree->key_type= KEY_TYPE_ANY;
411 113           tree->compare_fn_id= CMP_PERL;
412 113           tree->recent_limit= -1;
413 113           }
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 501479           static void TreeRBXS_init_tmp_item(struct TreeRBXS_item *item, struct TreeRBXS *tree, SV *key, SV *value) {
488 501479           STRLEN len= 0;
489              
490             // all fields should start NULL just to be safe
491 501479           memset(item, 0, sizeof(*item));
492             // copy key type from tree
493 501479           item->key_type= tree->key_type;
494             // If there's a transform function, apply that first
495 501479 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 481400           else item->orig_key_stored= 0;
501              
502             // set up the keys.
503 501479           item->ckeylen= 0;
504 501479           switch (item->key_type) {
505 140957           case KEY_TYPE_ANY:
506 140957           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 501479           item->value= value;
526 501479           }
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 500990           static struct TreeRBXS_item * TreeRBXS_new_item_from_tmp_item(struct TreeRBXS_item *src) {
657             struct TreeRBXS_item *dst;
658 500990 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 641092 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 360888           Newxz(dst, 1, struct TreeRBXS_item);
705 360888           switch (src->key_type) {
706 130772           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 140772           SvREADONLY_on(dst->keyunion.svkey);
710 140772           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 500990           dst->orig_key_stored= src->orig_key_stored;
718 500990           dst->key_type= src->key_type;
719 500990           dst->value= newSVsv(src->value);
720 500990           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 500991           static void TreeRBXS_item_free(struct TreeRBXS_item *item) {
810             //warn("TreeRBXS_item_free");
811 500991 100         switch (item->key_type) {
812 140773           case KEY_TYPE_ANY:
813 140773           case KEY_TYPE_CLAIM: SvREFCNT_dec(item->keyunion.svkey); break;
814             }
815 500991 100         if (item->orig_key_stored) {
816 20032           SV *tmp= GET_TreeRBXS_item_ORIG_KEY(item);
817 20032           SvREFCNT_dec(tmp);
818             }
819 500991 50         if (item->value)
820 500991           SvREFCNT_dec(item->value);
821 500991           Safefree(item);
822 500991           }
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 108           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 108           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 108 50         if (!rbtree_node_is_in_tree(&item->rbnode))
838 108           TreeRBXS_item_free(item);
839 108           }
840              
841             /* Simple linked-list deletion of an iterator from the list pointing to this item.
842             */
843 1680           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 16904 50         for (cur= &item->iter; *cur; cur= &((*cur)->next_iter)) {
848 16904 100         if (*cur == iter) {
849 1680           *cur= iter->next_iter;
850 1680           iter->next_iter= NULL;
851 1680           iter->item= NULL;
852 1680           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 1960           static void TreeRBXS_iter_set_item(struct TreeRBXS_iter *iter, struct TreeRBXS_item *item) {
882 1960 100         if (iter->item == item)
883 22           return;
884              
885 1938 100         if (iter->item)
886 1661           TreeRBXS_item_detach_iter(iter->item, iter);
887              
888 1938 100         if (item) {
889             // If this is a 'recent' iterator, it must be attached to a recent-tracked item
890 1723 100         if (iter->recent && !item->recent.next)
    50          
891 0           croak("BUG: Attempt to bind recent-iterator to non-recent-tracked item");
892 1723           iter->item= item;
893             // linked-list insert
894 1723           iter->next_iter= item->iter;
895 1723           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 1698           static void TreeRBXS_iter_advance(struct TreeRBXS_iter *iter, IV ofs) {
903             rbtree_node_t *node;
904 1698           struct TreeRBXS_item *item= iter->item;
905             struct dllist_node *cur, *end;
906              
907 1698 50         if (!iter->tree)
908 0           croak("BUG: iterator lost tree");
909             // Special logic for when iterating insertion order
910 1698 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 1595 100         else if (ofs == 1) {
933             // nothing to do at end of iteration
934 825 100         if (item) {
935 812           node= &item->rbnode;
936 812 100         node= iter->reverse? rbtree_node_prev(node) : rbtree_node_next(node);
937 812           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 770           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 770           pos= !item? cnt
950 1514 100         : !iter->reverse? rbtree_node_index(&item->rbnode)
951             // For reverse iterators, swap the scale so that math goes upward
952 744 100         : cnt - 1 - rbtree_node_index(&item->rbnode);
953 770 100         if (ofs > 0) {
954 766 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 770 100         if (iter->reverse) newpos= cnt - 1 - newpos;
961 770           node= rbtree_node_child_at_index(TreeRBXS_get_root(iter->tree), (size_t)newpos);
962 770           TreeRBXS_iter_set_item(iter, node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL);
963             }
964 1698           }
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 97 100         for (iter= item->iter, item->iter= NULL; iter; iter= next) {
983 79           next= iter->next_iter;
984 79 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 71 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 68 100         if (iter->reverse) { // iterating high to low
1022 29 100         if (!prev_item) {
1023 25           node= rbtree_node_prev(&item->rbnode);
1024 25 100         if (!node) {
1025             // end of iteration
1026 20           iter->item= NULL;
1027 20           iter->next_iter= NULL;
1028 20           continue;
1029             }
1030 5           prev_item= GET_TreeRBXS_item_FROM_rbnode(node);
1031             }
1032 9           iter->item= prev_item;
1033             // linked list add head node
1034 9           iter->next_iter= prev_item->iter;
1035 9           prev_item->iter= iter;
1036             }
1037             else { // iterating low to high
1038 39 100         if (!next_item) {
1039 26           node= rbtree_node_next(&item->rbnode);
1040 26 100         if (!node) {
1041             // end of iteration
1042 16           iter->item= NULL;
1043 16           iter->next_iter= NULL;
1044 16           continue;
1045             }
1046 10           next_item= GET_TreeRBXS_item_FROM_rbnode(node);
1047             }
1048 23           iter->item= next_item;
1049             // linked list add head node
1050 23           iter->next_iter= next_item->iter;
1051 23           next_item->iter= iter;
1052             }
1053             }
1054             }
1055 18           }
1056              
1057             /* Disconnect an item from the tree, gracefully
1058             */
1059 98           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 98           bool in_tree= rbtree_node_is_in_tree(&item->rbnode);
1063 98 50         if (in_tree) {
1064 98 100         if (tree->prev_inserted_item == item) {
1065 14           tree->prev_inserted_item= NULL;
1066 14           tree->prev_inserted_trend= 0;
1067             }
1068             // If any iterator points to this node, move it to the following node.
1069 98 100         if (item->iter)
1070 16           TreeRBXS_item_advance_all_iters(item, 0);
1071             // If the node was part of LRU cache, cancel that
1072 98 100         if (item->recent.next)
1073 25           TreeRBXS_recent_prune(tree, item);
1074 98           rbtree_node_prune(&item->rbnode);
1075             }
1076             /* The item could be owned by a tree or by a Node/Iterator, or both.
1077             If the tree releases the reference, the Node/Iterator will be the owner.
1078             Else the tree was the only owner, and the node needs freed */
1079 98 100         if (!item->owner)
1080 71           TreeRBXS_item_free(item);
1081 27 50         else if (in_tree)
1082 27           SvREFCNT_dec(item->owner);
1083 98           }
1084              
1085             /* Callback when clearing the entire tree.
1086             * This is like _detach_tree, but doesn't need to clean up relation to other nodes.
1087             */
1088 500892           static void TreeRBXS_item_clear(struct TreeRBXS_item* item, struct TreeRBXS *tree) {
1089 500892           struct TreeRBXS_iter *iter= item->iter, *next;
1090             /* Detach all iterators from this node */
1091 500898 100         while (iter) {
1092 6           next= iter->next_iter;
1093 6           iter->item= NULL;
1094 6           iter->next_iter= NULL;
1095 6           iter= next;
1096             }
1097 500892           item->iter= NULL;
1098 500892           item->recent.next= NULL;
1099 500892           item->recent.prev= NULL;
1100 500892           memset(&item->rbnode, 0, sizeof(item->rbnode));
1101             // If the item is still referenced by the perl Node object, don't delete it.
1102 500892 100         if (!item->owner)
1103 500812           TreeRBXS_item_free(item);
1104             else
1105 80           SvREFCNT_dec(item->owner);
1106 500892           }
1107              
1108 262           static void TreeRBXS_iter_free(struct TreeRBXS_iter *iter) {
1109 262 100         if (iter->item)
1110 19           TreeRBXS_item_detach_iter(iter->item, iter);
1111 262 50         if (iter->tree) {
1112 262 100         if (iter->tree->hashiter == iter)
1113 5           iter->tree->hashiter= NULL;
1114             else
1115 257           SvREFCNT_dec(iter->tree->owner);
1116             }
1117 262           Safefree(iter);
1118 262           }
1119              
1120 113           static void TreeRBXS_destroy(struct TreeRBXS *tree) {
1121             //warn("TreeRBXS_destroy");
1122 113           rbtree_clear(&tree->root_sentinel, (void (*)(void *, void *)) &TreeRBXS_item_clear, -OFS_TreeRBXS_item_FIELD_rbnode, tree);
1123 113 100         if (tree->compare_callback)
1124 8           SvREFCNT_dec(tree->compare_callback);
1125 113 100         if (tree->hashiter)
1126 5           TreeRBXS_iter_free(tree->hashiter);
1127 113           }
1128              
1129             // This gets used in two places, but I don't want to make it a function.
1130             #define TREERBXS_INSERT_ITEM_AT_NODE(tree, item, parent_node, direction) \
1131             do { \
1132             if (!(parent_node)) /* empty tree */ \
1133             rbtree_node_insert_before(&(tree)->root_sentinel, &(item)->rbnode); \
1134             else if ((direction) > 0) \
1135             rbtree_node_insert_after((parent_node), &(item)->rbnode); \
1136             else \
1137             rbtree_node_insert_before((parent_node), &(item)->rbnode); \
1138             if ((tree)->track_recent) \
1139             TreeRBXS_recent_insert_before((tree), (item), &(tree)->recent); \
1140             } while (0)
1141              
1142 376           static struct TreeRBXS_item *TreeRBXS_find_item(struct TreeRBXS *tree, struct TreeRBXS_item *stack_item, int mode) {
1143             struct TreeRBXS_item *item;
1144 376           rbtree_node_t *node= NULL;
1145             int cmp;
1146 376           bool step= false;
1147              
1148             // Need to ensure we find the *first* matching node for a key,
1149             // to deal with the case of duplicate keys.
1150 376           node= rbtree_find_nearest(
1151             &tree->root_sentinel,
1152             stack_item, // The item *is* the key that gets passed to the compare function
1153 376           (int(*)(void*,void*,void*)) tree->compare,
1154             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
1155             &cmp);
1156 376 100         if (node && cmp == 0) {
    100          
1157             // Found an exact match. First and last are the range of nodes matching.
1158 329           switch (mode) {
1159 8           case GET_LT:
1160             case GET_PREV:
1161 8           step= true;
1162 314           case GET_EQ:
1163             case GET_OR_ADD:
1164             case GET_GE:
1165             case GET_LE:
1166             // make sure it is the first of nodes with same key
1167 314 100         if (tree->allowed_duplicates)
1168 2           node= rbtree_find_leftmost_samekey(node, (int(*)(void*,void*,void*)) tree->compare,
1169             tree, -OFS_TreeRBXS_item_FIELD_rbnode);
1170 314 100         if (step)
1171 8           node= rbtree_node_prev(node);
1172 314           break;
1173 10           case GET_GT:
1174             case GET_NEXT:
1175 10           step= true;
1176 15           case GET_EQ_LAST:
1177             case GET_LE_LAST:
1178             // make sure it is the last of nodes with same key
1179 15 100         if (tree->allowed_duplicates)
1180 1           node= rbtree_find_rightmost_samekey(node, (int(*)(void*,void*,void*)) tree->compare,
1181             tree, -OFS_TreeRBXS_item_FIELD_rbnode);
1182 15 100         if (step)
1183 10           node= rbtree_node_next(node);
1184 15           break;
1185 0           default: croak("BUG: unhandled mode");
1186             }
1187             } else {
1188             // Didn't find an exact match. First and last are the bounds of what would have matched.
1189 47           switch (mode) {
1190 27           case GET_EQ:
1191             case GET_EQ_LAST:
1192             case GET_PREV:
1193             case GET_NEXT:
1194 27           node= NULL; break;
1195 7           case GET_GE:
1196             case GET_GT:
1197 7 50         if (node && cmp > 0)
    100          
1198 2           node= rbtree_node_next(node);
1199 7           break;
1200 7           case GET_LE:
1201             case GET_LE_LAST:
1202             case GET_LT:
1203 7 50         if (node && cmp < 0)
    100          
1204 6           node= rbtree_node_prev(node);
1205 7           break;
1206 6           case GET_OR_ADD:
1207 6           item= TreeRBXS_new_item_from_tmp_item(stack_item);
1208 6 100         TREERBXS_INSERT_ITEM_AT_NODE(tree, item, node, cmp);
    50          
    50          
1209 6           return item;
1210 0           default: croak("BUG: unhandled mode");
1211             }
1212             }
1213 370           return node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
1214             }
1215              
1216             struct TreeRBXS_item *
1217 501016           TreeRBXS_insert_item(struct TreeRBXS *tree, struct TreeRBXS_item *stack_item, bool overwrite, SV **oldval_out) {
1218             struct TreeRBXS_item *item;
1219             rbtree_node_t *hint, *tmpnode, *first, *last;
1220             int cmp;
1221             /* If newly inserted items have been adjacent to prev_inserted_item 3 or more times in a row,
1222             It is worth comparing them with that first. This optimization results in nearly linear
1223             insertion time when the keys are pre-sorted. */
1224 501016 100         if (tree->prev_inserted_trend >= INSERT_TREND_TRIGGER) {
1225 500671 100         if (tree->prev_inserted_trend > INSERT_TREND_CAP)
1226 470541           tree->prev_inserted_trend= INSERT_TREND_CAP;
1227 500671           hint= &tree->prev_inserted_item->rbnode;
1228 500671           cmp= tree->compare(tree, stack_item, tree->prev_inserted_item);
1229 500671 100         if (cmp == 0) {
1230 14           ++tree->prev_inserted_trend;
1231 14 100         if (overwrite) {
1232 1 50         if (tree->prev_inserted_dup) {
1233             /* the 'hint' provided needs to be the parent-most node of the duplicates */
1234 1           while (hint->parent != &tree->root_sentinel
1235 2 100         && tree->compare(tree, stack_item, GET_TreeRBXS_item_FROM_rbnode(hint->parent)) == 0)
    50          
1236 1           hint= hint->parent;
1237 1           goto overwrite_multi;
1238             }
1239             else
1240 0           goto overwrite_single;
1241 13 50         } else if (tree->allow_duplicates)
1242 13           goto insert_new_duplicate;
1243             else
1244 0           return NULL;
1245             }
1246 500657 100         else if (cmp > 0) {
1247 500607           tmpnode= rbtree_node_next(hint);
1248 500607 100         if (!tmpnode || tree->compare(tree, stack_item, GET_TreeRBXS_item_FROM_rbnode(tmpnode)) < 0) {
    100          
1249 485618           ++tree->prev_inserted_trend;
1250 485618           goto insert_relative;
1251             }
1252             // else it broke the trend and needs inserted normally.
1253             }
1254             else {
1255 50           tmpnode= rbtree_node_prev(hint);
1256 50 100         if (!tmpnode || tree->compare(tree, stack_item, GET_TreeRBXS_item_FROM_rbnode(tmpnode)) > 0) {
    100          
1257 12           ++tree->prev_inserted_trend;
1258 12           goto insert_relative;
1259             }
1260             // else it broke the trend and needs inserted normally.
1261             }
1262 15027           --tree->prev_inserted_trend;
1263             }
1264 15372           hint= rbtree_find_nearest(
1265             &tree->root_sentinel,
1266             stack_item, // The item *is* the key that gets passed to the compare function
1267 15372           (int(*)(void*,void*,void*)) tree->compare,
1268             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
1269             &cmp);
1270 15372 100         if (hint && cmp == 0) {
    100          
1271 52 100         if (overwrite) {
1272             // In case of multiple matches, find all
1273 25 100         if (tree->allowed_duplicates) {
1274 2           overwrite_multi:
1275 3 50         if (!rbtree_find_all(
1276             hint, stack_item,
1277 3           (int(*)(void*,void*,void*)) tree->compare,
1278             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
1279             &first, &last, NULL)
1280             )
1281 0           croak("BUG");
1282             //warn("replacing %d matching keys with new value", (int)count);
1283             // prune every node that follows 'first'
1284 9 100         while (last != first) {
1285 6           item= GET_TreeRBXS_item_FROM_rbnode(last);
1286 6           last= rbtree_node_prev(last);
1287 6           TreeRBXS_item_detach_tree(item, tree);
1288             }
1289 3           hint= first;
1290             }
1291             /* overwrite the value of the node */
1292 23           overwrite_single:
1293 26           item= GET_TreeRBXS_item_FROM_rbnode(hint);
1294 26           sv_2mortal(item->value);
1295 26 100         if (oldval_out)
1296 23           *oldval_out= item->value; // return the old value
1297 26           item->value= newSVsv(stack_item->value); // store new copy of supplied param
1298             /* If the tree is applying a transform to the items, the new key might not be identical
1299             to the old, even though the transformed keys are equal. (i.e. different case)
1300             So, store the new key in its place. */
1301 26 100         if (item->orig_key_stored) {
1302 6           SV *orig_key= GET_TreeRBXS_item_ORIG_KEY(item);
1303 6           SV *new_key= GET_TreeRBXS_stack_item_ORIG_KEY(stack_item);
1304 6 50         if (sv_cmp(orig_key, new_key)) {
1305 6           SvREFCNT_dec(orig_key);
1306 6           orig_key= newSVsv(new_key);
1307 6           SvREADONLY_on(orig_key);
1308 6           SET_TreeRBXS_item_ORIG_KEY(item, orig_key);
1309             }
1310             }
1311 26 100         if (tree->track_recent || item->recent.next)
    50          
1312 1           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
1313 26           tree->prev_inserted_dup= false;
1314 27 100         } else if (tree->allow_duplicates) {
1315 21           hint= rbtree_find_rightmost_samekey(hint, (int(*)(void*,void*,void*)) tree->compare,
1316             tree, -OFS_TreeRBXS_item_FIELD_rbnode);
1317 34           insert_new_duplicate:
1318 34           item= TreeRBXS_new_item_from_tmp_item(stack_item);
1319 34           rbtree_node_insert_after(hint, &item->rbnode);
1320 34 100         if (tree->track_recent)
1321 8           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
1322 34           tree->allowed_duplicates= true;
1323 34           tree->prev_inserted_dup= true;
1324             } else {
1325 6           item= GET_TreeRBXS_item_FROM_rbnode(hint);
1326 6 100         if (item == tree->prev_inserted_item)
1327 3           ++tree->prev_inserted_trend;
1328 6           return NULL; // nothing inserted
1329             }
1330             }
1331             else {
1332 15320           insert_relative:
1333 500950           item= TreeRBXS_new_item_from_tmp_item(stack_item);
1334 500950 100         TREERBXS_INSERT_ITEM_AT_NODE(tree, item, hint, cmp);
    100          
    100          
1335 500950           tree->prev_inserted_dup= false;
1336             }
1337             // If trend logic is triggered above, this is already calculated. Else check adjacency.
1338 501010 100         if (tree->prev_inserted_trend < INSERT_TREND_TRIGGER && tree->prev_inserted_item) {
    100          
1339             // Check trend. Is item adjacent to 'prev'?
1340 246 100         if (item == tree->prev_inserted_item
1341 238 100         || item->rbnode.parent == &tree->prev_inserted_item->rbnode
1342 57 100         || item->rbnode.left == &tree->prev_inserted_item->rbnode
1343 50 100         || item->rbnode.right == &tree->prev_inserted_item->rbnode
1344             ) {
1345 207           ++tree->prev_inserted_trend;
1346 39 100         } else if (tree->prev_inserted_trend)
1347 24           --tree->prev_inserted_trend;
1348             }
1349 501010           tree->prev_inserted_item= item;
1350 501010           return item;
1351             }
1352              
1353             /* Mark the current tree item as the most recent, regardless of whether it was previously tracked.
1354             */
1355 111           static void TreeRBXS_recent_insert_before(struct TreeRBXS *tree, struct TreeRBXS_item *item, struct dllist_node *node_after) {
1356             struct dllist_node *node_before;
1357             // Not already before this node?
1358 111 100         if (item->recent.next != node_after) {
1359 110 100         if (item->recent.next) {
1360             // remove from linkedlist
1361 6           item->recent.prev->next= item->recent.next;
1362 6           item->recent.next->prev= item->recent.prev;
1363             } else
1364 104           ++tree->recent_count;
1365             // Add following 'node_before'
1366 110           node_before= node_after->prev;
1367 110           node_after->prev= &item->recent;
1368 110           node_before->next= &item->recent;
1369 110           item->recent.prev= node_before;
1370 110           item->recent.next= node_after;
1371             // prune oldest node if exceeded limit
1372 110 100         if (tree->recent_limit >= 0 && tree->recent_count > tree->recent_limit)
    100          
1373 14           TreeRBXS_truncate_recent(tree, tree->recent_limit, NULL);
1374             }
1375 111           }
1376              
1377             /* Stop recent-tracking for the tree item.
1378             * Users can call this at any time in order to remove certain nodes from consideration.
1379             */
1380 35           static void TreeRBXS_recent_prune(struct TreeRBXS *tree, struct TreeRBXS_item *item) {
1381 35 50         if (item->recent.next) {
1382             // Move recent-list iterators pointing to this node
1383 35 100         if (item->iter)
1384 2           TreeRBXS_item_advance_all_iters(item, ONLY_ADVANCE_RECENT);
1385 35           item->recent.prev->next= item->recent.next;
1386 35           item->recent.next->prev= item->recent.prev;
1387 35           item->recent.next= NULL;
1388 35           item->recent.prev= NULL;
1389 35           --tree->recent_count;
1390             }
1391 35           }
1392              
1393             /* Remove all nodes older than the newest 'lim'-count
1394             * The caller may optionally supply 'nodes_out' to receive mortal references
1395             * to the removed nodes. The caller must size it for (tree->recent_count - lim)
1396             */
1397 15           static void TreeRBXS_truncate_recent(struct TreeRBXS *tree, IV lim, SV **nodes_out) {
1398 15 50         if (lim >= 0 && tree->recent_count > lim) {
    50          
1399 15           IV i, n= tree->recent_count - lim;
1400 15           struct dllist_node *next, *cur= tree->recent.next;
1401 32 100         for (i= 0; i < n && cur && cur != &tree->recent; i++) {
    50          
    50          
1402 17           struct TreeRBXS_item *item= GET_TreeRBXS_item_FROM_recent(cur);
1403 17 50         if (nodes_out)
1404 0           nodes_out[i]= sv_2mortal(TreeRBXS_wrap_item(item));
1405 17           next= cur->next;
1406 17           TreeRBXS_item_detach_tree(item, tree);
1407 17           cur= next;
1408             }
1409 15 50         if (i != n)
1410 0           croak("BUG: recent_count inconsistent with length of linked list");
1411             }
1412 15           }
1413              
1414             /*----------------------------------------------------------------------------
1415             * Comparison Functions.
1416             * These conform to the rbtree_compare_fn signature of a context followed by
1417             * two "key" pointers. In this case, the keys are TreeRBXS_item structs
1418             * and the actual key field depends on the key_type of the node. However,
1419             * for speed, the key_type is assumed to have been chosen correctly for the
1420             * comparison function during _init
1421             */
1422              
1423             // Compare integers which were both already decoded from the original SVs
1424 221076           static int TreeRBXS_cmp_int(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1425             //warn(" int compare %p (%d) <=> %p (%d)", a, (int)a->keyunion.ikey, b, (int)b->keyunion.ikey);
1426 221076           IV diff= a->keyunion.ikey - b->keyunion.ikey;
1427 221076 100         return diff < 0? -1 : diff > 0? 1 : 0; /* shrink from IV to int might lose upper bits */
1428             }
1429              
1430             // Compare floats which were both already decoded from the original SVs
1431 220847           static int TreeRBXS_cmp_float(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1432 220847           NV diff= a->keyunion.nkey - b->keyunion.nkey;
1433 220847 100         return diff < 0? -1 : diff > 0? 1 : 0;
1434             }
1435              
1436             // Compare C strings using memcmp, on raw byte values. The strings have been pre-processed to
1437             // be comparable with memcmp, by case-folding, or making sure both are UTF-8, etc.
1438 672297           static int TreeRBXS_cmp_memcmp(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1439 672297           size_t alen= a->ckeylen, blen= b->ckeylen;
1440 672297           int cmp= memcmp(a->keyunion.ckey, b->keyunion.ckey, alen < blen? alen : blen);
1441 672297 100         return cmp? cmp : alen < blen? -1 : alen > blen? 1 : 0;
    100          
1442             }
1443              
1444 40253           static int cmp_numsplit(
1445             const char *apos, const char *alim, bool a_utf8,
1446             const char *bpos, const char *blim, bool b_utf8
1447             ) {
1448             const char *amark, *bmark;
1449             size_t alen, blen;
1450             int cmp;
1451              
1452 40308 100         while (apos < alim && bpos < blim) {
    100          
1453             // Step forward as long as both strings are identical
1454 40417 100         while (apos < alim && bpos < blim && *apos == *bpos && !isdigit(*apos))
    50          
    100          
    100          
1455 124           apos++, bpos++;
1456             // find the next start of digits along the strings
1457 40293           amark= apos;
1458 40636 100         while (apos < alim && !isdigit(*apos)) apos++;
    100          
1459 40293           bmark= bpos;
1460 40487 100         while (bpos < blim && !isdigit(*bpos)) bpos++;
    100          
1461 40293           alen= apos - amark;
1462 40293           blen= bpos - bmark;
1463             // compare the non-digit portions found in each string
1464 40293 100         if (alen || blen) {
    100          
1465             // If one of the non-digit spans was length=0, then we are comparing digits (or EOF)
1466             // with string, and digits sort first.
1467 92 100         if (alen == 0) return -1;
1468 82 100         if (blen == 0) return 1;
1469             // else compare the portions in common.
1470             #if PERL_VERSION_GE(5,14,0)
1471 36 50         if (a_utf8 != b_utf8) {
1472 0           cmp= a_utf8? -bytes_cmp_utf8((const U8*) bmark, blen, (const U8*) amark, alen)
1473 0 0         : bytes_cmp_utf8((const U8*) amark, alen, (const U8*) bmark, blen);
1474 0 0         if (cmp) return cmp;
1475             } else
1476             #endif
1477             {
1478 36           cmp= memcmp(amark, bmark, alen < blen? alen : blen);
1479 36 50         if (cmp) return cmp;
1480 0 0         if (alen < blen) return -1;
1481 0 0         if (alen > blen) return -1;
1482             }
1483             }
1484             // If one of the strings ran out of characters, it is the lesser one.
1485 40201 100         if (!(apos < alim && bpos < blim)) break;
    50          
1486             // compare the digit portions found in each string
1487             // Find the start of nonzero digits
1488 40387 50         while (apos < alim && *apos == '0') apos++;
    100          
1489 40325 50         while (bpos < blim && *bpos == '0') bpos++;
    100          
1490 40197           amark= apos;
1491 40197           bmark= bpos;
1492             // find the first differing digit
1493 147629 100         while (apos < alim && bpos < blim && *apos == *bpos && isdigit(*apos))
    100          
    100          
    100          
1494 107432           apos++, bpos++;
1495             // If there are more digits to consider beyond the first mismatch (or EOF) then need to
1496             // find the end of the digits and see which number was longer.
1497 40197 100         if ((apos < alim && isdigit(*apos)) || (bpos < blim && isdigit(*bpos))) {
    100          
    100          
    100          
1498 40142 100         if (apos == alim) return -1;
1499 40141 50         if (bpos == blim) return 1;
1500             // If the strings happen to be the same length, this will be the deciding character
1501 40141           cmp= *apos - *bpos;
1502             // find the end of digits
1503 88660 100         while (apos < alim && isdigit(*apos)) apos++;
    100          
1504 88645 100         while (bpos < blim && isdigit(*bpos)) bpos++;
    100          
1505             // Whichever number is longer is greater
1506 40141           alen= apos - amark;
1507 40141           blen= bpos - bmark;
1508 40141 100         if (alen < blen) return -1;
1509 40089 100         if (alen > blen) return 1;
1510             // Else they're the same length, and the 'cmp' captured earlier is the answer.
1511 40022           return cmp;
1512             }
1513             // Else they're equal, continue to the next component.
1514             }
1515             // One or both of the strings ran out of characters
1516 19 100         if (bpos < blim) return -1;
1517 18 100         if (apos < alim) return 1;
1518 4           return 0;
1519             }
1520              
1521 40186           static int TreeRBXS_cmp_numsplit(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1522             const char *apos, *alim;
1523             const char *bpos, *blim;
1524             size_t alen, blen;
1525 40186           bool a_utf8= false, b_utf8= false;
1526              
1527 40186           switch (tree->key_type) {
1528 20062           case KEY_TYPE_USTR:
1529 20062           a_utf8= b_utf8= true;
1530 20124           case KEY_TYPE_BSTR:
1531 20124           apos= a->keyunion.ckey; alim= apos + a->ckeylen;
1532 20124           bpos= b->keyunion.ckey; blim= bpos + b->ckeylen;
1533 20124           break;
1534 20062           case KEY_TYPE_ANY:
1535             case KEY_TYPE_CLAIM:
1536             #if PERL_VERSION_LT(5,14,0)
1537             // before 5.14, need to force both to utf8 if either are utf8
1538             if (SvUTF8(a->keyunion.svkey) || SvUTF8(b->keyunion.svkey)) {
1539             apos= SvPVutf8(a->keyunion.svkey, alen);
1540             bpos= SvPVutf8(b->keyunion.svkey, blen);
1541             a_utf8= b_utf8= true;
1542             } else
1543             #else
1544             // After 5.14, can compare utf8 with bytes without converting the buffer
1545 20062           a_utf8= SvUTF8(a->keyunion.svkey);
1546 20062           b_utf8= SvUTF8(b->keyunion.svkey);
1547             #endif
1548             {
1549 20062           apos= SvPV(a->keyunion.svkey, alen);
1550 20062           bpos= SvPV(b->keyunion.svkey, blen);
1551             }
1552 20062           alim= apos + alen;
1553 20062           blim= bpos + blen;
1554 20062           break;
1555 0           default: croak("BUG");
1556             }
1557 40186           return cmp_numsplit(apos, alim, a_utf8, bpos, blim, b_utf8);
1558             }
1559              
1560             // Compare SV items using Perl's 'cmp' operator
1561 95426           static int TreeRBXS_cmp_perl(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1562 95426           return sv_cmp(a->keyunion.svkey, b->keyunion.svkey);
1563             }
1564              
1565             // Compare SV items using a user-supplied perl callback
1566 221330           static int TreeRBXS_cmp_perl_cb(struct TreeRBXS *tree, struct TreeRBXS_item *a, struct TreeRBXS_item *b) {
1567             IV ret;
1568 221330           dSP;
1569 221330           ENTER;
1570             // There are a max of $tree_depth comparisons to do during an insert or search,
1571             // so should be safe to not free temporaries for a little bit.
1572 221330 50         PUSHMARK(SP);
1573 221330 50         EXTEND(SP, 2);
1574 221330           PUSHs(a->keyunion.svkey);
1575 221330           PUSHs(b->keyunion.svkey);
1576 221330           PUTBACK;
1577 221330 50         if (call_sv(tree->compare_callback, G_SCALAR) != 1)
1578 0           croak("stack assertion failed");
1579 221330           SPAGAIN;
1580 221330           ret= POPi;
1581 221330           PUTBACK;
1582             // FREETMPS;
1583 221330           LEAVE;
1584 221330 100         return (int)(ret < 0? -1 : ret > 0? 1 : 0);
1585             }
1586              
1587             // Equivalent of "return fc($key)" (case folding)
1588 20079           static SV* TreeRBXS_xform_fc(struct TreeRBXS *tree, SV *orig_key) {
1589             SV *folded_mortal;
1590 20079           dSP;
1591             /* Annoyingly, the 'fc' implementation is not packaged into the perl api
1592             * as a callable function, so I just have to invoke the perl op itself :-(
1593             * For 5.16 and onward I can call CORE::fc, but before that I even need
1594             * to wrap the op in my own sub. (see XS.pm)
1595             */
1596 20079           ENTER;
1597 20079 50         PUSHMARK(SP);
1598 20079 50         XPUSHs(orig_key);
1599 20079           PUTBACK;
1600             #if PERL_VERSION_GE(5,16,0)
1601 20079           call_pv("CORE::fc", G_SCALAR);
1602             #else
1603             call_pv("Tree::RB::XS::_fc_impl", G_SCALAR);
1604             #endif
1605 20079           SPAGAIN;
1606 20079           folded_mortal= POPs;
1607 20079           PUTBACK;
1608 20079           LEAVE;
1609 20079           return folded_mortal;
1610             }
1611              
1612             /*------------------------------------------------------------------------------------
1613             * Definitions of Perl MAGIC that attach C structs to Perl SVs
1614             * All instances of Tree::RB::XS have a magic-attached struct TreeRBXS
1615             * All instances of Tree::RB::XS::Node have a magic-attached struct TreeRBXS_item
1616             */
1617              
1618             // destructor for Tree::RB::XS
1619 113           static int TreeRBXS_magic_free(pTHX_ SV* sv, MAGIC* mg) {
1620 113 50         if (mg->mg_ptr) {
1621 113           TreeRBXS_destroy((struct TreeRBXS*) mg->mg_ptr);
1622 113           Safefree(mg->mg_ptr);
1623 113           mg->mg_ptr= NULL;
1624             }
1625 113           return 0; // ignored anyway
1626             }
1627              
1628             // destructor for Tree::RB::XS::Node
1629 108           static int TreeRBXS_item_magic_free(pTHX_ SV* sv, MAGIC* mg) {
1630 108 50         if (mg->mg_ptr) {
1631 108           TreeRBXS_item_detach_owner((struct TreeRBXS_item*) mg->mg_ptr);
1632 108           mg->mg_ptr= NULL;
1633             }
1634 108           return 0;
1635             }
1636              
1637             // destructor for Tree::RB::XS::Iter
1638 257           static int TreeRBXS_iter_magic_free(pTHX_ SV* sv, MAGIC *mg) {
1639 257 50         if (mg->mg_ptr)
1640 257           TreeRBXS_iter_free((struct TreeRBXS_iter*) mg->mg_ptr);
1641 257           return 0;
1642             }
1643              
1644             #ifdef USE_ITHREADS
1645             /* Function that tells ithread users they can't clone the tree
1646             * (patches welcome, but it's a hard problem with needing to reconnect the
1647             * items and iterators that might be held as perl objects)
1648             */
1649             static int TreeRBXS_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
1650             croak("This object cannot be shared between threads");
1651             return 0;
1652             };
1653             #endif
1654              
1655              
1656             // Return the TreeRBXS struct attached to a Perl object via MAGIC.
1657             // The 'obj' should be a reference to a blessed SV.
1658             // Use AUTOCREATE to attach magic and allocate a struct if it wasn't present.
1659             // Use OR_DIE for a built-in croak() if the return value would be NULL.
1660 501536           static struct TreeRBXS* TreeRBXS_get_magic_tree(SV *obj, int flags) {
1661             SV *sv;
1662             MAGIC* magic;
1663             struct TreeRBXS *tree;
1664 501536 50         if (!sv_isobject(obj)) {
1665 0 0         if (flags & OR_DIE)
1666 0           croak("Not an object");
1667 0           return NULL;
1668             }
1669 501536           sv= SvRV(obj);
1670 501536 100         if (SvMAGICAL(sv) && (magic= mg_findext(sv, PERL_MAGIC_ext, &TreeRBXS_magic_vt)))
    50          
1671 501423           return (struct TreeRBXS*) magic->mg_ptr;
1672              
1673 113 50         if (flags & AUTOCREATE) {
1674 113           Newxz(tree, 1, struct TreeRBXS);
1675 113           TreeRBXS_init(tree, sv);
1676 113           magic= sv_magicext(sv, NULL, PERL_MAGIC_ext, &TreeRBXS_magic_vt, (const char*) tree, 0);
1677             #ifdef USE_ITHREADS
1678             magic->mg_flags |= MGf_DUP;
1679             #endif
1680 113           return tree;
1681             }
1682 0 0         else if (flags & OR_DIE)
1683 0           croak("Object lacks 'struct TreeRBXS' magic");
1684 0           return NULL;
1685             }
1686              
1687             // Return the TreeRBXS_item that was attached to a perl object via MAGIC.
1688             // The 'obj' should be a reference to a blessed magical SV.
1689 729           static struct TreeRBXS_item* TreeRBXS_get_magic_item(SV *obj, int flags) {
1690             SV *sv;
1691             MAGIC* magic;
1692              
1693 729 100         if (!sv_isobject(obj)) {
1694 35 50         if (flags & OR_DIE)
1695 0           croak("Not an object");
1696 35           return NULL;
1697             }
1698 694           sv= SvRV(obj);
1699 694 50         if (SvMAGICAL(sv) && (magic= mg_findext(sv, PERL_MAGIC_ext, &TreeRBXS_item_magic_vt)))
    100          
1700 452           return (struct TreeRBXS_item*) magic->mg_ptr;
1701              
1702 242 50         if (flags & OR_DIE)
1703 0           croak("Object lacks 'struct TreeRBXS_item' magic");
1704 242           return NULL;
1705             }
1706              
1707             // Return existing Node object, or create a new one.
1708             // Returned SV is a reference with active refcount, which is what the typemap
1709             // wants for returning a "struct TreeRBXS_item*" to perl-land
1710 298           static SV* TreeRBXS_wrap_item(struct TreeRBXS_item *item) {
1711             SV *obj;
1712             MAGIC *magic;
1713             // Since this is used in typemap, handle NULL gracefully
1714 298 100         if (!item)
1715 75           return &PL_sv_undef;
1716             // If there is already a node object, return a new reference to it.
1717 223 100         if (item->owner)
1718 118           return newRV_inc(item->owner);
1719             // else create a node object
1720 105           item->owner= newSV(0);
1721 105           magic= sv_magicext(item->owner, NULL, PERL_MAGIC_ext, &TreeRBXS_item_magic_vt, (const char*) item, 0);
1722             #ifdef USE_ITHREADS
1723             magic->mg_flags |= MGf_DUP;
1724             #else
1725             (void)magic; // suppress warning
1726             #endif
1727 105           obj= newRV_inc(item->owner);
1728 105           sv_bless(obj, gv_stashpv("Tree::RB::XS::Node", GV_ADD));
1729 105           return obj;
1730             }
1731              
1732 926           static SV* TreeRBXS_item_wrap_key(struct TreeRBXS_item *item) {
1733 926 100         if (!item)
1734 24           return &PL_sv_undef;
1735 902 100         if (item->orig_key_stored) {
1736 159           SV *tmp= GET_TreeRBXS_item_ORIG_KEY(item);
1737 159           return SvREFCNT_inc(tmp);
1738             }
1739 743           switch (item->key_type) {
1740 497           case KEY_TYPE_ANY:
1741 497           case KEY_TYPE_CLAIM: return SvREFCNT_inc(item->keyunion.svkey);
1742 81           case KEY_TYPE_INT: return newSViv(item->keyunion.ikey);
1743 5           case KEY_TYPE_FLOAT: return newSVnv(item->keyunion.nkey);
1744 159           case KEY_TYPE_USTR: return newSVpvn_flags(item->keyunion.ckey, item->ckeylen, SVf_UTF8);
1745 1           case KEY_TYPE_BSTR: return newSVpvn(item->keyunion.ckey, item->ckeylen);
1746 0           default: croak("BUG: un-handled key_type");
1747             }
1748             }
1749              
1750             // Can't figure out how to create new CV instances on the fly...
1751             /*
1752             static SV* TreeRBXS_wrap_iter(pTHX_ struct TreeRBXS_iter *iter) {
1753             SV *obj;
1754             CV *iter_next_cv;
1755             MAGIC *magic;
1756             // Since this is used in typemap, handle NULL gracefully
1757             if (!iter)
1758             return &PL_sv_undef;
1759             // If there is already a node object, return a new reference to it.
1760             if (iter->owner)
1761             return newRV_inc(iter->owner);
1762             // else create an iterator
1763             iter_next_cv= get_cv("Tree::RB::XS::Iter::next", 0);
1764             if (!iter_next_cv) croak("BUG: can't find Iter->next");
1765             obj= newRV_noinc((SV*)cv_clone(iter_next_cv));
1766             sv_bless(obj, gv_stashpv("Tree::RB::XS::Iter", GV_ADD));
1767             magic= sv_magicext(SvRV(obj), NULL, PERL_MAGIC_ext, &TreeRBXS_iter_magic_vt, (const char*) iter, 0);
1768             #ifdef USE_ITHREADS
1769             magic->mg_flags |= MGf_DUP;
1770             #else
1771             (void)magic; // suppress warning
1772             #endif
1773             return obj;
1774             }
1775             */
1776              
1777             // Return the TreeRBXS_iter struct attached to a Perl object via MAGIC.
1778             // The 'obj' should be a reference to a blessed SV.
1779             // Use AUTOCREATE to attach magic and allocate a struct if it wasn't present.
1780             // Use OR_DIE for a built-in croak() if the return value would be NULL.
1781 2241           static struct TreeRBXS_iter* TreeRBXS_get_magic_iter(SV *obj, int flags) {
1782             SV *sv;
1783             MAGIC* magic;
1784             struct TreeRBXS_iter *iter;
1785 2241 50         if (!sv_isobject(obj)) {
1786 0 0         if (flags & OR_DIE)
1787 0           croak("Not an object");
1788 0           return NULL;
1789             }
1790 2241           sv= SvRV(obj);
1791 2241 50         if (SvMAGICAL(sv) && (magic= mg_findext(sv, PERL_MAGIC_ext, &TreeRBXS_iter_magic_vt)))
    100          
1792 1727           return (struct TreeRBXS_iter*) magic->mg_ptr;
1793              
1794 514 100         if (flags & AUTOCREATE) {
1795 257           Newxz(iter, 1, struct TreeRBXS_iter);
1796 257           magic= sv_magicext(sv, NULL, PERL_MAGIC_ext, &TreeRBXS_iter_magic_vt, (const char*) iter, 0);
1797             #ifdef USE_ITHREADS
1798             magic->mg_flags |= MGf_DUP;
1799             #endif
1800 257           iter->owner= sv;
1801 257           return iter;
1802             }
1803 257 50         else if (flags & OR_DIE)
1804 0           croak("Object lacks 'struct TreeRBXS_iter' magic");
1805 257           return NULL;
1806             }
1807              
1808             #define FUNCTION_IS_LVALUE(x) function_is_lvalue(aTHX_ stash, #x)
1809 65           static void function_is_lvalue(pTHX_ HV *stash, const char *name) {
1810             CV *method_cv;
1811             GV *method_gv;
1812 65 50         if (!(method_gv= gv_fetchmethod(stash, name))
1813 65 50         || !(method_cv= GvCV(method_gv)))
1814 0           croak("Missing method %s", name);
1815 65           CvLVALUE_on(method_cv);
1816 65           }
1817              
1818             #define EXPORT_ENUM(x) newCONSTSUB(stash, #x, new_enum_dualvar(aTHX_ x, newSVpvs_share(#x)))
1819 348           static SV * new_enum_dualvar(pTHX_ IV ival, SV *name) {
1820 348 50         SvUPGRADE(name, SVt_PVNV);
1821 348           SvIV_set(name, ival);
1822 348           SvIOK_on(name);
1823 348           SvREADONLY_on(name);
1824 348           return name;
1825             }
1826              
1827             /* Return an SV array of an AV.
1828             * Returns NULL if it wasn't an AV or arrayref.
1829             */
1830 41           static SV** unwrap_array(SV *array, IV *len) {
1831             AV *av;
1832             SV **vec;
1833             IV n;
1834 41 50         if (array && SvTYPE(array) == SVt_PVAV)
    100          
1835 3           av= (AV*) array;
1836 38 50         else if (array && SvROK(array) && SvTYPE(SvRV(array)) == SVt_PVAV)
    50          
    50          
1837 38           av= (AV*) SvRV(array);
1838             else
1839 0           return NULL;
1840 41           n= av_len(av) + 1;
1841 41           vec= AvARRAY(av);
1842             /* tied arrays and non-allocated empty arrays return NULL */
1843 41 100         if (!vec) {
1844 1 50         if (n == 0) /* don't return a NULL for an empty array, but doesn't need to be a real pointer */
1845 0           vec= (SV**) 8;
1846             else {
1847             /* in case of a tied array, extract the elements into a temporary buffer */
1848             IV i;
1849 1 50         Newx(vec, n, SV*);
1850 1           SAVEFREEPV(vec);
1851 11 100         for (i= 0; i < n; i++) {
1852 10           SV **el= av_fetch(av, i, 0);
1853 10 50         vec[i]= el? *el : NULL;
1854             }
1855             }
1856             }
1857 41 50         if (len) *len= n;
1858 41           return vec;
1859             }
1860              
1861             /* Initialize a new tree from settings in a list of perl SVs
1862             *
1863             * This functions expects that 'tree' has been suitably initialized so that if an attribute
1864             * does not occur in the list, it has already received a sensible default value.
1865             * The attr_list is assumed to be an array of mortal SVs such that they can be re-ordered
1866             * or removed freely, so that _init_tree can return the list of un-consumed attributes.
1867             * This means that attr_list should *NOT* be AvARRAY of an AV.
1868             * Returns the number of unknown attributes, which have been re-packed in the list.
1869             */
1870 113           IV init_tree_from_attr_list(
1871             struct TreeRBXS *tree,
1872             SV **attr_list,
1873             IV attr_list_len
1874             ) {
1875             IV i, out_i;
1876 113           SV *key_type_sv= NULL,
1877 113           *compare_fn_sv= NULL,
1878 113           *kv_sv= NULL,
1879 113           *keys_sv= NULL,
1880 113           *values_sv= NULL,
1881 113           *recent_sv= NULL,
1882 113           *hashiter_sv= NULL;
1883             int key_type;
1884 113           IV nodecount= 0;
1885             struct TreeRBXS_item *item, stack_item;
1886             rbtree_node_t *node;
1887              
1888             /* Begin iterating arguments, and store the SVs we know in variables, and put the
1889             * rest into the object hash. */
1890 253 100         for (i= out_i= 0; i < attr_list_len; i += 2) {
1891 140           SV *key= attr_list[i], *val;
1892             STRLEN len;
1893 140           const char *attrname= SvPV(key, len);
1894             /* every attribute needs a value */
1895 140 50         if (i + 1 == attr_list_len)
1896 0           croak("No value provided for %s", attrname);
1897 140           val= attr_list[i+1];
1898 140 50         if (!SvOK(val))
1899 0           val= NULL; /* prefer NULLs for unset attributes */
1900 140           switch (len) {
1901 29 50         case 2: if (strcmp("kv", attrname) == 0) { kv_sv= val; break; }
1902 0           else goto keep_unknown;
1903 5 50         case 4: if (strcmp("keys", attrname) == 0) { keys_sv= val; break; }
1904 0           else goto keep_unknown;
1905 3 100         case 6: if (strcmp("values", attrname) == 0) { values_sv= val; break; }
1906 1 50         else if (strcmp("recent", attrname) == 0) { recent_sv= val; break; }
1907 0           else goto keep_unknown;
1908 24 50         case 8: if (strcmp("key_type", attrname) == 0) { key_type_sv= val; break; }
1909 0 0         else if (strcmp("hashiter", attrname) == 0) { hashiter_sv= val; break; }
1910 0           else goto keep_unknown;
1911 56 50         case 10: if (strcmp("compare_fn", attrname) == 0) { compare_fn_sv= val; break; }
1912 0           else goto keep_unknown;
1913 10 100         case 12: if (strcmp("track_recent", attrname) == 0) { tree->track_recent= val && SvTRUE(val); break; }
    50          
    50          
1914 1 50         else if (strcmp("recent_limit", attrname) == 0) {
1915 1 50         tree->recent_limit= val && SvOK(val) && SvIV(val) >= 0? SvIV(val) : -1;
    50          
    50          
1916 1           break;
1917             }
1918 0           else goto keep_unknown;
1919 0 0         case 15: if (strcmp("compat_list_get", attrname) == 0) { tree->compat_list_get= val && SvTRUE(val); break; }
    0          
    0          
1920 0           else goto keep_unknown;
1921 10 50         case 16: if (strcmp("allow_duplicates", attrname) == 0) { tree->allow_duplicates= val && SvTRUE(val); break; }
    50          
    50          
1922 0           else goto keep_unknown;
1923 3 50         case 20: if (strcmp("keys_in_recent_order", attrname) == 0) { tree->keys_in_recent_order= val && SvTRUE(val); break; }
    50          
    50          
1924 0           else goto keep_unknown;
1925 0 0         case 21: if (strcmp("lookup_updates_recent", attrname) == 0) { tree->lookup_updates_recent= val && SvTRUE(val); break; }
    0          
    0          
1926              
1927 0           default: keep_unknown:
1928             /* unknown attribute. Re-pack it into the list */
1929 0 0         if (i > out_i) {
1930 0           attr_list[out_i]= attr_list[i];
1931 0           attr_list[out_i+1]= attr_list[i+1];
1932             }
1933 0           out_i += 2;
1934             }
1935             }
1936              
1937             // parse key type and compare_fn
1938 113 100         if (key_type_sv) {
1939 24           key_type= parse_key_type(key_type_sv);
1940 24 50         if (key_type < 0)
1941 0           croak("invalid key_type %s", SvPV_nolen(key_type_sv));
1942 24           tree->key_type= key_type;
1943             }
1944 89           else key_type= tree->key_type;
1945            
1946 113 100         if (compare_fn_sv) {
1947 56           int cmp_id= parse_cmp_fn(compare_fn_sv);
1948 56 50         if (cmp_id < 0)
1949 0           croak("invalid compare_fn %s", SvPV_nolen(compare_fn_sv));
1950 56           tree->compare_fn_id= cmp_id;
1951 57 100         } else if (key_type_sv) {
1952 21           tree->compare_fn_id=
1953             key_type == KEY_TYPE_INT? CMP_INT
1954 31 100         : key_type == KEY_TYPE_FLOAT? CMP_FLOAT
1955 17 100         : key_type == KEY_TYPE_BSTR? CMP_MEMCMP
1956 11 100         : key_type == KEY_TYPE_USTR? CMP_STR
1957 4 100         : key_type == KEY_TYPE_ANY? CMP_PERL /* use Perl's cmp operator */
1958             : key_type == KEY_TYPE_CLAIM? CMP_PERL
1959             : CMP_PERL;
1960             }
1961              
1962 113           switch (tree->compare_fn_id) {
1963 8           case CMP_SUB:
1964 8 50         if (!compare_fn_sv || !SvRV(compare_fn_sv) || SvTYPE(SvRV(compare_fn_sv)) != SVt_PVCV)
    50          
    50          
1965 0           croak("Can't set compare_fn to CMP_SUB without supplying a coderef");
1966 8           tree->compare_callback= compare_fn_sv;
1967 8           SvREFCNT_inc(tree->compare_callback);
1968 8 50         if (key_type != KEY_TYPE_CLAIM) tree->key_type= KEY_TYPE_ANY;
1969 8           tree->compare= TreeRBXS_cmp_perl_cb;
1970 8           break;
1971 12           case CMP_FOLDCASE:
1972 12           tree->transform= TreeRBXS_xform_fc;
1973 18           case CMP_STR:
1974 18           tree->key_type= KEY_TYPE_USTR;
1975 18           tree->compare= TreeRBXS_cmp_memcmp;
1976 18           break;
1977 40           case CMP_PERL:
1978 40 100         if (key_type != KEY_TYPE_CLAIM) tree->key_type= KEY_TYPE_ANY;
1979 40           tree->compare= TreeRBXS_cmp_perl;
1980 40           break;
1981 25           case CMP_INT:
1982 25           tree->key_type= KEY_TYPE_INT;
1983 25           tree->compare= TreeRBXS_cmp_int;
1984 25           break;
1985 7           case CMP_FLOAT:
1986 7           tree->key_type= KEY_TYPE_FLOAT;
1987 7           tree->compare= TreeRBXS_cmp_float;
1988 7           break;
1989 6           case CMP_MEMCMP:
1990 6           tree->key_type= KEY_TYPE_BSTR;
1991 6           tree->compare= TreeRBXS_cmp_memcmp;
1992 6           break;
1993 4           case CMP_NUMSPLIT_FOLDCASE:
1994 4           tree->key_type= KEY_TYPE_USTR;
1995 4           tree->transform= TreeRBXS_xform_fc;
1996 4           tree->compare= TreeRBXS_cmp_numsplit;
1997 4           break;
1998 5           case CMP_NUMSPLIT:
1999 5 100         if (key_type != KEY_TYPE_USTR && key_type != KEY_TYPE_ANY && key_type != KEY_TYPE_CLAIM)
    100          
    50          
2000 1           tree->key_type= KEY_TYPE_BSTR;
2001 5           tree->compare= TreeRBXS_cmp_numsplit;
2002 5           break;
2003 0           default:
2004 0           croak("BUG: unhandled cmp_id");
2005             }
2006              
2007             /* if keys and/or values supplied... */
2008 113 100         if (kv_sv || keys_sv || values_sv) {
    100          
    50          
2009 34           SV **key_vec= NULL, **key_lim, **val_vec= NULL;
2010 34           IV num_kv, key_step= 0, val_step= 0;
2011 34           bool track_recent= tree->track_recent;
2012            
2013 34 100         if (kv_sv) {
2014 29 50         if (keys_sv || values_sv)
    50          
2015 0           croak("'kv' cannot be specified at the same time as 'keys' or 'values'");
2016 29 50         if (!(key_vec= unwrap_array(kv_sv, &num_kv)))
2017 0           croak("'kv' must be an arrayref");
2018 29 50         if (num_kv & 1)
2019 0           croak("Odd number of elements in 'kv' array");
2020 29           num_kv >>= 1;
2021 29           val_vec= key_vec+1;
2022 29           key_step= val_step= 2;
2023             }
2024 34 100         if (keys_sv) {
2025 5 50         if (!(key_vec= unwrap_array(keys_sv, &num_kv)))
2026 0           croak("'keys' must be an arrayref");
2027 5           key_step= 1;
2028             }
2029 34 100         if (values_sv) {
2030             IV nvals;
2031 2 50         if (!key_vec)
2032 0           croak("'values' can't be specified without 'keys'");
2033 2 50         if (!(val_vec= unwrap_array(values_sv, &nvals)))
2034 0           croak("'values' must be an arrayref");
2035 2 50         if (nvals != num_kv)
2036 0           croak("Length of 'values' array (%ld) does not match keys (%ld)", (long)nvals, (long)num_kv);
2037 2           val_step= 1;
2038             }
2039             /* If recent list is about to be overwritten, don't track any insertions */
2040 34 100         if (recent_sv)
2041 1           tree->track_recent= false;
2042 194 100         for (key_lim= key_vec + num_kv * key_step; key_vec < key_lim; key_vec += key_step, val_vec += val_step) {
2043 160 100         TreeRBXS_init_tmp_item(&stack_item, tree, (*key_vec? *key_vec : &PL_sv_undef), (val_vec? *val_vec : &PL_sv_undef));
    50          
2044 160           TreeRBXS_insert_item(tree, &stack_item, !tree->allow_duplicates, NULL);
2045             }
2046             /* restore tracking setting */
2047 34           tree->track_recent= track_recent;
2048             /* might not equal num_kv if there were duplicates */
2049 34           nodecount= TreeRBXS_get_count(tree);
2050             }
2051             /* user wants to initialize the linked list of track_recent */
2052 113 100         if (recent_sv) {
2053             IV i, idx, n;
2054 1           SV **rvec= unwrap_array(recent_sv, &n);
2055 1 50         if (!rvec) croak("'recent' must be an arrayref");
2056 24 100         for (i= 0; i < n; i++) {
2057 23 50         if (!looks_like_integer(rvec[i]))
2058 0           croak("Elements of 'recent' must be integers");
2059 23           idx= SvIV(rvec[i]);
2060 23 50         if (idx < 0 || idx >= nodecount)
    50          
2061 0           croak("Element in 'recent' (%ld) is out of bounds (0-%ld)", (long)idx, (long)(nodecount-1));
2062 23           node= rbtree_node_child_at_index(TreeRBXS_get_root(tree), idx);
2063 23 50         if (!node) croak("BUG: access node[idx]");
2064 23           item= GET_TreeRBXS_item_FROM_rbnode(node);
2065 23           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
2066             }
2067             }
2068             /* hashiter_sv restores the state of tied-hash iteration */
2069 113 50         if (hashiter_sv) {
2070 0           struct TreeRBXS_iter *iter= TreeRBXS_get_hashiter(tree);
2071             IV idx;
2072 0 0         if (!looks_like_integer(hashiter_sv))
2073 0           croak("Expected integer for 'hashiter'");
2074 0           idx= SvIV(hashiter_sv);
2075 0 0         if (idx < 0 || idx >= nodecount)
    0          
2076 0           croak("'hashiter' value out of bounds");
2077 0           node= rbtree_node_child_at_index(TreeRBXS_get_root(tree), idx);
2078 0 0         if (!node) croak("BUG: access node[idx]");
2079 0           item= GET_TreeRBXS_item_FROM_rbnode(node);
2080 0           TreeRBXS_iter_set_item(iter, item);
2081             }
2082              
2083             /* return number of attribute (k,v) remaining in the supplied list */
2084 113           return out_i;
2085             }
2086              
2087 7           int get_integer_version() {
2088 7           SV *version= get_sv("Tree::RB::XS::VERSION", 0);
2089 7 50         if (!version || !SvOK(version))
    50          
2090 0           croak("$Tree::RB::XS::VERSION is not defined");
2091 7           return (int)(SvNV(version) * 1000000);
2092             }
2093              
2094             /*----------------------------------------------------------------------------
2095             * Tree Methods
2096             */
2097              
2098             MODULE = Tree::RB::XS PACKAGE = Tree::RB::XS
2099              
2100             void
2101             new(obj_or_pkg, ...)
2102             SV *obj_or_pkg
2103             ALIAS:
2104             Tree::RB::XS::TIEHASH = 0
2105             Tree::RB::XS::_init_tree = 1
2106             INIT:
2107 110           struct TreeRBXS *tree= NULL;
2108 110           SV *objref= NULL, **attr_list;
2109 110           HV *obj_hv= NULL, *pkg= NULL;
2110             IV n_unknown, i, attr_len;
2111             PPCODE:
2112 110 50         if (sv_isobject(obj_or_pkg) && SvTYPE(SvRV(obj_or_pkg)) == SVt_PVHV) {
    0          
2113 0           objref= obj_or_pkg;
2114 0           obj_hv= (HV*) SvRV(objref);
2115             }
2116 110 50         else if (SvPOK(obj_or_pkg) && (pkg= gv_stashsv(obj_or_pkg, 0))) {
    50          
2117 110 50         if (!sv_derived_from(obj_or_pkg, "Tree::RB::XS"))
2118 0           croak("Package %s does not derive from Tree:RB::XS", SvPV_nolen(obj_or_pkg));
2119 110           obj_hv= newHV();
2120 110           objref= sv_2mortal(newRV_noinc((SV*)obj_hv));
2121 110           sv_bless(objref, pkg);
2122 110           ST(0)= objref;
2123             }
2124             else
2125 0 0         croak("%s: first arg must be package name or blessed object", ix == 1? "_init_tree":"new");
2126            
2127             /* Special cases for 'new': it can be compare_fn, or a hashref */
2128 110 100         if (items == 2) {
2129 4           SV *first= ST(1);
2130             /* non-ref means a compare_fn constant, likewise for coderef */
2131 4 100         if (!SvROK(first) || SvTYPE(SvRV(first)) == SVt_PVCV) {
    50          
2132 3           Newx(attr_list, 2, SV*);
2133 3           SAVEFREEPV(attr_list);
2134 3           attr_list[0]= newSVpvs("compare_fn");
2135 3           attr_list[1]= first;
2136 3           attr_len= 2;
2137             }
2138 1 50         else if (SvTYPE(SvRV(first)) == SVt_PVHV) {
2139 1           HV *attrhv= (HV*) SvRV(first);
2140 1           IV n= hv_iterinit(attrhv);
2141             HE *ent;
2142 1           attr_len= n*2;
2143 1 50         Newx(attr_list, attr_len, SV*);
2144 1           SAVEFREEPV(attr_list);
2145 1           i= 0;
2146 4 100         while ((ent= hv_iternext(attrhv)) && i < attr_len) {
    50          
2147 3           attr_list[i++]= hv_iterkeysv(ent);
2148 3           attr_list[i++]= hv_iterval(attrhv, ent);
2149             }
2150             }
2151 0           else croak("Expected compare_fn constant, coderef, hashref, or key/value pairs");
2152             } else {
2153 106           attr_list= PL_stack_base+ax+1;
2154 106           attr_len= items - 1;
2155             }
2156              
2157             /* Upgrade this object to have TreeRBXS struct attached magically */
2158 110           tree= TreeRBXS_get_magic_tree(objref, AUTOCREATE|OR_DIE);
2159 110 50         if (tree->owner != (SV*) obj_hv)
2160 0           croak("Tree was already initialized");
2161              
2162 110           n_unknown= init_tree_from_attr_list(tree, attr_list, attr_len);
2163 110 50         if (n_unknown) {
2164             /* if called by the public constructor, throw an error */
2165 0 0         if (ix == 0)
2166 0           croak("Unknown attribute %s", SvPV_nolen(attr_list[0]));
2167             /* else return them to caller. They might already be in the stack. */
2168 0 0         if (attr_list != PL_stack_base+ax+1) {
2169 0 0         EXTEND(SP, n_unknown);
    0          
2170 0 0         for (i= 0; i < n_unknown; i++)
2171 0           ST(i+1)= attr_list[i];
2172             }
2173             }
2174 110 50         i= ix == 0? 1 : n_unknown;
2175 110           XSRETURN(i);
2176              
2177             void
2178             _assert_structure(tree)
2179             struct TreeRBXS *tree
2180             CODE:
2181 19           TreeRBXS_assert_structure(tree);
2182              
2183             void
2184             STORABLE_freeze(tree, cloning)
2185             struct TreeRBXS *tree
2186             bool cloning
2187             INIT:
2188 4           IV nodecount= TreeRBXS_get_count(tree);
2189 4           rbtree_node_t *node= rbtree_node_left_leaf(TreeRBXS_get_root(tree));
2190             struct TreeRBXS_item *item;
2191 4           int i, cmp_id= tree->compare_fn_id, key_type= tree->key_type,
2192 4           flags, version= get_integer_version();
2193             unsigned char sb[14];
2194 4           AV *attrs= newAV();
2195 4           SV *attrs_ref= sv_2mortal(newRV_noinc((SV*) attrs));
2196 4           HV *treehv= (HV*) tree->owner;
2197             HE *pos;
2198             PPCODE:
2199             /* dump out the contents of the hashref, which is empty unless set by subclass */
2200 4           hv_iterinit(treehv);
2201 4 50         while ((pos= hv_iternext(treehv))) {
2202 0           av_push(attrs, SvREFCNT_inc(hv_iterkeysv(pos)));
2203 0           av_push(attrs, newSVsv(hv_iterval(treehv, pos)));
2204             }
2205 4 50         if (tree->recent_limit >= 0) {
2206 0           av_push(attrs, newSVpvs("recent_limit"));
2207 0           av_push(attrs, newSViv(tree->recent_limit));
2208             }
2209              
2210             /* Build lists of keys and values */
2211 4 100         if (nodecount) {
2212             AV *keys_av, *values_av;
2213 2           SV *keys= NULL, *values= NULL;
2214             //IV *keys_ivec;
2215             //NV *keys_nvec;
2216             //bool all_one= true, all_undef= true;
2217 2           values_av= newAV();
2218 2           values= sv_2mortal(newRV_noinc((SV*) values_av));
2219 2           av_extend(values_av, nodecount-1);
2220             /* decide whether keys will be an AV, or packed in a buffer */
2221             //if (key_type == KEY_TYPE_INT) {
2222             // /* allocate a buffer of ints */
2223             // svtmp= make_aligned_buffer(NULL, sizeof(IV)*nodecount, sizeof(IV));
2224             // keys_ivec= (IV*) SvPVX(svtmp);
2225             // keys= newRV_noinc(svtmp);
2226             //} else if (key_type == KEY_TYPE_FLOAT) {
2227             // /* allocate a buffer of NV */
2228             // svtmp= make_aligned_buffer(NULL, sizeof(NV)*nodecount, sizeof(NV));
2229             // keys_nvec= (NV*) SvPVX(svtmp);
2230             // keys= newRV_noinc(svtmp);
2231             //} else {
2232 2           keys_av= newAV();
2233 2           keys= sv_2mortal(newRV_noinc((SV*) keys_av));
2234 2           av_extend(keys_av, nodecount-1);
2235             //}
2236             /* Now fill the key and value arrays */
2237 36 100         for (i= 0; i < nodecount && node; i++, node=rbtree_node_next(node)) {
    50          
2238 34           item= GET_TreeRBXS_item_FROM_rbnode(node);
2239             /* I think I need to populate this array with the exact SV from the tree so that
2240             * Storable can recognize repeat references that might live in the larger graph.
2241             * In normal circumstances the correct thing here is to newSVsv() so that
2242             * the array items aren't shared. */
2243 34           av_push(values_av, SvREFCNT_inc(item->value));
2244             /* for packed keys, write the existing buffer. else create a new SV */
2245             //switch (key_type) {
2246             //case KEY_TYPE_INT:
2247             // keys_ivec[i]= item->keyunion.ikey;
2248             // break;
2249             //case KEY_TYPE_FLOAT:
2250             // keys_nvec[i]= item->keyunion.nkey;
2251             // break;
2252             //case KEY_TYPE_ANY:
2253             //case KEY_TYPE_CLAIM: av_push(keys_av, SvREFCNT_inc(item->keyunion.svkey)); break;
2254             //default:
2255 34           av_push(keys_av, TreeRBXS_item_wrap_key(item));
2256             //}
2257             }
2258             /* sanity-check: ensure loop ended at expected count */
2259 2 50         if (i < nodecount) croak("BUG: too few nodes in tree");
2260 2 50         if (node) croak("BUG: too many nodes in tree");
2261             /* optimize key storage */
2262             //if (key_type == KEY_TYPE_INT) {
2263             // key_bits=
2264             //}
2265 2           av_push(attrs, newSVpvs("keys"));
2266 2           av_push(attrs, SvREFCNT_inc(keys));
2267 2           av_push(attrs, newSVpvs("values"));
2268 2           av_push(attrs, SvREFCNT_inc(values));
2269              
2270             /* if any nodes belong to the recent-list, emit that as an array of node indices */
2271 2 100         if (tree->recent_count) {
2272 1           struct dllist_node *root= &tree->recent, *pos= tree->recent.next;
2273 1           AV *recent_av= newAV();
2274 1           av_push(attrs, newSVpvs("recent"));
2275 1           av_push(attrs, newRV_noinc((SV*) recent_av));
2276 1           av_extend(recent_av, tree->recent_count-1);
2277 24 100         for (i= tree->recent_count; i > 0 && pos != root; --i, pos= pos->next) {
    50          
2278 23           item= GET_TreeRBXS_item_FROM_recent(pos);
2279 23           av_push(recent_av, newSViv(rbtree_node_index(&item->rbnode)));
2280             }
2281             /* sanity check */
2282 1 50         if (i != 0) croak("BUG: too few recent-tracked nodes");
2283 1 50         if (pos != root) croak("BUG: too many recent-tracked nodes");
2284             }
2285             /* and finally, the optional built-in iterator for when the tree is tied to a hash */
2286 2 50         if (tree->hashiter && tree->hashiter->item) {
    0          
2287             /* only need to initialize it if it is pointing somewhere other than the first
2288             * tree node. */
2289 0           IV pointing_at= rbtree_node_index(&tree->hashiter->item->rbnode);
2290 0 0         if (pointing_at) {
2291 0           av_push(attrs, newSVpvs("hashiter"));
2292 0           av_push(attrs, newSViv(pointing_at));
2293             }
2294             }
2295             }
2296             /* Attempting to clone a tree with a user-supplied callback comparison function will
2297             * fail, because Storable won't encode coderefs by default. This makes sense for
2298             * process-to-process serialization, but for dclone() I want to enable it so long as
2299             * the coderef is a global function. */
2300 4 100         if (cmp_id == CMP_SUB) {
2301 2 50         if (!cloning)
2302 0           croak("Can't serialize a Tree::RB::XS with a custom comparison coderef");
2303             else {
2304 2           CV *cv= (CV*) SvRV(tree->compare_callback);
2305 2           GV *gv= CvGV(cv), *re_gv;
2306 2           HV *stash= GvSTASH(gv);
2307 2           const char *name= GvNAME(gv);
2308            
2309 2 50         if (!stash || !name || !(re_gv= gv_fetchmethod(stash, name)) || GvCV(re_gv) != cv)
    50          
    100          
    50          
2310 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          
2311             stash? HvNAME(stash) : "NULL",
2312             name? name : "NULL"
2313             );
2314 1           av_push(attrs, newSVpvs("compare_fn"));
2315 1 50         av_push(attrs, newSVpvf("%s::%s", HvNAME(stash), name));
    50          
    50          
    0          
    50          
    50          
2316             }
2317             }
2318              
2319 3 50         if (version < 0 || version > 0x7FFFFFFF)
2320 0           croak("BUG: version out of bounds");
2321 3           sb[0]= version & 0xFF;
2322 3           sb[1]= (version >> 8) & 0xFF;
2323 3           sb[2]= (version >> 16) & 0xFF;
2324 3           sb[3]= (version >> 24) & 0xFF;
2325 3 50         if (key_type < 1 || key_type > 255)
    50          
2326 0           croak("BUG: key_type out of bounds");
2327 3           sb[4]= key_type & 0xFF;
2328 3           sb[5]= (key_type >> 8) & 0xFF;
2329 3 50         if (cmp_id < 1 || cmp_id > 255)
    50          
2330 0           croak("BUG: compare_fn outof bounds");
2331 3           sb[6]= cmp_id & 0xFF;
2332 3           sb[7]= (cmp_id >> 8) & 0xFF;
2333 6           flags= (tree->allow_duplicates? 1 : 0)
2334 3 50         | (tree->compat_list_get? 2 : 0)
2335 3 100         | (tree->track_recent? 4 : 0)
2336 3 50         | (tree->lookup_updates_recent? 8 : 0)
2337 3 50         | (tree->keys_in_recent_order? 0x10 : 0);
2338 3           sb[8]= flags & 0xFF;
2339 3           sb[9]= (flags >> 8) & 0xFF;
2340              
2341 3 50         EXTEND(SP, 2);
2342 3           ST(0)= sv_2mortal(newSVpvn((char*)sb, 10));
2343 3           ST(1)= attrs_ref;
2344 3           XSRETURN(2);
2345              
2346             void
2347             STORABLE_thaw(objref, cloning, serialized, attrs)
2348             SV *objref
2349             bool cloning
2350             SV *serialized
2351             AV *attrs
2352             INIT:
2353 3           struct TreeRBXS *tree= NULL;
2354             int version, cmp_id, key_type, flags, i;
2355             IV attr_len, n_unknown;
2356             const unsigned char *sb;
2357             STRLEN sb_len;
2358 3           SV **attr_vec= unwrap_array((SV*)attrs, &attr_len), **attr_list, *tmpsv;
2359             PPCODE:
2360 3 50         if (!SvROK(objref) || SvTYPE(SvRV(objref)) != SVt_PVHV)
    50          
2361 0           croak("Expected blessed hashref as first argument");
2362 3           tree= TreeRBXS_get_magic_tree(objref, AUTOCREATE|OR_DIE);
2363 3 50         if (tree->owner != SvRV(objref))
2364 0           croak("Tree was already initialized");
2365            
2366             /* unpack serialized fields */
2367 3           sb= (const unsigned char*) SvPV(serialized, sb_len);
2368 3 50         if (sb_len < 10)
2369 0           croak("Expected at least 10 bytes of serialized data");
2370 3           version= sb[0] + (sb[1] << 8) + (sb[2] << 16) + (sb[3] << 24); // 4 bytes LE
2371 3           key_type= sb[4] + (sb[5] << 8); // 2 bytes LE
2372 3           cmp_id= sb[6] + (sb[7] << 8); // 2 bytes LE
2373 3           flags= sb[8] + (sb[9] << 8); // 2 bytes LE
2374              
2375 3 50         if (version <= 0)
2376 0           croak("Invalid serialized version");
2377 3 50         if (version > get_integer_version())
2378 0           croak("Attempt to deserialize Tree::RB::XS from a newer version");
2379             /* STORABLE_freeze lists nodes exactly as they were, so alllow duplicates if present,
2380             * regardless of the final state of the allow_duplicates attribute. */
2381 3           tree->allow_duplicates= /* flags & 1 */ true; /* corrected below */
2382 3           tree->compat_list_get= flags & 2;
2383 3           tree->track_recent= flags & 4;
2384 3           tree->lookup_updates_recent= flags & 8;
2385 3           tree->keys_in_recent_order= flags & 0x10;
2386              
2387 3 50         if (key_type <= 0 || key_type > KEY_TYPE_MAX)
    50          
2388 0           croak("Invalid serialized key_type");
2389 3           tree->key_type= key_type;
2390              
2391 3 50         if (cmp_id <= 0 || cmp_id > CMP_MAX)
    50          
2392 0           croak("Invalid serialized compare_fn");
2393 3           tree->compare_fn_id= cmp_id;
2394             /* These two comparison function codes imply a transform function */
2395 3 50         if (cmp_id == CMP_FOLDCASE || cmp_id == CMP_NUMSPLIT_FOLDCASE)
    50          
2396 0           tree->transform= TreeRBXS_xform_fc;
2397              
2398             /* attr_vec gets modified, so make a copy of attrs' AvARRAY */
2399 3 50         Newx(attr_list, attr_len, SV*);
2400 3           SAVEFREEPV(attr_list);
2401 3           memcpy(attr_list, attr_vec, sizeof(SV*) * attr_len);
2402              
2403             /* If the comparison function is a coderef, try to look up the name of the function */
2404 3 100         if (cmp_id == CMP_SUB) {
2405 1 50         if (!cloning) croak("compare_fn lookup is forbidden unless cloning");
2406             /* look for attribute compare_fn, which is probably the final one */
2407 1 50         for (i= attr_len-2; i >= 0; i-= 2) {
2408 1 50         if (strcmp(SvPV_nolen(attr_list[i]), "compare_fn") == 0) {
2409             /* replace function name with coderef */
2410 1           GV *gv= gv_fetchsv(attr_list[i+1], 0, SVt_PVCV);
2411 1 50         if (gv && GvCV(gv))
    50          
2412 1           attr_list[i+1]= sv_2mortal(newRV_inc((SV*) GvCV(gv)));
2413             else
2414 0           croak("Can't find function %s", SvPV_nolen(attr_list[i+1]));
2415 1           break;
2416             }
2417             }
2418 1 50         if (i < 0)
2419 0           croak("No compare_fn name found in serialized data");
2420             }
2421              
2422 3           n_unknown= init_tree_from_attr_list(tree, attr_list, attr_len);
2423 3 50         if (n_unknown) {
2424 0           HV *obj= (HV*) SvRV(objref);
2425             /* store leftovers into the hashref of the object */
2426 0 0         for (i= 0; i < n_unknown-1; i += 2)
2427 0 0         if (!hv_store_ent(obj, attr_list[i], (tmpsv= newSVsv(attr_list[i+1])), 0))
2428 0           sv_2mortal(tmpsv);
2429             }
2430 3           tree->allow_duplicates= flags & 1; /* delayed for reason above */
2431 3           XSRETURN(0);
2432              
2433             void
2434             key_type(tree)
2435             struct TreeRBXS *tree
2436             INIT:
2437 20           int kt= tree->key_type;
2438             PPCODE:
2439 20           ST(0)= sv_2mortal(new_enum_dualvar(aTHX_ kt, newSVpv(get_key_type_name(kt), 0)));
2440 20           XSRETURN(1);
2441              
2442             void
2443             compare_fn(tree)
2444             struct TreeRBXS *tree
2445             INIT:
2446 18           int id= tree->compare_fn_id;
2447             PPCODE:
2448 18           ST(0)= id == CMP_SUB? tree->compare_callback
2449 18 100         : sv_2mortal(new_enum_dualvar(aTHX_ id, newSVpv(get_cmp_name(id), 0)));
2450 18           XSRETURN(1);
2451              
2452             void
2453             allow_duplicates(tree, allow= NULL)
2454             struct TreeRBXS *tree
2455             SV* allow
2456             PPCODE:
2457 15 100         if (items > 1) {
2458 4           tree->allow_duplicates= SvTRUE(allow);
2459             // ST(0) is $self, so let it be the return value
2460             } else {
2461 11           ST(0)= sv_2mortal(newSViv(tree->allow_duplicates? 1 : 0));
2462             }
2463 15           XSRETURN(1);
2464              
2465             void
2466             compat_list_get(tree, allow= NULL)
2467             struct TreeRBXS *tree
2468             SV* allow
2469             PPCODE:
2470 2 50         if (items > 1) {
2471 0           tree->compat_list_get= SvTRUE(allow);
2472             // ST(0) is $self, so let it be the return value
2473             } else {
2474 2 50         ST(0)= tree->compat_list_get? &PL_sv_yes : &PL_sv_no;
2475             }
2476 2           XSRETURN(1);
2477              
2478             void
2479             track_recent(tree, enable= NULL)
2480             struct TreeRBXS *tree
2481             SV* enable
2482             PPCODE:
2483 2 50         if (items > 1) {
2484 0           tree->track_recent= SvTRUE(enable);
2485             // ST(0) is $self, so let it be the return value
2486             } else {
2487 2 50         ST(0)= tree->track_recent? &PL_sv_yes : &PL_sv_no;
2488             }
2489 2           XSRETURN(1);
2490              
2491             void
2492             lookup_updates_recent(tree, enable= NULL)
2493             struct TreeRBXS *tree
2494             SV *enable
2495             PPCODE:
2496 3 100         if (items > 1) {
2497 1           tree->lookup_updates_recent= SvTRUE(enable);
2498             //ST(0) is $self, so let it be the return value
2499             } else {
2500 2 50         ST(0)= tree->lookup_updates_recent? &PL_sv_yes : &PL_sv_no;
2501             }
2502 3           XSRETURN(1);
2503              
2504             void
2505             keys_in_recent_order(tree, enable= NULL)
2506             struct TreeRBXS *tree
2507             SV *enable
2508             PPCODE:
2509 1 50         if (items > 1) {
2510 1           tree->keys_in_recent_order= SvTRUE(enable);
2511             //ST(0) is $self, so let it be the return value
2512             } else {
2513 0 0         ST(0)= tree->keys_in_recent_order? &PL_sv_yes : &PL_sv_no;
2514             }
2515 1           XSRETURN(1);
2516              
2517             IV
2518             size(tree)
2519             struct TreeRBXS *tree
2520             CODE:
2521 56 50         RETVAL= TreeRBXS_get_count(tree);
2522             OUTPUT:
2523             RETVAL
2524              
2525             IV
2526             recent_count(tree)
2527             struct TreeRBXS *tree;
2528             CODE:
2529 9 50         RETVAL= tree->recent_count;
2530             OUTPUT:
2531             RETVAL
2532              
2533             void
2534             recent_limit(tree, newval=NULL)
2535             struct TreeRBXS *tree
2536             SV *newval
2537             PPCODE:
2538 4 100         if (newval) {
2539 2 100         if (!SvOK(newval)) {
2540 1           tree->recent_limit= -1;
2541 1 50         } else if (!looks_like_number(newval) || SvIV(newval) < 0) {
    50          
2542 0           croak("Expected non-negative integer");
2543             } else {
2544 1           tree->recent_limit= SvIV(newval);
2545 1           TreeRBXS_truncate_recent(tree, tree->recent_limit, NULL);
2546             }
2547             //ST(0) is $self, so let it be the return value
2548             } else {
2549 2           ST(0)= tree->recent_limit < 0? &PL_sv_undef
2550 2 100         : sv_2mortal(newSViv(tree->recent_limit));
2551             }
2552 4           XSRETURN(1);
2553              
2554             void
2555             _insert_optimization_debug(tree)
2556             struct TreeRBXS *tree;
2557             PPCODE:
2558 1 50         EXTEND(SP, 5);
2559 1           PUSHs(sv_2mortal(newSViv(tree->prev_inserted_trend)));
2560 1           PUSHs(sv_2mortal(newSViv(INSERT_TREND_TRIGGER)));
2561 1           PUSHs(sv_2mortal(newSViv(INSERT_TREND_CAP)));
2562 1           PUSHs(sv_2mortal(newSViv(tree->prev_inserted_dup? 1 : 0)));
2563              
2564             void
2565             insert(tree, key, val=&PL_sv_undef)
2566             struct TreeRBXS *tree
2567             SV *key
2568             SV *val
2569             ALIAS:
2570             Tree::RB::XS::put = 1
2571             Tree::RB::XS::STORE = 1
2572             Tree::RB::XS::insert_as_node = 2
2573             Tree::RB::XS::put_as_node = 3
2574             INIT:
2575             struct TreeRBXS_item stack_item, *inserted;
2576 500341           SV *oldval= NULL;
2577             PPCODE:
2578             //TreeRBXS_assert_structure(tree);
2579 500341 50         if (!SvOK(key))
2580 0           croak("Can't use undef as a key");
2581 500341           TreeRBXS_init_tmp_item(&stack_item, tree, key, val);
2582 500341           inserted= TreeRBXS_insert_item(tree, &stack_item, (ix & 1), &oldval);
2583 100133 100         ST(0)= ix == 0? sv_2mortal(newSViv(inserted? rbtree_node_index(&inserted->rbnode) : -1))
2584 1300751 100         : ix == 1? (oldval? oldval : &PL_sv_undef)
    100          
2585 800410 100         : (inserted? sv_2mortal(TreeRBXS_wrap_item(inserted)) : &PL_sv_undef);
    100          
2586 500341           XSRETURN(1);
2587              
2588             IV
2589             insert_multi(tree, ...)
2590             struct TreeRBXS *tree
2591             ALIAS:
2592             Tree::RB::XS::put_multi = 1
2593             INIT:
2594             struct TreeRBXS_item stack_item, *inserted;
2595 4           AV *av= NULL;
2596             SV *key, *val, *oldval, **el;
2597 4           int added= 0, i, lim;
2598             CODE:
2599             // Is there exactly one element which is an un-blessed arrayref?
2600 4 50         if (items == 2 && SvROK(ST(1)) && SvTYPE(SvRV(ST(1))) == SVt_PVAV && !sv_isobject(ST(1))) {
    0          
    0          
    0          
2601 0           av= (AV*) SvRV(ST(1));
2602 0           i= 0;
2603 0           lim= av_len(av)+1;
2604             } else {
2605 4           i= 1;
2606 4           lim= items;
2607             }
2608             // Iterate either the array or the stack
2609 519 100         while (i < lim) {
2610 515           val= &PL_sv_undef;
2611             // either iterating an array, or iterating the stack
2612 515 50         if (av) {
2613 0           el= av_fetch(av, i, 0);
2614 0 0         if (!el) croak("Tree->insert_multi does not support tied or sparse arrays");
2615 0           key= *el;
2616 0           i++;
2617 0 0         if (i < lim) {
2618 0           el= av_fetch(av, i, 0);
2619 0 0         if (!el) croak("Tree->insert_multi does not support tied or sparse arrays");
2620 0           val= *el;
2621 0           i++;
2622             }
2623             } else {
2624 515           key= ST(i);
2625 515 50         if (++i < lim) {
2626 515           val= ST(i);
2627 515           i++;
2628             }
2629             }
2630 515 50         if (!SvOK(key))
2631 0           croak("Can't use undef as a key");
2632 515           TreeRBXS_init_tmp_item(&stack_item, tree, key, val);
2633 515           oldval= NULL;
2634 515           inserted= TreeRBXS_insert_item(tree, &stack_item, ix == 1, &oldval);
2635             // Count the newly added nodes. For insert, that is the number of non-null 'inserted'.
2636             // For put, that is the number of inserts that did not return an old value.
2637 515 100         if (ix == 0? (inserted != NULL) : (oldval == NULL))
    100          
2638 507           ++added;
2639             }
2640 4 50         RETVAL= added;
2641             OUTPUT:
2642             RETVAL
2643              
2644             IV
2645             exists(tree, ...)
2646             struct TreeRBXS *tree
2647             ALIAS:
2648             Tree::RB::XS::EXISTS = 1
2649             INIT:
2650             struct TreeRBXS_item stack_item;
2651             rbtree_node_t *node;
2652             SV *key;
2653             int i, cmp;
2654 4           size_t count, total= 0;
2655             CODE:
2656             (void) ix; /* unused */
2657 8 100         for (i= 1; i < items; i++) {
2658 4           key= ST(i);
2659 4           TreeRBXS_init_tmp_item(&stack_item, tree, key, &PL_sv_undef);
2660 4 100         if (tree->allowed_duplicates) {
2661 1           count= 0;
2662 1           rbtree_find_all(&tree->root_sentinel,
2663             &stack_item,
2664 1           (int(*)(void*,void*,void*)) tree->compare,
2665             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2666             NULL, NULL, &count);
2667 1           total += count;
2668             } else {
2669 3           node= rbtree_find_nearest(
2670             &tree->root_sentinel,
2671             &stack_item,
2672 3           (int(*)(void*,void*,void*)) tree->compare,
2673             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2674             &cmp);
2675 3 100         if (node && cmp == 0)
    50          
2676 2           ++total;
2677             }
2678             }
2679 4 50         RETVAL= total;
2680             OUTPUT:
2681             RETVAL
2682              
2683             void
2684             get(tree, key, mode_sv= NULL)
2685             struct TreeRBXS *tree
2686             SV *key
2687             SV *mode_sv
2688             ALIAS:
2689             Tree::RB::XS::get_node = 0x00
2690             Tree::RB::XS::get_key = 0x01
2691             Tree::RB::XS::key = 0x01
2692             Tree::RB::XS::FETCH = 0x02
2693             Tree::RB::XS::lookup = 0x03
2694             Tree::RB::XS::get = 0x04
2695             Tree::RB::XS::get_node_ge = 0x10
2696             Tree::RB::XS::get_key_ge = 0x11
2697             Tree::RB::XS::get_node_le = 0x20
2698             Tree::RB::XS::get_key_le = 0x21
2699             Tree::RB::XS::get_node_gt = 0x30
2700             Tree::RB::XS::get_key_gt = 0x31
2701             Tree::RB::XS::get_node_lt = 0x40
2702             Tree::RB::XS::get_key_lt = 0x41
2703             Tree::RB::XS::get_node_last = 0x70
2704             Tree::RB::XS::get_node_le_last = 0x80
2705             Tree::RB::XS::get_or_add = 0x92
2706             INIT:
2707             struct TreeRBXS_item stack_item, *item;
2708 370           int mode= 0, n= 0;
2709             PPCODE:
2710 370 50         if (!SvOK(key))
2711 0           croak("Can't use undef as a key");
2712             // Extract the comparison enum from ix, or read it from mode_sv
2713 370 100         if (ix >> 4) {
2714 6           mode= (ix >> 4);
2715 6           ix &= 0xF;
2716 6 50         if (mode_sv)
2717 0           croak("extra get-mode argument");
2718             } else {
2719 364 100         mode= mode_sv? parse_lookup_mode(mode_sv) : GET_EQ;
2720 364 50         if (mode < 0)
2721 0           croak("Invalid lookup mode %s", SvPV_nolen(mode_sv));
2722             }
2723             // In "full compatibility mode", 'get' is identical to 'lookup' and depends on list context.
2724             // In scalar context, they both become the same as FETCH
2725 370 100         if (ix >= 3)
2726 249 100         ix= (GIMME_V == G_SCALAR || (ix == 4 && !tree->compat_list_get))? 2 : 3;
    100          
    50          
2727             // From here,
2728             // ix = 0 : return node
2729             // ix = 1 : return key
2730             // ix = 2 : return value
2731             // ix = 3 : return (value, node)
2732              
2733             // create a fake item to act as a search key
2734 370           TreeRBXS_init_tmp_item(&stack_item, tree, key, &PL_sv_undef);
2735 370           item= TreeRBXS_find_item(tree, &stack_item, mode);
2736 370 100         if (item) {
2737 339 100         if (tree->lookup_updates_recent)
2738 1           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
2739 339 50         if (GIMME_V == G_VOID)
2740 0           n= 0;
2741 339 100         else if (ix <= 1) {
2742 50 100         if (ix == 0) { // return node
2743 40           ST(0)= sv_2mortal(TreeRBXS_wrap_item(item));
2744 40           n= 1;
2745             } else { // return key
2746 10           ST(0)= sv_2mortal(TreeRBXS_item_wrap_key(item));
2747 10           n= 1;
2748             }
2749 289 100         } else if (ix == 2) { // return value
2750 273           ST(0)= item->value;
2751 273           n= 1;
2752             } else { // return (value, node)
2753 16           ST(0)= item->value;
2754 16           ST(1)= sv_2mortal(TreeRBXS_wrap_item(item));
2755 16           n= 2;
2756             }
2757             } else {
2758 31           ST(0)= &PL_sv_undef;
2759 31           n= (ix == 3)? 0 : 1; // empty list, else single undef
2760             }
2761 370           XSRETURN(n);
2762              
2763             void
2764             get_all(tree, key)
2765             struct TreeRBXS *tree
2766             SV *key
2767             INIT:
2768             struct TreeRBXS_item stack_item, *item;
2769             rbtree_node_t *first;
2770             size_t count, i;
2771             PPCODE:
2772 1 50         if (!SvOK(key))
2773 0           croak("Can't use undef as a key");
2774 1           TreeRBXS_init_tmp_item(&stack_item, tree, key, &PL_sv_undef);
2775 1 50         if (rbtree_find_all(
2776             &tree->root_sentinel,
2777             &stack_item,
2778 1           (int(*)(void*,void*,void*)) tree->compare,
2779             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2780             &first, NULL, &count)
2781             ) {
2782 1 50         EXTEND(SP, count);
2783 7 100         for (i= 0; i < count; i++) {
2784 6           item= GET_TreeRBXS_item_FROM_rbnode(first);
2785 6           ST(i)= item->value;
2786 6 50         if (tree->lookup_updates_recent)
2787 0           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
2788 6           first= rbtree_node_next(first);
2789             }
2790             } else
2791 0           count= 0;
2792 1           XSRETURN(count);
2793              
2794             IV
2795             delete(tree, key1, key2= NULL)
2796             struct TreeRBXS *tree
2797             SV *key1
2798             SV *key2
2799             INIT:
2800             struct TreeRBXS_item stack_item, *item;
2801             rbtree_node_t *first, *last, *node;
2802             size_t count, i;
2803             CODE:
2804 25 50         if (!SvOK(key1))
2805 0           croak("Can't use undef as a key");
2806 25           RETVAL= 0;
2807 25 50         if ((item= TreeRBXS_get_magic_item(key1, 0))) {
2808 0 0         if (!TreeRBXS_is_member(tree, item))
2809 0           croak("Node does not belong to this tree");
2810             }
2811             else {
2812 25           TreeRBXS_init_tmp_item(&stack_item, tree, key1, &PL_sv_undef);
2813 25 100         if (rbtree_find_all(
2814             &tree->root_sentinel,
2815             &stack_item,
2816 25           (int(*)(void*,void*,void*)) tree->compare,
2817             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2818             &first, &last, &count)
2819             ) {
2820 22 100         if (key2)
2821 1           last= NULL;
2822             }
2823             else {
2824             // Didn't find any matches. But if range is given, then start deleting
2825             // from the node following the key
2826 3 100         if (key2) {
2827 2           first= last;
2828 2           last= NULL;
2829             }
2830             }
2831             }
2832             // If a range is given, and the first part of the range found a node,
2833             // look for the end of the range.
2834 25 100         if (key2 && first) {
    50          
2835 3 50         if ((item= TreeRBXS_get_magic_item(key2, 0))) {
2836 0 0         if (!TreeRBXS_is_member(tree, item))
2837 0           croak("Node does not belong to this tree");
2838             }
2839             else {
2840 3           TreeRBXS_init_tmp_item(&stack_item, tree, key2, &PL_sv_undef);
2841 3 100         if (rbtree_find_all(
2842             &tree->root_sentinel,
2843             &stack_item,
2844 3           (int(*)(void*,void*,void*)) tree->compare,
2845             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
2846             &node, &last, NULL)
2847             ) {
2848             // first..last is ready to be deleted
2849             } else {
2850             // didn't match, so 'node' holds the final element before the key
2851 2           last= node;
2852             }
2853             }
2854             // Ensure that first comes before last
2855 3 50         if (last && rbtree_node_index(first) > rbtree_node_index(last))
    100          
2856 1           last= NULL;
2857             }
2858             // Delete the nodes if constructed a successful range
2859 25           i= 0;
2860 25 50         if (first && last) {
    100          
2861             do {
2862 26           item= GET_TreeRBXS_item_FROM_rbnode(first);
2863 26 100         first= (first == last)? NULL : rbtree_node_next(first);
2864 26           TreeRBXS_item_detach_tree(item, tree);
2865 26           ++i;
2866 26 100         } while (first);
2867             }
2868 25 100         RETVAL= i;
2869             OUTPUT:
2870             RETVAL
2871              
2872             void
2873             rekey(tree, ...)
2874             struct TreeRBXS *tree
2875             INIT:
2876 23           struct TreeRBXS_item *min_item= NULL, *max_item= NULL;
2877 23           SV *min_sv= NULL, *max_sv= NULL, *offset_sv= NULL;
2878             int i;
2879             PPCODE:
2880             /* Look for parameters named:
2881             offset
2882             min
2883             max
2884             */
2885 56 100         for (i= 1; i < items; i++) {
2886             STRLEN len;
2887 33           char *opt_name= SvPV(ST(i), len);
2888 33 50         if (++i >= items)
2889 0           croak("Expected value for key '%s'", opt_name);
2890 33           switch (len) {
2891 10           case 3:
2892 10           switch (opt_name[1]) {
2893 3 50         case 'a': if (strncmp(opt_name, "max", len) == 0) { max_sv= ST(i); continue; }
2894 7 50         case 'i': if (strncmp(opt_name, "min", len) == 0) { min_sv= ST(i); continue; }
2895             }
2896 0           break;
2897 23           case 6:
2898 23 50         if (strncmp(opt_name, "offset", len) == 0) { offset_sv= ST(i); continue; }
2899             }
2900 0           croak("Unknown option '%s'", opt_name);
2901             }
2902             /* Nothing to do for an empty tree */
2903 23 100         if (!TreeRBXS_get_count(tree))
2904 1           goto done;
2905             /* Translate min/max from iterators or keys to the nearest affected nodes */
2906 22 100         if (min_sv) {
2907 7           min_item= TreeRBXS_get_magic_item(min_sv, 0);
2908 7 100         if (!min_item) {
2909             struct TreeRBXS_iter *it;
2910 6 100         if (SvROK(min_sv) && (it= TreeRBXS_get_magic_iter(min_sv, 0))) {
    50          
2911 1           min_item= it->item;
2912 1 50         if (!min_item) croak("Iterator for 'min' is not referencing a tree node");
2913             }
2914             else {
2915             struct TreeRBXS_item stack_item;
2916 5           TreeRBXS_init_tmp_item(&stack_item, tree, min_sv, &PL_sv_undef);
2917 5           min_item= TreeRBXS_find_item(tree, &stack_item, GET_GE);
2918             }
2919             }
2920             }
2921 22 100         if (max_sv) {
2922 3           max_item= TreeRBXS_get_magic_item(max_sv, 0);
2923 3 100         if (!max_item) {
2924             struct TreeRBXS_iter *it;
2925 2 100         if (SvROK(max_sv) && (it= TreeRBXS_get_magic_iter(max_sv, 0))) {
    50          
2926 1           max_item= it->item;
2927 1 50         if (!max_item) croak("Iterator for 'max' is not referencing a tree node");
2928             }
2929             else {
2930             struct TreeRBXS_item stack_item;
2931 1           TreeRBXS_init_tmp_item(&stack_item, tree, max_sv, &PL_sv_undef);
2932 1           max_item= TreeRBXS_find_item(tree, &stack_item, GET_LE);
2933             }
2934             }
2935             }
2936 22 50         if (offset_sv) {
2937 22           int intmode= 0, positive= 0;
2938             IV offset_iv;
2939             NV offset_nv;
2940 22           struct TreeRBXS_item *first_item= GET_TreeRBXS_item_FROM_rbnode(rbtree_node_left_leaf(TreeRBXS_get_root(tree)));
2941 22           struct TreeRBXS_item *last_item= GET_TreeRBXS_item_FROM_rbnode(rbtree_node_right_leaf(TreeRBXS_get_root(tree)));
2942             struct TreeRBXS_item *edge_item;
2943             rbtree_node_t *boundary_node;
2944 22           rbtree_node_t* (*node_seek[2])(rbtree_node_t *)= { rbtree_node_prev, rbtree_node_next };
2945             /* offset can only be used for integer or floating point keys */
2946 22 100         if (tree->key_type == KEY_TYPE_INT) {
2947 18           intmode= 1;
2948             /* I could create a whole branch of UV comparisons and handling, but I don't feel like it */
2949 18 100         if (SvUOK(offset_sv) && SvUV(offset_sv) > IV_MAX)
    50          
2950 1           croak("Unsigned values larger than can fit in a signed IV are not supported. Patches welcome.");
2951 17           offset_iv= SvIV(offset_sv);
2952 17 50         if (offset_iv == 0)
2953 0           goto done;
2954             else
2955 17           positive= offset_iv > 0? 1 : 0;
2956             /* For integers, ensure there isn't an overflow */
2957 17 100         if (positive) {
2958 11 100         IV max_key= (max_item? max_item : last_item)->keyunion.ikey;
2959 11 100         if (IV_MAX - offset_iv < max_key)
2960 1           croak("Integer overflow when adding this offset (%"IVdf") to the maximum key (%"IVdf")", offset_iv, max_key);
2961             } else {
2962 6 100         IV min_key= (min_item? min_item : first_item)->keyunion.ikey;
2963 6 100         if (IV_MIN - offset_iv > min_key)
2964 1           croak("Integer overflow when adding this offset (%"IVdf") to the maximum key (%"IVdf")", offset_iv, min_key);
2965             }
2966             }
2967 4 100         else if (tree->key_type == KEY_TYPE_FLOAT) {
2968 2           offset_nv= SvNV(offset_sv);
2969 2 50         if (offset_nv == 0.0)
2970 0           goto done;
2971             else
2972 2           positive= offset_nv > 0? 1 : 0;
2973             }
2974 2           else croak("Option 'offset' may only be used on trees with integer or floating point numeric keys");
2975              
2976             /* If keys are increasing, compare max modified vs. node to the right of that.
2977             * If keys are decreasing, compare min modified vs. node to the left of that.
2978             * To prevent redundant code, use the 'positive' flag to indicate rightward
2979             * or leftward comparisons. The 'edge_item' is the one that needs checked for
2980             * collisions with its stationary neighbors, the first of thich is 'boundary_item'..
2981             */
2982 17 100         edge_item= positive? max_item : min_item;
2983 17 100         if (edge_item && (boundary_node= node_seek[positive](&edge_item->rbnode))) {
    100          
2984 4           void (*node_insert[2])(rbtree_node_t *parent, rbtree_node_t *child)=
2985             { rbtree_node_insert_before, rbtree_node_insert_after };
2986             struct TreeRBXS_item
2987 4           *boundary_item= GET_TreeRBXS_item_FROM_rbnode(boundary_node),
2988 1 50         *final_item= (positive? (min_item? min_item : first_item)
2989 5 100         : (max_item? max_item : last_item)),
    50          
2990             stack_item;
2991 4           TreeRBXS_init_tmp_item(&stack_item, tree, &PL_sv_no, &PL_sv_undef);
2992 5           while (1) {
2993 9 50         if (intmode) {
2994 9           IV newval= edge_item->keyunion.ikey + offset_iv;
2995 17 100         if (positive? (newval < boundary_item->keyunion.ikey)
    100          
2996 8           : (newval > boundary_item->keyunion.ikey)
2997 4           ) break; /* no longer overlappig boundary */
2998 5           stack_item.keyunion.ikey= newval;
2999             } else {
3000 0           NV newval= edge_item->keyunion.nkey + offset_nv;
3001 0 0         if (positive? (newval < boundary_item->keyunion.nkey)
    0          
3002 0           : (newval > boundary_item->keyunion.nkey)
3003 0           ) break; /* no longer overlappig boundary */
3004 0           stack_item.keyunion.nkey= newval;
3005             }
3006             /* There is an overlap. Perform a prune + insert. This can't re-use
3007             * the standard insertion code for nodes because that makes assumptions
3008             * that the tree is changing size, where in this case the size remains
3009             * the same. Also this code intentionally doesn't modify the 'recent'
3010             * order.
3011             *
3012             * In the spirit of preserving order, make sure this element inserts closest
3013             * to the boundary_item of any duplicates, so use rbtree_find_all to get the
3014             * leftmost/rightmost match, or else the nearest node to insert under.
3015             *
3016             * Also, wait until the last minute to perform the prune operation so
3017             * that if there is a perl exception in a compare function we don't leak
3018             * the node.
3019             */
3020 5           rbtree_node_t *next= node_seek[1-positive](&edge_item->rbnode);
3021             rbtree_node_t *search[2];
3022 5           bool found_identical= rbtree_find_all(
3023             &tree->root_sentinel,
3024             &stack_item,
3025 5           (int(*)(void*,void*,void*)) tree->compare,
3026             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
3027             &search[0], &search[1], NULL);
3028             /* remove */
3029 5           rbtree_node_prune(&edge_item->rbnode);
3030             /* alter key */
3031 5 50         if (intmode) edge_item->keyunion.ikey= stack_item.keyunion.ikey;
3032 0           else edge_item->keyunion.nkey= stack_item.keyunion.nkey;
3033 5 100         if (found_identical) {
3034             /* insert-before leftmost, or insert-after rightmost */
3035 1           node_insert[1-positive](search[1-positive], &edge_item->rbnode);
3036             /* remove conflicting nodes, if not permitted */
3037 1 50         if (!tree->allow_duplicates) {
3038 1           rbtree_node_t *node= search[0];
3039             do {
3040 1           struct TreeRBXS_item *item= GET_TreeRBXS_item_FROM_rbnode(node);
3041 1 50         node= (node == search[1])? NULL : rbtree_node_next(node);
3042 1 50         if (item != edge_item)
3043 1           TreeRBXS_item_detach_tree(item, tree);
3044 1 50         } while (node);
3045             /* boundary item may have just been deleted */
3046 1           boundary_item= GET_TreeRBXS_item_FROM_rbnode(node_seek[positive](next));
3047             }
3048             }
3049 4 100         else if (search[0])
3050 3           rbtree_node_insert_after(search[0], &edge_item->rbnode);
3051             else
3052 1           rbtree_node_insert_before(search[1], &edge_item->rbnode);
3053 5 50         if (!next || edge_item == final_item)
    50          
3054 0           goto done;
3055 5           edge_item= GET_TreeRBXS_item_FROM_rbnode(next);
3056             }
3057             /* The loop above ends as soon as there is no more overlap, or skips to end of
3058             * function when there are no more nodes to move. The code below will continue
3059             * modifying keys under the assumption that nothing collides and the tree
3060             * structure remains unchanged.
3061             */
3062 4 100         *(positive? &max_item : &min_item)= edge_item;
3063             }
3064             {
3065 17 100         rbtree_node_t *node= min_item? &min_item->rbnode : &first_item->rbnode;
3066 17 100         rbtree_node_t *end= max_item? &max_item->rbnode : &last_item->rbnode;
3067             while (1) {
3068 41 100         if (intmode)
3069 36           GET_TreeRBXS_item_FROM_rbnode(node)->keyunion.ikey += offset_iv;
3070             else
3071 5           GET_TreeRBXS_item_FROM_rbnode(node)->keyunion.nkey += offset_nv;
3072 41 100         if (node == end) break;
3073 24           node= rbtree_node_next(node);
3074             }
3075             }
3076             }
3077             else {
3078             /* In the future, other modes of altering keys could be available */
3079 0           croak("offset not specified");
3080             }
3081            
3082 18           done:
3083 18           XSRETURN(1); /* return self for chaining */
3084              
3085             void
3086             truncate_recent(tree, max_count)
3087             struct TreeRBXS *tree
3088             IV max_count
3089             INIT:
3090             struct dllist_node *cur, *next;
3091             struct TreeRBXS_item *item;
3092 0 0         bool keep= !(GIMME_V == G_VOID || GIMME_V == G_SCALAR);
    0          
3093 0           IV i, n= 0;
3094             PPCODE:
3095             // prune oldest nodes if exceeded limit
3096 0 0         if (max_count >= 0 && tree->recent_count > max_count) {
    0          
3097 0           n= tree->recent_count - max_count;
3098 0 0         if (keep) {
3099 0 0         EXTEND(SP, n);
    0          
3100 0           TreeRBXS_truncate_recent(tree, max_count, &ST(0));
3101 0           XSRETURN(n);
3102             } else {
3103 0           TreeRBXS_truncate_recent(tree, max_count, NULL);
3104             }
3105             }
3106 0 0         if (GIMME_V == G_SCALAR)
3107 0           PUSHs(sv_2mortal(newSViv(n)));
3108             /* else return empty list */
3109              
3110             IV
3111             clear(tree)
3112             struct TreeRBXS *tree
3113             ALIAS:
3114             Tree::RB::XS::CLEAR = 1
3115             CODE:
3116             (void) ix; /* unused */
3117 3           RETVAL= TreeRBXS_get_count(tree);
3118 3           TreeRBXS_clear(tree);
3119             OUTPUT:
3120             RETVAL
3121              
3122             struct TreeRBXS_item *
3123             min_node(tree)
3124             struct TreeRBXS *tree
3125             INIT:
3126 23           rbtree_node_t *node= rbtree_node_left_leaf(TreeRBXS_get_root(tree));
3127             CODE:
3128 23           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3129             OUTPUT:
3130             RETVAL
3131              
3132             struct TreeRBXS_item *
3133             max_node(tree)
3134             struct TreeRBXS *tree
3135             INIT:
3136 10           rbtree_node_t *node= rbtree_node_right_leaf(TreeRBXS_get_root(tree));
3137             CODE:
3138 10           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3139             OUTPUT:
3140             RETVAL
3141              
3142             struct TreeRBXS_item *
3143             nth_node(tree, ofs)
3144             struct TreeRBXS *tree
3145             IV ofs
3146             INIT:
3147             rbtree_node_t *node;
3148             CODE:
3149 15 100         if (ofs < 0) ofs += TreeRBXS_get_count(tree);
3150 15           node= rbtree_node_child_at_index(TreeRBXS_get_root(tree), ofs);
3151 15           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3152             OUTPUT:
3153             RETVAL
3154              
3155             struct TreeRBXS_item *
3156             root_node(tree)
3157             struct TreeRBXS *tree
3158             CODE:
3159 8           RETVAL= !TreeRBXS_get_count(tree)? NULL
3160 8 50         : GET_TreeRBXS_item_FROM_rbnode(TreeRBXS_get_root(tree));
3161             OUTPUT:
3162             RETVAL
3163              
3164             struct TreeRBXS_item *
3165             oldest_node(tree, newval= NULL)
3166             struct TreeRBXS *tree
3167             struct TreeRBXS_item *newval
3168             CODE:
3169 13 100         if (newval) {
3170 1 50         if (!TreeRBXS_is_member(tree, newval))
3171 0           croak("Node does not belong to this tree");
3172 1           TreeRBXS_recent_insert_before(tree, newval, tree->recent.next);
3173             }
3174 13           RETVAL= tree->recent.next == &tree->recent? NULL
3175 13 100         : GET_TreeRBXS_item_FROM_recent(tree->recent.next);
3176             OUTPUT:
3177             RETVAL
3178              
3179             struct TreeRBXS_item *
3180             newest_node(tree, newval= NULL)
3181             struct TreeRBXS *tree
3182             struct TreeRBXS_item *newval
3183             CODE:
3184 19 100         if (newval) {
3185 1 50         if (!TreeRBXS_is_member(tree, newval))
3186 0           croak("Node does not belong to this tree");
3187 1           TreeRBXS_recent_insert_before(tree, newval, &tree->recent);
3188             }
3189 19           RETVAL= tree->recent.prev == &tree->recent? NULL
3190 19 100         : GET_TreeRBXS_item_FROM_recent(tree->recent.prev);
3191             OUTPUT:
3192             RETVAL
3193              
3194             void
3195             keys(tree)
3196             struct TreeRBXS *tree
3197             ALIAS:
3198             Tree::RB::XS::values = 1
3199             Tree::RB::XS::kv = 2
3200             Tree::RB::XS::reverse_keys = 4
3201             Tree::RB::XS::reverse_values = 5
3202             Tree::RB::XS::reverse_kv = 6
3203             INIT:
3204 80 100         size_t n_ret, i, node_count= tree->keys_in_recent_order? tree->recent_count : TreeRBXS_get_count(tree);
3205 80           bool reverse= (ix & 4),
3206 80           keys_only= (ix & 3) == 0,
3207 80           values_only= (ix & 3) == 1,
3208 80           want_kv= (ix & 3) == 2;
3209             PPCODE:
3210 80 50         if (GIMME_V == G_VOID) {
3211 0           n_ret= 0;
3212             }
3213 80 50         else if (GIMME_V == G_SCALAR) {
3214 0           n_ret= 1;
3215 0           ST(0)= sv_2mortal(newSViv(node_count));
3216             }
3217             else {
3218 80 100         n_ret= want_kv? node_count*2 : node_count;
3219 80 50         EXTEND(SP, n_ret);
3220 80 100         if (tree->keys_in_recent_order) {
3221 8 100         struct dllist_node *node= reverse? tree->recent.prev : tree->recent.next;
3222 8           struct dllist_node *limit= &tree->recent;
3223 8 100         if (keys_only) {
3224 11 100         for (i= 0; i < n_ret && node != limit; i++, node= reverse? node->prev : node->next)
    50          
3225 8 100         ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_recent(node)));
3226             }
3227 5 100         else if (values_only) {
3228 8 100         for (i= 0; i < n_ret && node != limit; i++, node= reverse? node->prev : node->next)
    50          
3229 6 100         ST(i)= GET_TreeRBXS_item_FROM_recent(node)->value;
3230             }
3231             else {
3232 11 100         for (i= 0; i < n_ret && node != limit; node= reverse? node->prev : node->next) {
    50          
3233 8           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_recent(node)));
3234 8           i++;
3235 8           ST(i)= GET_TreeRBXS_item_FROM_recent(node)->value;
3236 8 100         i++;
3237             }
3238             }
3239 8 50         if (node != limit) i++; // for error reporting
3240             }
3241             else {
3242 72 50         rbtree_node_t *node= (reverse? rbtree_node_right_leaf : rbtree_node_left_leaf)(TreeRBXS_get_root(tree));
3243 72 50         rbtree_node_t *(*step)(rbtree_node_t *)= reverse? rbtree_node_prev : rbtree_node_next;
3244 72 100         if (keys_only) {
3245 430 100         for (i= 0; i < n_ret && node; i++, node= step(node))
    50          
3246 374           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_rbnode(node)));
3247             }
3248 16 50         else if (values_only) {
3249 0 0         for (i= 0; i < n_ret && node; i++, node= step(node))
    0          
3250 0           ST(i)= GET_TreeRBXS_item_FROM_rbnode(node)->value;
3251             }
3252             else {
3253 72 100         for (i= 0; i < n_ret && node; node= step(node)) {
    50          
3254 56           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_rbnode(node)));
3255 56           i++;
3256 56           ST(i)= GET_TreeRBXS_item_FROM_rbnode(node)->value;
3257 56           i++;
3258             }
3259             }
3260 72 50         if (node) i++; // for error reporting
3261             }
3262 80 50         if (i != n_ret)
3263 0 0         croak("BUG: expected %ld nodes but found %ld",
3264             (long) node_count,
3265             (long) (want_kv? ((i+1)>>1) : i));
3266             }
3267 80           XSRETURN(n_ret);
3268              
3269             SV *
3270             FIRSTKEY(tree)
3271             struct TreeRBXS *tree
3272             INIT:
3273 15           struct TreeRBXS_iter *iter= TreeRBXS_get_hashiter(tree);
3274             CODE:
3275 15 100         if (tree->hashiterset)
3276 1           tree->hashiterset= false; // iter has 'hseek' applied, don't change it
3277             else {
3278 14           iter->recent= tree->keys_in_recent_order;
3279 14           TreeRBXS_iter_rewind(iter);
3280             }
3281 15           RETVAL= TreeRBXS_item_wrap_key(iter->item); // handles null by returning undef
3282             OUTPUT:
3283             RETVAL
3284              
3285             SV *
3286             NEXTKEY(tree, lastkey)
3287             struct TreeRBXS *tree
3288             SV *lastkey
3289             INIT:
3290 61           struct TreeRBXS_iter *iter= TreeRBXS_get_hashiter(tree);
3291             CODE:
3292 61 100         if (tree->hashiterset)
3293 2           tree->hashiterset= false; // iter has 'hseek' applied, don't change it
3294             else
3295 59           TreeRBXS_iter_advance(iter, 1);
3296 61           RETVAL= TreeRBXS_item_wrap_key(iter->item);
3297             (void)lastkey;
3298             OUTPUT:
3299             RETVAL
3300              
3301             void
3302             _set_hashiter(tree, item_sv, reverse)
3303             struct TreeRBXS *tree
3304             SV *item_sv
3305             bool reverse
3306             INIT:
3307 3           struct TreeRBXS_item *item= TreeRBXS_get_magic_item(item_sv, 0);
3308 3           struct TreeRBXS_iter *iter= TreeRBXS_get_hashiter(tree);
3309             PPCODE:
3310 3 100         if (item && (TreeRBXS_item_get_tree(item) != tree))
    50          
3311 0           croak("Node is not part of this tree");
3312 3           iter->reverse= reverse;
3313 3           iter->recent= tree->keys_in_recent_order;
3314 3           TreeRBXS_iter_set_item(iter, item);
3315 3 100         if (!item) TreeRBXS_iter_rewind(iter);
3316 3           tree->hashiterset= true;
3317 3           XSRETURN(0);
3318              
3319             SV *
3320             SCALAR(tree)
3321             struct TreeRBXS *tree
3322             CODE:
3323 6           RETVAL= newSViv(TreeRBXS_get_count(tree));
3324             OUTPUT:
3325             RETVAL
3326              
3327             void
3328             DELETE(tree, key)
3329             struct TreeRBXS *tree
3330             SV *key
3331             INIT:
3332             struct TreeRBXS_item stack_item, *item;
3333             rbtree_node_t *first, *last;
3334             PPCODE:
3335 3 50         if (!SvOK(key))
3336 0           croak("Can't use undef as a key");
3337 3           TreeRBXS_init_tmp_item(&stack_item, tree, key, &PL_sv_undef);
3338 3 100         if (rbtree_find_all(
3339             &tree->root_sentinel,
3340             &stack_item,
3341 3           (int(*)(void*,void*,void*)) tree->compare,
3342             tree, -OFS_TreeRBXS_item_FIELD_rbnode,
3343             &first, &last, NULL)
3344             ) {
3345 2           ST(0)= sv_2mortal(SvREFCNT_inc(GET_TreeRBXS_item_FROM_rbnode(first)->value));
3346             do {
3347 2           item= GET_TreeRBXS_item_FROM_rbnode(first);
3348 2 50         first= (first == last)? NULL : rbtree_node_next(first);
3349 2           TreeRBXS_item_detach_tree(item, tree);
3350 2 50         } while (first);
3351             } else {
3352 1           ST(0)= &PL_sv_undef;
3353             }
3354 3           XSRETURN(1);
3355              
3356             IV
3357             cmp_numsplit(key_a, key_b)
3358             SV *key_a
3359             SV *key_b
3360             INIT:
3361             const char *apos, *bpos;
3362             STRLEN alen, blen;
3363 67           bool a_utf8= false, b_utf8= false;
3364             CODE:
3365             #if PERL_VERSION_LT(5,14,0)
3366             // before 5.14, need to force both to utf8 if either are utf8
3367             if (SvUTF8(key_a) || SvUTF8(key_b)) {
3368             apos= SvPVutf8(key_a, alen);
3369             bpos= SvPVutf8(key_b, blen);
3370             a_utf8= b_utf8= true;
3371             } else
3372             #else
3373             // After 5.14, can compare utf8 with bytes without converting the buffer
3374 67           a_utf8= SvUTF8(key_a);
3375 67           b_utf8= SvUTF8(key_b);
3376             #endif
3377             {
3378 67           apos= SvPV(key_a, alen);
3379 67           bpos= SvPV(key_b, blen);
3380             }
3381 67 100         RETVAL= cmp_numsplit(apos, apos+alen, a_utf8, bpos, bpos+blen, b_utf8);
3382             OUTPUT:
3383             RETVAL
3384              
3385             #-----------------------------------------------------------------------------
3386             # Node Methods
3387             #
3388              
3389             MODULE = Tree::RB::XS PACKAGE = Tree::RB::XS::Node
3390              
3391             SV *
3392             key(item, newval=NULL)
3393             struct TreeRBXS_item *item
3394             SV *newval
3395             CODE:
3396 202 100         if (newval) {
3397 47           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3398             struct TreeRBXS_item stack_item;
3399 47           TreeRBXS_init_tmp_item(&stack_item, tree, newval, &PL_sv_undef);
3400 47           TreeRBXS_item_set_key(&item, &stack_item);
3401             }
3402             /* semi-expensive if just setting key, so check for void context */
3403 404           RETVAL= (GIMME_V == G_VOID)? &PL_sv_undef
3404 202 100         : TreeRBXS_item_wrap_key(item);
3405             OUTPUT:
3406             RETVAL
3407              
3408             SV *
3409             value(item, newval=NULL)
3410             struct TreeRBXS_item *item
3411             SV *newval
3412             CODE:
3413 51 50         if (newval)
3414 0           sv_setsv(item->value, newval);
3415 51           RETVAL= SvREFCNT_inc_simple_NN(item->value);
3416             OUTPUT:
3417             RETVAL
3418              
3419             IV
3420             index(item)
3421             struct TreeRBXS_item *item
3422             CODE:
3423 0 0         RETVAL= rbtree_node_index(&item->rbnode);
3424             OUTPUT:
3425             RETVAL
3426              
3427             struct TreeRBXS_item *
3428             prev(item)
3429             struct TreeRBXS_item *item
3430             INIT:
3431 9           rbtree_node_t *node= rbtree_node_prev(&item->rbnode);
3432             CODE:
3433 9           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3434             OUTPUT:
3435             RETVAL
3436              
3437             struct TreeRBXS_item *
3438             next(item)
3439             struct TreeRBXS_item *item
3440             INIT:
3441 56           rbtree_node_t *node= rbtree_node_next(&item->rbnode);
3442             CODE:
3443 56           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3444             OUTPUT:
3445             RETVAL
3446              
3447             struct TreeRBXS_item *
3448             newer(item, newval=NULL)
3449             struct TreeRBXS_item *item
3450             struct TreeRBXS_item *newval
3451             INIT:
3452 12           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3453 12           struct dllist_node *next= item->recent.next;
3454             CODE:
3455 12 100         if (newval) {
3456 1 50         if (!next)
3457 0           croak("Can't insert relative to a node that isn't recent_tracked");
3458 1 50         if (!tree) croak("Node was removed from tree");
3459 1           TreeRBXS_recent_insert_before(tree, newval, next);
3460 1           next= &newval->recent;
3461             }
3462 17 50         RETVAL= (!next || !tree || next == &tree->recent)? NULL
    50          
3463 17 100         : GET_TreeRBXS_item_FROM_recent(next);
3464             OUTPUT:
3465             RETVAL
3466              
3467             struct TreeRBXS_item *
3468             older(item, newval=NULL)
3469             struct TreeRBXS_item *item
3470             struct TreeRBXS_item *newval
3471             INIT:
3472 14           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3473 14           struct dllist_node *prev= item->recent.prev;
3474             CODE:
3475 14 100         if (newval) {
3476 1 50         if (!prev)
3477 0           croak("Can't insert relative to a node that isn't recent_tracked");
3478 1 50         if (!tree) croak("Node was removed from tree");
3479 1           TreeRBXS_recent_insert_before(tree, newval, &item->recent);
3480 1           prev= &newval->recent;
3481             }
3482 21 50         RETVAL= (!prev || !tree || prev == &tree->recent)? NULL
    50          
3483 21 100         : GET_TreeRBXS_item_FROM_recent(prev);
3484             OUTPUT:
3485             RETVAL
3486              
3487             struct TreeRBXS_item *
3488             parent(item)
3489             struct TreeRBXS_item *item
3490             CODE:
3491 6 50         RETVAL= rbtree_node_is_in_tree(&item->rbnode) && item->rbnode.parent->count?
3492 6 100         GET_TreeRBXS_item_FROM_rbnode(item->rbnode.parent) : NULL;
3493             OUTPUT:
3494             RETVAL
3495              
3496             void
3497             tree(item)
3498             struct TreeRBXS_item *item
3499             INIT:
3500 15           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3501             PPCODE:
3502 15 100         ST(0)= tree && tree->owner? sv_2mortal(newRV_inc(tree->owner)) : &PL_sv_undef;
    50          
3503 15           XSRETURN(1);
3504              
3505             struct TreeRBXS_item *
3506             left(item)
3507             struct TreeRBXS_item *item
3508             CODE:
3509 20 100         RETVAL= rbtree_node_is_in_tree(&item->rbnode) && item->rbnode.left->count?
3510 20 100         GET_TreeRBXS_item_FROM_rbnode(item->rbnode.left) : NULL;
3511             OUTPUT:
3512             RETVAL
3513              
3514             struct TreeRBXS_item *
3515             right(item)
3516             struct TreeRBXS_item *item
3517             CODE:
3518 20 100         RETVAL= rbtree_node_is_in_tree(&item->rbnode) && item->rbnode.right->count?
3519 20 100         GET_TreeRBXS_item_FROM_rbnode(item->rbnode.right) : NULL;
3520             OUTPUT:
3521             RETVAL
3522              
3523             struct TreeRBXS_item *
3524             left_leaf(item)
3525             struct TreeRBXS_item *item
3526             INIT:
3527 2           rbtree_node_t *node= rbtree_node_left_leaf(&item->rbnode);
3528             CODE:
3529 2           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3530             OUTPUT:
3531             RETVAL
3532              
3533             struct TreeRBXS_item *
3534             right_leaf(item)
3535             struct TreeRBXS_item *item
3536             INIT:
3537 2           rbtree_node_t *node= rbtree_node_right_leaf(&item->rbnode);
3538             CODE:
3539 2           RETVAL= node? GET_TreeRBXS_item_FROM_rbnode(node) : NULL;
3540             OUTPUT:
3541             RETVAL
3542              
3543             IV
3544             color(item)
3545             struct TreeRBXS_item *item
3546             CODE:
3547 5 100         RETVAL= item->rbnode.color;
3548             OUTPUT:
3549             RETVAL
3550              
3551             IV
3552             count(item)
3553             struct TreeRBXS_item *item
3554             CODE:
3555 5 50         RETVAL= item->rbnode.count;
3556             OUTPUT:
3557             RETVAL
3558              
3559             IV
3560             prune(item)
3561             struct TreeRBXS_item *item
3562             INIT:
3563 6           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3564             CODE:
3565 6           RETVAL= 0;
3566 6 100         if (tree) {
3567 5           TreeRBXS_item_detach_tree(item, tree);
3568 5           RETVAL= 1;
3569             }
3570             OUTPUT:
3571             RETVAL
3572              
3573             void
3574             recent_tracked(item, newval=NULL)
3575             struct TreeRBXS_item *item
3576             SV* newval
3577             INIT:
3578             struct TreeRBXS *tree;
3579             PPCODE:
3580 14 100         if (items > 1) {
3581 10           tree= TreeRBXS_item_get_tree(item);
3582 10 50         if (!tree) croak("Node was removed from tree");
3583 10 50         if (SvTRUE(newval))
3584 0           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
3585             else
3586 10           TreeRBXS_recent_prune(tree, item);
3587             // ST(0) is $self, so let it be the return value
3588             } else {
3589 4           ST(0)= sv_2mortal(newSViv(item->recent.next? 1 : 0));
3590             }
3591 14           XSRETURN(1);
3592              
3593             void
3594             mark_newest(item)
3595             struct TreeRBXS_item *item
3596             INIT:
3597 2           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3598             PPCODE:
3599 2 50         if (!tree) croak("Node was removed from tree");
3600 2           TreeRBXS_recent_insert_before(tree, item, &tree->recent);
3601 2           XSRETURN(1);
3602              
3603             void
3604             mark_oldest(item)
3605             struct TreeRBXS_item *item
3606             INIT:
3607 0           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3608             PPCODE:
3609 0 0         if (!tree) croak("Node was removed from tree");
3610 0           TreeRBXS_recent_insert_before(tree, item, tree->recent.next);
3611 0           XSRETURN(1);
3612              
3613             void
3614             STORABLE_freeze(item, cloning)
3615             struct TreeRBXS_item *item
3616             bool cloning
3617             INIT:
3618 3           struct TreeRBXS *tree= TreeRBXS_item_get_tree(item);
3619             AV *av;
3620             PPCODE:
3621             (void) cloning; /* unused */
3622 3 100         if (tree) {
3623 2           ST(0)= sv_2mortal(newSViv(rbtree_node_index(&item->rbnode)));
3624 2           ST(1)= sv_2mortal(newRV_inc(tree->owner));
3625             } else {
3626 1           ST(0)= sv_2mortal(newSViv(-1));
3627 1           ST(1)= sv_2mortal(newRV_noinc((SV*)(av= newAV())));
3628 1           av_push(av, TreeRBXS_item_wrap_key(item));
3629 1           av_push(av, SvREFCNT_inc(item->value));
3630             }
3631 3           XSRETURN(2);
3632              
3633             void
3634             STORABLE_thaw(item_sv, cloning, idx, refs)
3635             SV *item_sv
3636             bool cloning
3637             IV idx
3638             SV *refs
3639             INIT:
3640             struct TreeRBXS *tree;
3641             struct TreeRBXS_item *item;
3642             rbtree_node_t *node;
3643             MAGIC *magic;
3644             PPCODE:
3645             (void) cloning; /* unused */
3646 3 100         if (idx == -1) {
3647             IV n;
3648 1           SV **svec= unwrap_array(refs, &n);
3649 1 50         if (!svec || n != 2 || !svec[0] || !svec[1])
    50          
    50          
    50          
3650 0           croak("Expected arrayref of (key,value)");
3651 1           Newx(item, 1, struct TreeRBXS_item);
3652 1           memset(item, 0, sizeof(*item));
3653 1           item->key_type= KEY_TYPE_ANY;
3654 1           item->keyunion.svkey= SvREFCNT_inc(svec[0]);
3655 1           item->value= SvREFCNT_inc(svec[1]);
3656 1           item->owner= SvRV(item_sv);
3657             } else {
3658 2           tree= TreeRBXS_get_magic_tree(refs, OR_DIE);
3659 2 50         if (!(node= rbtree_node_child_at_index(TreeRBXS_get_root(tree), idx)))
3660 0           croak("Tree does not have element %ld", (long) idx);
3661 2           item= GET_TreeRBXS_item_FROM_rbnode(node);
3662 2 50         if (item->owner) {
3663 0 0         if (item->owner != SvRV(item_sv))
3664 0           croak("BUG: Storable deserialized tree node multiple times");
3665 0           return;
3666             }
3667             else {
3668 2           item->owner= SvRV(item_sv);
3669 2           SvREFCNT_inc(item->owner);
3670             }
3671             }
3672 3           magic= sv_magicext(item->owner, NULL, PERL_MAGIC_ext, &TreeRBXS_item_magic_vt, (const char*) item, 0);
3673 3           #ifdef USE_ITHREADS
3674             magic->mg_flags |= MGf_DUP;
3675 3           #else
3676             (void)magic; // suppress warning
3677             #endif
3678             XSRETURN(0);
3679              
3680             #-----------------------------------------------------------------------------
3681             # Iterator methods
3682             #
3683              
3684             MODULE = Tree::RB::XS PACKAGE = Tree::RB::XS::Iter
3685              
3686             void
3687             _init(iter_sv, target, direction= 1)
3688             SV *iter_sv
3689             SV *target
3690             IV direction
3691             INIT:
3692 257           struct TreeRBXS_iter *iter2, *iter= TreeRBXS_get_magic_iter(iter_sv, AUTOCREATE|OR_DIE);
3693             struct TreeRBXS *tree;
3694 257           struct TreeRBXS_item *item= NULL;
3695 257           rbtree_node_t *node= NULL;
3696             struct dllist_node *lnode;
3697             PPCODE:
3698 257 50         if (iter->item || iter->tree)
    50          
3699 0           croak("Iterator is already initialized");
3700 257           switch (direction) {
3701 5           case -2: iter->recent= 1; iter->reverse= 1; break;
3702 110           case -1: iter->recent= 0; iter->reverse= 1; break;
3703 132           case 1: iter->recent= 0; iter->reverse= 0; break;
3704 10           case 2: iter->recent= 1; iter->reverse= 0; break;
3705 0           default: croak("Invalid direction code");
3706             }
3707              
3708             // target can be a tree, a node, or another iterator
3709 257 50         if ((iter2= TreeRBXS_get_magic_iter(target, 0))) {
3710             // use this direction unless overridden
3711 0 0         if (items < 2) {
3712 0           iter->reverse= iter2->reverse;
3713 0           iter->recent= iter2->recent;
3714             }
3715 0           tree= iter2->tree;
3716 0           item= iter2->item;
3717             }
3718 257 100         else if ((item= TreeRBXS_get_magic_item(target, 0))) {
3719 17           tree= TreeRBXS_item_get_tree(item);
3720             }
3721 240 50         else if ((tree= TreeRBXS_get_magic_tree(target, 0))) {
3722 240 100         if (iter->recent) {
3723 13 100         lnode= iter->reverse? tree->recent.prev : tree->recent.next;
3724 13           item= (lnode == &tree->recent)? NULL
3725 13 50         : GET_TreeRBXS_item_FROM_recent(lnode);
3726             }
3727             else {
3728 454           node= !TreeRBXS_get_count(tree)? NULL
3729 454 50         : iter->reverse? rbtree_node_right_leaf(TreeRBXS_get_root(tree))
3730 227 100         : rbtree_node_left_leaf(TreeRBXS_get_root(tree));
3731 227 50         if (node)
3732 227           item= GET_TreeRBXS_item_FROM_rbnode(node);
3733             }
3734             }
3735 257 50         if (!tree)
3736 0           croak("Can't iterate a node that isn't in the tree");
3737 257 100         if (iter->recent && item && !item->recent.next)
    50          
    50          
3738 0           croak("Can't perform insertion-order iteration on a node that isn't tracked");
3739 257           iter->tree= tree;
3740 257 50         if (tree->owner)
3741 257           SvREFCNT_inc(tree->owner);
3742 257           TreeRBXS_iter_set_item(iter, item);
3743 257           ST(0)= iter_sv;
3744 257           XSRETURN(1);
3745              
3746             SV *
3747             key(iter)
3748             struct TreeRBXS_iter *iter
3749             CODE:
3750             // wrap_key handles NULL items
3751 38           RETVAL= TreeRBXS_item_wrap_key(iter->item);
3752             OUTPUT:
3753             RETVAL
3754              
3755             SV *
3756             value(iter)
3757             struct TreeRBXS_iter *iter
3758             CODE:
3759 35 100         RETVAL= iter->item? SvREFCNT_inc_simple_NN(iter->item->value) : &PL_sv_undef;
3760             OUTPUT:
3761             RETVAL
3762              
3763             struct TreeRBXS_item *
3764             node(iter)
3765             struct TreeRBXS_iter *iter
3766             CODE:
3767 18           RETVAL= iter->item;
3768             OUTPUT:
3769             RETVAL
3770              
3771             SV *
3772             index(iter)
3773             struct TreeRBXS_iter *iter
3774             CODE:
3775 1 0         RETVAL= !iter->item || !rbtree_node_is_in_tree(&iter->item->rbnode)? &PL_sv_undef
3776 1 50         : newSViv(rbtree_node_index(&iter->item->rbnode));
3777             OUTPUT:
3778             RETVAL
3779              
3780             SV *
3781             tree(iter)
3782             struct TreeRBXS_iter *iter
3783             CODE:
3784 0 0         RETVAL= iter->tree && iter->tree->owner? newRV_inc(iter->tree->owner) : &PL_sv_undef;
    0          
3785             OUTPUT:
3786             RETVAL
3787              
3788             bool
3789             done(iter)
3790             struct TreeRBXS_iter *iter
3791             CODE:
3792 46 100         RETVAL= !iter->item;
3793             OUTPUT:
3794             RETVAL
3795              
3796             void
3797             next(iter, count_sv= NULL)
3798             struct TreeRBXS_iter *iter
3799             SV* count_sv
3800             ALIAS:
3801             Tree::RB::XS::Iter::next = 0
3802             Tree::RB::XS::Iter::next_key = 1
3803             Tree::RB::XS::Iter::next_keys = 1
3804             Tree::RB::XS::Iter::next_value = 2
3805             Tree::RB::XS::Iter::next_values = 2
3806             Tree::RB::XS::Iter::next_kv = 3
3807             INIT:
3808 46 100         size_t pos, n, nret, i, max_count= iter->recent? iter->tree->recent_count : TreeRBXS_get_count(iter->tree);
3809             IV request;
3810             rbtree_node_t *node;
3811 46 100         rbtree_node_t *(*step)(rbtree_node_t *)= iter->reverse? &rbtree_node_prev : rbtree_node_next;
3812             PPCODE:
3813 46 100         if (iter->item) {
3814 38           request= !count_sv? 1
3815 51 100         : ((SvPOK(count_sv) && *SvPV_nolen(count_sv) == '*')
    100          
    50          
3816 13 100         || (SvNOK(count_sv) && SvNV(count_sv) > (NV)PERL_INT_MAX))? max_count
    50          
3817 7           : SvIV(count_sv);
3818 38 100         if (request < 1) {
3819 1           nret= 0;
3820             }
3821             // A request for 1 is simpler because there is no need to count how many will be returned.
3822             // iter->item wasn't NULL so it is guaranteed to be 1.
3823 37 100         else if (GIMME_V == G_VOID) {
3824             // skip all the busywork if called in void context
3825             // (but still advance the iterator below)
3826 1           TreeRBXS_iter_advance(iter, request);
3827 1           nret= 0;
3828             }
3829 36 100         else if (request == 1 || iter->recent) { // un-optimized loop
    100          
3830 20 100         nret= ix == 3? request<<1 : request;
3831 20 50         EXTEND(SP, nret);
3832 106 100         for (i= 0; i < nret && iter->item; ) {
    50          
3833 6           ST(i++)= ix == 0? sv_2mortal(TreeRBXS_wrap_item(iter->item))
3834 166 100         : ix == 2? iter->item->value
3835 80 50         : sv_2mortal(TreeRBXS_item_wrap_key(iter->item));
3836 86 100         if (ix == 3)
3837 46           ST(i++)= iter->item->value;
3838 86           TreeRBXS_iter_advance(iter, 1);
3839             }
3840 20           nret= i;
3841             }
3842             else { // optimized loop, for iterating batches of tree nodes quickly
3843 16           pos= rbtree_node_index(&iter->item->rbnode);
3844             // calculate how many nodes will be returned
3845 16 100         n= iter->reverse? 1 + pos : max_count - pos;
3846 16 100         if (n > request) n= request;
3847 16           node= &iter->item->rbnode;
3848 16 100         nret= (ix == 3)? n<<1 : n;
3849 16 50         EXTEND(SP, nret); // EXTEND macro declares a temp 'ix' internally - GitHub #2
3850 16 100         if (ix == 0) {
3851 3 100         for (i= 0; i < nret && node; i++, node= step(node))
    50          
3852 2           ST(i)= sv_2mortal(TreeRBXS_wrap_item(GET_TreeRBXS_item_FROM_rbnode(node)));
3853             }
3854 15 100         else if (ix == 1) {
3855 20 100         for (i= 0; i < nret && node; i++, node= step(node))
    50          
3856 16           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_rbnode(node)));
3857             }
3858 11 100         else if (ix == 2) {
3859 96 100         for (i= 0; i < nret && node; i++, node= step(node))
    50          
3860 91           ST(i)= GET_TreeRBXS_item_FROM_rbnode(node)->value;
3861             }
3862             else {
3863 76 100         for (i= 0; i < nret && node; node= step(node)) {
    50          
3864 70           ST(i)= sv_2mortal(TreeRBXS_item_wrap_key(GET_TreeRBXS_item_FROM_rbnode(node)));
3865 70           i++;
3866 70           ST(i)= GET_TreeRBXS_item_FROM_rbnode(node)->value;
3867 70           i++;
3868             }
3869             }
3870 16 50         if (i != nret)
3871 0 0         croak("BUG: expected %ld nodes but found %ld", (long) n, (long) (ix == 3? i>>1 : i));
3872 16           TreeRBXS_iter_advance(iter, n);
3873             }
3874 38           XSRETURN(nret);
3875             } else {
3876             // end of iteration, nothing to do
3877 8           ST(0)= &PL_sv_undef;
3878             // return the undef only if the user didn't specify a count
3879 8           XSRETURN(count_sv? 0 : 1);
3880             }
3881              
3882             bool
3883             step(iter, ofs= 1)
3884             struct TreeRBXS_iter *iter
3885             IV ofs
3886             CODE:
3887 1536           TreeRBXS_iter_advance(iter, ofs);
3888             // Return boolean whether the iterator points to an item
3889 1536 100         RETVAL= !!iter->item;
3890             OUTPUT:
3891             RETVAL
3892              
3893             void
3894             delete(iter)
3895             struct TreeRBXS_iter *iter
3896             PPCODE:
3897 5 50         if (iter->item) {
3898             // up the refcnt temporarily to make sure it doesn't get lost when item gets freed
3899 5           ST(0)= sv_2mortal(SvREFCNT_inc(iter->item->value));
3900             // pruning the item automatically moves iterators to next, including this iterator.
3901 5           TreeRBXS_item_detach_tree(iter->item, iter->tree);
3902             }
3903             else
3904 0           ST(0)= &PL_sv_undef;
3905 5           XSRETURN(1);
3906              
3907             void
3908             STORABLE_freeze(iter, cloning)
3909             struct TreeRBXS_iter *iter
3910             bool cloning
3911             INIT:
3912 0           char itertype= (iter->reverse? 1 : 0) | (iter->recent? 2 : 0);
3913             AV *refs;
3914             PPCODE:
3915             (void) cloning; /* unused */
3916 0           ST(0)= sv_2mortal(newSVpvn(&itertype, 1));
3917 0           ST(1)= sv_2mortal(newRV_noinc((SV*) (refs= newAV())));
3918 0           av_push(refs, newRV_inc(iter->tree->owner));
3919 0 0         av_push(refs, iter->item? newSViv(rbtree_node_index(&iter->item->rbnode)) : newSV(0));
3920 0           XSRETURN(2);
3921              
3922             #-----------------------------------------------------------------------------
3923             # Constants
3924             #
3925              
3926             BOOT:
3927 13           HV *stash= gv_stashpvn("Tree::RB::XS::Node", 18, 1);
3928 13           FUNCTION_IS_LVALUE(value);
3929            
3930 13           stash= gv_stashpvn("Tree::RB::XS", 12, 1);
3931 13           FUNCTION_IS_LVALUE(get);
3932 13           FUNCTION_IS_LVALUE(get_or_add);
3933 13           FUNCTION_IS_LVALUE(FETCH);
3934 13           FUNCTION_IS_LVALUE(lookup);
3935 13           EXPORT_ENUM(KEY_TYPE_ANY);
3936 13           EXPORT_ENUM(KEY_TYPE_INT);
3937 13           EXPORT_ENUM(KEY_TYPE_FLOAT);
3938 13           EXPORT_ENUM(KEY_TYPE_USTR);
3939 13           EXPORT_ENUM(KEY_TYPE_BSTR);
3940 13           EXPORT_ENUM(KEY_TYPE_CLAIM);
3941 13           EXPORT_ENUM(CMP_PERL);
3942 13           EXPORT_ENUM(CMP_INT);
3943 13           EXPORT_ENUM(CMP_FLOAT);
3944 13           EXPORT_ENUM(CMP_STR);
3945 13           EXPORT_ENUM(CMP_FOLDCASE);
3946 13           EXPORT_ENUM(CMP_MEMCMP);
3947 13           EXPORT_ENUM(CMP_NUMSPLIT);
3948 13           EXPORT_ENUM(CMP_NUMSPLIT_FOLDCASE);
3949 13           EXPORT_ENUM(GET_EQ);
3950 13           EXPORT_ENUM(GET_OR_ADD);
3951 13           EXPORT_ENUM(GET_EQ_LAST);
3952 13           EXPORT_ENUM(GET_GE);
3953 13           EXPORT_ENUM(GET_LE);
3954 13           EXPORT_ENUM(GET_LE_LAST);
3955 13           EXPORT_ENUM(GET_GT);
3956 13           EXPORT_ENUM(GET_LT);
3957 13           EXPORT_ENUM(GET_NEXT);
3958 13           EXPORT_ENUM(GET_PREV);
3959              
3960             PROTOTYPES: DISABLE