File Coverage

Indexed.xs
Criterion Covered Total %
statement 294 306 96.0
branch 212 342 61.9
condition n/a
subroutine n/a
pod n/a
total 506 648 78.0


line stmt bran cond sub pod time code
1             /*******************************************************************************
2             *
3             * MODULE: Indexed.xs
4             *
5             ********************************************************************************
6             *
7             * DESCRIPTION: XS Interface for Tie::Hash::Indexed Perl extension module
8             *
9             ********************************************************************************
10             *
11             * Copyright (c) Marcus Holland-Moritz. All rights reserved.
12             * This program is free software; you can redistribute it and/or modify
13             * it under the same terms as Perl itself.
14             *
15             *******************************************************************************/
16              
17              
18             /*===== GLOBAL INCLUDES ======================================================*/
19              
20             #define PERL_NO_GET_CONTEXT
21             #include "EXTERN.h"
22             #include "perl.h"
23             #include "XSUB.h"
24              
25             #define NEED_sv_2pv_flags
26              
27             #include "ppport.h"
28              
29              
30             /*===== DEFINES ==============================================================*/
31              
32             #define XSCLASS "Tie::Hash::Indexed"
33              
34             /*-----------------*/
35             /* debugging stuff */
36             /*-----------------*/
37              
38             #define DB_THI_MAIN 0x00000001
39              
40             #ifdef THI_DEBUGGING
41             # define DEBUG_FLAG(flag) \
42             (DB_THI_ ## flag & gs_dbflags)
43             # define THI_DEBUG(flag, x) \
44             do { if (DEBUG_FLAG(flag)) debug_printf x; } while (0)
45             # define DBG_CTXT_FMT "%s"
46             # define DBG_CTXT_ARG (GIMME_V == G_VOID ? "0=" : \
47             (GIMME_V == G_SCALAR ? "$=" : \
48             (GIMME_V == G_ARRAY ? "@=" : \
49             "?=" \
50             )))
51             #else
52             # define THI_DEBUG(flag, x) (void) 0
53             #endif
54              
55             #define THI_DEBUG_METHOD \
56             THI_DEBUG(MAIN, (DBG_CTXT_FMT XSCLASS "::%s\n", DBG_CTXT_ARG, method))
57              
58             #define THI_DEBUG_METHOD1(fmt, arg1) \
59             THI_DEBUG(MAIN, (DBG_CTXT_FMT XSCLASS "::%s(" fmt ")\n", \
60             DBG_CTXT_ARG, method, arg1))
61              
62             #define THI_DEBUG_METHOD2(fmt, arg1, arg2) \
63             THI_DEBUG(MAIN, (DBG_CTXT_FMT XSCLASS "::%s(" fmt ")\n", \
64             DBG_CTXT_ARG, method, arg1, arg2))
65              
66             #define THI_METHOD( name ) const char * const method = #name
67              
68             /*---------------------------------*/
69             /* check object against corruption */
70             /*---------------------------------*/
71              
72             #define THI_CHECK_OBJECT \
73             do { \
74             if (THIS == NULL ) \
75             Perl_croak(aTHX_ "NULL OBJECT IN " XSCLASS "::%s", method); \
76             if (THIS->signature != THI_SIGNATURE) \
77             { \
78             if (THIS->signature == 0xDEADC0DE) \
79             Perl_croak(aTHX_ "DEAD OBJECT IN " XSCLASS "::%s", method); \
80             Perl_croak(aTHX_ "INVALID OBJECT IN " XSCLASS "::%s", method); \
81             } \
82             if (THIS->hv == NULL || THIS->root == NULL) \
83             Perl_croak(aTHX_ "OBJECT INCONSITENCY IN " XSCLASS "::%s", method);\
84             } while (0)
85              
86             #define THI_CHECK_ITERATOR \
87             do { \
88             if (SvIVX(THIS->serial) != THIS->orig_serial) \
89             { \
90             Perl_croak(aTHX_ "invalid iterator access"); \
91             } \
92             } while (0)
93              
94             #define THI_INVALIDATE_ITERATORS ++SvIVX(THIS->serial)
95              
96             #if PERL_BCDVERSION < 0x5010000
97             # define HAS_OP_DOR 0
98             # define MY_OP_DOR OP_OR
99             #else
100             # define HAS_OP_DOR 1
101             # define MY_OP_DOR OP_DOR
102             #endif
103              
104             /*--------------------------------*/
105             /* very simple doubly linked list */
106             /*--------------------------------*/
107              
108             #define IxLink_new(link) \
109             do { \
110             New(0, link, 1, IxLink); \
111             (link)->key = NULL; \
112             (link)->val = NULL; \
113             (link)->prev = (link)->next = link; \
114             } while (0)
115              
116             #define IxLink_delete(link) \
117             do { \
118             Safefree(link); \
119             link = NULL; \
120             } while (0)
121              
122             #define IxLink_common_(root, link, prev, next) \
123             do { \
124             (link)->prev = (root)->prev; \
125             (link)->next = (root); \
126             (root)->prev->next = (link); \
127             (root)->prev = (link); \
128             } while (0)
129              
130             #define IxLink_push(root, link) \
131             IxLink_common_(root, link, prev, next)
132              
133             #define IxLink_unshift(root, link) \
134             IxLink_common_(root, link, next, prev)
135              
136             #define IxLink_extract(link) \
137             do { \
138             (link)->prev->next = (link)->next; \
139             (link)->next->prev = (link)->prev; \
140             (link)->next = (link); \
141             (link)->prev = (link); \
142             } while (0)
143              
144              
145             /*===== TYPEDEFS =============================================================*/
146              
147             typedef struct sIxLink IxLink;
148              
149             struct sIxLink {
150             SV *key;
151             SV *val;
152             IxLink *prev;
153             IxLink *next;
154             };
155              
156             typedef struct {
157             HV *hv;
158             IxLink *root;
159             IxLink *iter;
160             SV *serial;
161             U32 signature;
162             #define THI_SIGNATURE 0x54484924
163             } IXHV;
164              
165             typedef struct {
166             IxLink *cur;
167             IxLink *end;
168             bool reverse;
169             SV *serial;
170             IV orig_serial;
171             } Iterator;
172              
173             /*---------------*/
174             /* serialization */
175             /*---------------*/
176              
177             typedef struct {
178             char id[4];
179             #define THI_SERIAL_ID "THI!" /* this must _never_ be changed */
180             unsigned char major;
181             #define THI_SERIAL_REV_MAJOR 0 /* incompatible changes */
182             unsigned char minor;
183             #define THI_SERIAL_REV_MINOR 0 /* compatible changes */
184             } SerialRev;
185              
186             typedef struct {
187             SerialRev rev;
188             /* add configuration items here, don't change order, only use bytes */
189             } Serialized;
190              
191              
192             /*===== STATIC VARIABLES =====================================================*/
193              
194             #ifdef THI_DEBUGGING
195             static U32 gs_dbflags;
196             #endif
197              
198              
199             /*===== STATIC FUNCTIONS =====================================================*/
200              
201             #ifdef THI_DEBUGGING
202             static void debug_printf(char *f, ...)
203             {
204             #ifdef PERL_IMPLICIT_SYS
205             dTHX;
206             #endif
207             va_list l;
208             va_start(l, f);
209             vfprintf(stderr, f, l);
210             va_end(l);
211             }
212              
213             static void set_debug_opt(pTHX_ const char *dbopts)
214             {
215             if (strEQ(dbopts, "all"))
216             {
217             gs_dbflags = 0xFFFFFFFF;
218             }
219             else
220             {
221             gs_dbflags = 0;
222             while (*dbopts)
223             {
224             switch (*dbopts)
225             {
226             case 'd': gs_dbflags |= DB_THI_MAIN; break;
227             default:
228             Perl_croak(aTHX_ "Unknown debug option '%c'", *dbopts);
229             break;
230             }
231             dbopts++;
232             }
233             }
234             }
235             #endif
236              
237             #ifndef HeVAL
238             # define HeVAL(he) (he)->hent_val
239             #endif
240              
241             #ifndef HvUSEDKEYS
242             # define HvUSEDKEYS(hv) HvKEYS(hv)
243             #endif
244              
245             #ifndef SvREFCNT_dec_NN
246             # define SvREFCNT_dec_NN(sv) SvREFCNT_dec(sv)
247             #endif
248              
249             enum store_mode {
250             SM_SET,
251             SM_PUSH,
252             SM_UNSHIFT,
253             SM_GET,
254             SM_GET_NUM
255             };
256              
257 90           static void ixlink_insert(IxLink *root, IxLink *cur, enum store_mode mode)
258             {
259 90 100         switch (mode)
260             {
261 2           case SM_UNSHIFT: IxLink_unshift(root, cur); break;
262 88           default: IxLink_push(root, cur); break;
263             }
264 90           }
265              
266 117           static IxLink *ixhv_store(pTHX_ IXHV *THIS, SV *key, SV *value, enum store_mode mode)
267             {
268             HE *he;
269             SV *pair;
270             IxLink *cur;
271              
272 117 50         if ((he = hv_fetch_ent(THIS->hv, key, 1, 0)) == NULL)
273             {
274 0           Perl_croak(aTHX_ "couldn't store value");
275             }
276              
277 117           pair = HeVAL(he);
278              
279 117 100         if (SvTYPE(pair) == SVt_NULL)
280             {
281 88           IxLink_new(cur);
282              
283 88           ixlink_insert(THIS->root, cur, mode);
284              
285 88           sv_setiv(pair, PTR2IV(cur));
286              
287 88           cur->key = newSVsv(key);
288              
289 88 100         if (mode == SM_GET_NUM)
290             {
291 4           cur->val = newSViv(0);
292             }
293             else
294             {
295 84 50         if (mode == SM_GET && !value)
    0          
296             {
297 0           value = &PL_sv_undef;
298             }
299             assert(value);
300 88           cur->val = newSVsv(value);
301             }
302             }
303             else
304             {
305 29           cur = INT2PTR(IxLink *, SvIVX(pair));
306              
307 29 100         if (mode < SM_GET)
308             {
309 12 100         if (mode != SM_SET)
310             {
311 2           IxLink_extract(cur);
312 2           ixlink_insert(THIS->root, cur, mode);
313             }
314              
315 12           sv_setsv(cur->val, value);
316             }
317             }
318              
319 117           return cur;
320             }
321              
322 15           static void ixhv_clear(pTHX_ IXHV *THIS)
323             {
324             IxLink *cur;
325              
326 30 100         for (cur = THIS->root->next; cur != THIS->root;)
327             {
328 15           IxLink *del = cur;
329 15           cur = cur->next;
330 15           SvREFCNT_dec_NN(del->key);
331 15           SvREFCNT_dec(del->val);
332 15           IxLink_delete(del);
333             }
334              
335 15           THIS->root->next = THIS->root->prev = THIS->root;
336              
337 15           hv_clear(THIS->hv);
338 15           }
339              
340 164           static IxLink *ixhv_find(pTHX_ IXHV *THIS, SV *key)
341             {
342             HE *he;
343              
344 164 50         if ((he = hv_fetch_ent(THIS->hv, key, 0, 0)) == NULL)
345             {
346 0           return NULL;
347             }
348              
349 164           return INT2PTR(IxLink *, SvIVX(HeVAL(he)));
350             }
351              
352             /*===== XS FUNCTIONS =========================================================*/
353              
354             MODULE = Tie::Hash::Indexed PACKAGE = Tie::Hash::Indexed::Iterator
355              
356             PROTOTYPES: DISABLE
357              
358             void
359             Iterator::DESTROY()
360             PPCODE:
361 4           SvREFCNT_dec(THIS->serial);
362 4           Safefree(THIS);
363              
364             void
365             Iterator::next()
366             ALIAS:
367             prev = 1
368              
369             PREINIT:
370 24           int rvnum = 0;
371              
372             PPCODE:
373 24 100         THI_CHECK_ITERATOR;
374              
375 23 50         if (GIMME_V == G_ARRAY && THIS->cur != THIS->end)
    100          
    100          
376             {
377 6 50         EXTEND(SP, 2);
378 6           PUSHs(sv_mortalcopy(THIS->cur->key));
379 6           PUSHs(sv_mortalcopy(THIS->cur->val));
380 6           rvnum = 2;
381             }
382              
383 23 100         THIS->cur = ix == THIS->reverse ? THIS->cur->next : THIS->cur->prev;
384              
385 23           XSRETURN(rvnum);
386              
387             bool
388             Iterator::valid()
389             CODE:
390 18 50         RETVAL = SvIVX(THIS->serial) == THIS->orig_serial &&
    100          
391 18           THIS->cur != THIS->end;
392              
393             OUTPUT:
394             RETVAL
395              
396             void
397             Iterator::key()
398             ALIAS:
399             value = 1
400              
401             PPCODE:
402 30 50         THI_CHECK_ITERATOR;
403 30 100         ST(0) = sv_mortalcopy(ix ? THIS->cur->val : THIS->cur->key);
404 30           XSRETURN(1);
405              
406              
407             MODULE = Tie::Hash::Indexed PACKAGE = Tie::Hash::Indexed
408              
409             PROTOTYPES: DISABLE
410              
411             ################################################################################
412             #
413             # METHOD: TIEHASH
414             #
415             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
416             # CHANGED BY: ON:
417             #
418             ################################################################################
419              
420             IXHV *
421             TIEHASH(CLASS, ...)
422             char *CLASS
423              
424             ALIAS:
425             new = 1
426              
427             PREINIT:
428 14           THI_METHOD(TIEHASH);
429             SV **cur;
430             SV **end;
431              
432             CODE:
433             THI_DEBUG_METHOD;
434             (void) ix;
435              
436 14 50         if (items % 2 == 0)
437             {
438 0           Perl_croak(aTHX_ "odd number of arguments");
439             }
440              
441 14           New(0, RETVAL, 1, IXHV);
442 14           IxLink_new(RETVAL->root);
443 14           RETVAL->iter = NULL;
444 14           RETVAL->hv = newHV();
445 14           RETVAL->serial = newSViv(0);
446 14           RETVAL->signature = THI_SIGNATURE;
447              
448 14           end = &ST(items);
449 28 100         for (cur = &ST(1); cur < end; cur += 2)
450             {
451 14           ixhv_store(aTHX_ RETVAL, cur[0], cur[1], SM_SET);
452             }
453              
454             OUTPUT:
455             RETVAL
456              
457             ################################################################################
458             #
459             # METHOD: DESTROY
460             #
461             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
462             # CHANGED BY: ON:
463             #
464             ################################################################################
465              
466             void
467             IXHV::DESTROY()
468             PREINIT:
469 22           THI_METHOD(DESTROY);
470             IxLink *cur;
471              
472             PPCODE:
473 22           PUTBACK;
474             THI_DEBUG_METHOD;
475 22 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
476              
477 22           THI_INVALIDATE_ITERATORS;
478              
479 155 100         for (cur = THIS->root->next; cur != THIS->root;)
480             {
481 133           IxLink *del = cur;
482 133           cur = cur->next;
483 133           SvREFCNT_dec_NN(del->key);
484 133           SvREFCNT_dec(del->val);
485 133           IxLink_delete(del);
486             }
487              
488 22           IxLink_delete(THIS->root);
489 22           SvREFCNT_dec(THIS->hv);
490 22           SvREFCNT_dec(THIS->serial);
491              
492 22           THIS->root = NULL;
493 22           THIS->iter = NULL;
494 22           THIS->hv = NULL;
495 22           THIS->serial = NULL;
496 22           THIS->signature = 0xDEADC0DE;
497              
498 22           Safefree(THIS);
499 22           return;
500              
501             ################################################################################
502             #
503             # METHOD: FETCH
504             #
505             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
506             # CHANGED BY: ON:
507             #
508             ################################################################################
509              
510             void
511             IXHV::FETCH(key)
512             SV *key
513              
514             ALIAS:
515             get = 1
516              
517             PREINIT:
518 164           THI_METHOD(FETCH);
519             IxLink *link;
520              
521             PPCODE:
522             THI_DEBUG_METHOD1("'%s'", SvPV_nolen(key));
523 164 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
524             (void) ix;
525              
526 164           link = ixhv_find(aTHX_ THIS, key);
527              
528 164 50         ST(0) = link == NULL ? &PL_sv_undef : sv_mortalcopy(link->val);
529              
530 164           XSRETURN(1);
531              
532             ################################################################################
533             #
534             # METHOD: STORE
535             #
536             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
537             # CHANGED BY: ON:
538             #
539             ################################################################################
540              
541             void
542             IXHV::STORE(key, value)
543             SV *key
544             SV *value
545              
546             ALIAS:
547             set = 1
548              
549             PREINIT:
550 69           THI_METHOD(STORE);
551              
552             PPCODE:
553 69           PUTBACK;
554             THI_DEBUG_METHOD2("'%s', '%s'", SvPV_nolen(key), SvPV_nolen(value));
555 69 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
556              
557 69           THI_INVALIDATE_ITERATORS;
558              
559 69 100         bool has_rv = ix == 1 && GIMME_V != G_VOID;
    50          
    100          
    0          
560              
561 69 100         if (has_rv)
562             {
563 3           ST(0) = sv_mortalcopy(value);
564             }
565              
566 69           ixhv_store(aTHX_ THIS, key, value, SM_SET);
567              
568 69 100         if (has_rv)
569             {
570 3           XSRETURN(1);
571             }
572              
573 66           return;
574              
575             ################################################################################
576             #
577             # METHOD: FIRSTKEY
578             #
579             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
580             # CHANGED BY: ON:
581             #
582             ################################################################################
583              
584             void
585             IXHV::FIRSTKEY()
586             PREINIT:
587 34           THI_METHOD(FIRSTKEY);
588              
589             PPCODE:
590             THI_DEBUG_METHOD;
591 34 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
592              
593 34           THIS->iter = THIS->root->next;
594              
595 34 100         if (THIS->iter->key == NULL)
596             {
597 2           XSRETURN_UNDEF;
598             }
599              
600 32           ST(0) = sv_mortalcopy(THIS->iter->key);
601 32           XSRETURN(1);
602              
603             ################################################################################
604             #
605             # METHOD: NEXTKEY
606             #
607             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
608             # CHANGED BY: ON:
609             #
610             ################################################################################
611              
612             void
613             IXHV::NEXTKEY(last)
614             SV *last
615              
616             PREINIT:
617 213           THI_METHOD(NEXTKEY);
618              
619             PPCODE:
620             THI_DEBUG_METHOD1("'%s'", SvPV_nolen(last));
621 213 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
622              
623 213           THIS->iter = THIS->iter->next;
624              
625 213 100         if (THIS->iter->key == NULL)
626             {
627 32           XSRETURN_UNDEF;
628             }
629              
630 181           ST(0) = sv_mortalcopy(THIS->iter->key);
631 181           XSRETURN(1);
632              
633             ################################################################################
634             #
635             # METHOD: EXISTS
636             #
637             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
638             # CHANGED BY: ON:
639             #
640             ################################################################################
641              
642             void
643             IXHV::EXISTS(key)
644             SV *key
645              
646             ALIAS:
647             exists = 1
648             has = 2
649              
650             PREINIT:
651 14           THI_METHOD(EXISTS);
652              
653             PPCODE:
654             THI_DEBUG_METHOD1("'%s'", SvPV_nolen(key));
655 14 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
656             (void) ix;
657              
658 14 100         if (hv_exists_ent(THIS->hv, key, 0))
659             {
660 8           XSRETURN_YES;
661             }
662             else
663             {
664 6           XSRETURN_NO;
665             }
666              
667             ################################################################################
668             #
669             # METHOD: DELETE
670             #
671             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
672             # CHANGED BY: ON:
673             #
674             ################################################################################
675              
676             void
677             IXHV::DELETE(key)
678             SV *key
679              
680             ALIAS:
681             delete = 1
682              
683             PREINIT:
684 11           THI_METHOD(DELETE);
685             IxLink *cur;
686             SV *sv;
687              
688             PPCODE:
689 11           SP++;
690 11           PUTBACK;
691             THI_DEBUG_METHOD1("'%s'", SvPV_nolen(key));
692 11 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
693             (void) ix;
694              
695 11 100         if ((sv = hv_delete_ent(THIS->hv, key, 0, 0)) == NULL)
696             {
697             THI_DEBUG(MAIN, ("key '%s' not found\n", SvPV_nolen(key)));
698 4           *SP = &PL_sv_undef;
699 4           return;
700             }
701              
702 7           THI_INVALIDATE_ITERATORS;
703              
704 7           cur = INT2PTR(IxLink *, SvIVX(sv));
705 7           *SP = sv_2mortal(cur->val);
706              
707 7 100         if (THIS->iter == cur)
708             {
709             THI_DEBUG(MAIN, ("need to move current iterator %p -> %p\n",
710             THIS->iter, cur->prev));
711 4           THIS->iter = cur->prev;
712             }
713              
714 7           IxLink_extract(cur);
715 7           SvREFCNT_dec_NN(cur->key);
716 7           IxLink_delete(cur);
717              
718             THI_DEBUG(MAIN, ("key '%s' deleted\n", SvPV_nolen(key)));
719              
720 7           return;
721              
722             ################################################################################
723             #
724             # METHOD: CLEAR
725             #
726             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
727             # CHANGED BY: ON:
728             #
729             ################################################################################
730              
731             void
732             IXHV::CLEAR()
733             ALIAS:
734             clear = 1
735              
736             PREINIT:
737 14           THI_METHOD(CLEAR);
738              
739             PPCODE:
740             THI_DEBUG_METHOD;
741 14 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
742              
743 14           THI_INVALIDATE_ITERATORS;
744              
745 14           ixhv_clear(aTHX_ THIS);
746              
747 14 100         if (ix == 1 && GIMME_V != G_VOID)
    50          
    100          
748             {
749 1           XSRETURN(1);
750             }
751              
752             ################################################################################
753             #
754             # METHOD: SCALAR
755             #
756             # WRITTEN BY: Marcus Holland-Moritz ON: Jan 2004
757             # CHANGED BY: ON:
758             #
759             ################################################################################
760              
761             void
762             IXHV::SCALAR()
763             PREINIT:
764 5           THI_METHOD(SCALAR);
765              
766             PPCODE:
767             THI_DEBUG_METHOD;
768 5 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
769             #if defined(hv_scalar) && PERL_BCDVERSION < 0x5025003
770             ST(0) = hv_scalar(THIS->hv);
771             #else
772 5           ST(0) = sv_newmortal();
773 5 100         if (HvFILL(THIS->hv))
774             {
775 3           Perl_sv_setpvf(aTHX_ ST(0), "%ld/%ld", (long)HvFILL(THIS->hv),
776 3           (long)HvMAX(THIS->hv)+1);
777             }
778             else
779             {
780 2           sv_setiv(ST(0), 0);
781             }
782             #endif
783 5           XSRETURN(1);
784              
785             ################################################################################
786             #
787             # METHOD: items / as_list / keys / values
788             #
789             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
790             # CHANGED BY: ON:
791             #
792             ################################################################################
793              
794             void
795             IXHV::items(...)
796             ALIAS:
797             as_list = 0
798             keys = 1
799             values = 2
800              
801             PREINIT:
802 35           THI_METHOD(items);
803             long num_keys;
804             long num_items;
805              
806             PPCODE:
807             THI_DEBUG_METHOD;
808 35 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
809              
810 35 100         num_keys = items > 1 ? (unsigned)(items - 1) : HvUSEDKEYS(THIS->hv);
    50          
811 35 100         num_items = (ix == 0 ? 2 : 1)*num_keys;
812              
813 35 50         if (GIMME_V == G_SCALAR)
    100          
814             {
815 12           XSRETURN_IV(num_items);
816             }
817             else
818             {
819 23 100         if (items == 1) /* "vanilla" version */
820             {
821             IxLink *cur;
822              
823 17 50         EXTEND(SP, num_items);
    50          
824              
825 75 100         for (cur = THIS->root->next; cur != THIS->root; cur = cur->next, num_keys--)
826             {
827 58 100         if (ix != 2) PUSHs(sv_mortalcopy(cur->key));
828 58 100         if (ix != 1) PUSHs(sv_mortalcopy(cur->val));
829             }
830              
831             assert(num_keys == 0);
832             }
833             else /* slice version */
834             {
835             SV **end;
836             SV **key;
837             SV **beg;
838             HE *he;
839              
840 6 50         EXTEND(SP, num_items);
    50          
841              
842 6           end = &ST(num_items - 1);
843 6           key = &ST(num_keys - 1);
844 6           beg = &ST(0);
845              
846 6 50         Move(beg + 1, beg, items, SV *);
847              
848 21 100         for (; key >= beg; --key)
849             {
850 15 100         if ((he = hv_fetch_ent(THIS->hv, *key, 0, 0)) != NULL)
851             {
852 9 100         if (ix != 1)
853             {
854 9           *end-- = sv_mortalcopy((INT2PTR(IxLink *, SvIVX(HeVAL(he))))->val);
855             }
856             }
857             else
858             {
859 6 100         if (ix != 1)
860             {
861 4           *end-- = &PL_sv_undef;
862             }
863             }
864 15 100         if (ix != 2) *end-- = *key;
865             }
866             }
867 23           XSRETURN(num_items);
868             }
869              
870             ################################################################################
871             #
872             # METHOD: merge / assign / push / unshift
873             #
874             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
875             # CHANGED BY: ON:
876             #
877             ################################################################################
878              
879             void
880             IXHV::merge(...)
881             ALIAS:
882             assign = 1
883             push = 2
884             unshift = 3
885              
886             PREINIT:
887 5           THI_METHOD(merge);
888             SV **cur;
889             SV **end;
890 5           enum store_mode mode = SM_SET;
891              
892             PPCODE:
893             THI_DEBUG_METHOD;
894 5 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
895              
896 5 50         if (items % 2 == 0)
897             {
898 0           Perl_croak(aTHX_ "odd number of arguments");
899             }
900              
901 5           THI_INVALIDATE_ITERATORS;
902              
903 5           switch (ix)
904             {
905 1           case 1: ixhv_clear(aTHX_ THIS); break;
906 1           case 2: mode = SM_PUSH; break;
907 1           case 3: mode = SM_UNSHIFT; break;
908             }
909              
910 5 100         if (mode == SM_UNSHIFT)
911             {
912 1           end = &ST(0);
913 3 100         for (cur = &ST(items - 1); cur > end; cur -= 2)
914             {
915 2           ixhv_store(aTHX_ THIS, cur[-1], cur[0], mode);
916             }
917             }
918             else
919             {
920 4           end = &ST(items);
921 15 100         for (cur = &ST(1); cur < end; cur += 2)
922             {
923 11           ixhv_store(aTHX_ THIS, cur[0], cur[1], mode);
924             }
925             }
926              
927 5 50         if (GIMME_V != G_VOID)
    100          
928             {
929 4 50         XSRETURN_IV(HvUSEDKEYS(THIS->hv));
930             }
931              
932             ################################################################################
933             #
934             # METHOD: pop / shift
935             #
936             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
937             # CHANGED BY: ON:
938             #
939             ################################################################################
940              
941             void
942             IXHV::pop()
943             ALIAS:
944             shift = 1
945              
946             PREINIT:
947 6           THI_METHOD(pop);
948             IxLink *root;
949             IxLink *goner;
950              
951             PPCODE:
952             THI_DEBUG_METHOD;
953 6 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
954              
955 6           root = THIS->root;
956              
957 6 100         if (root->next == root)
958             {
959 1           XSRETURN_EMPTY;
960             }
961              
962 5           THI_INVALIDATE_ITERATORS;
963              
964 5 100         goner = ix == 0 ? root->prev : root->next;
965 5           IxLink_extract(goner);
966              
967 5           hv_delete_ent(THIS->hv, goner->key, 0, 0);
968              
969 5 50         if (GIMME_V == G_ARRAY)
    100          
970             {
971 2 50         XPUSHs(sv_2mortal(goner->key));
972             }
973             else
974             {
975 3           SvREFCNT_dec_NN(goner->key);
976             }
977              
978 5 50         XPUSHs(sv_2mortal(goner->val));
979              
980 5           IxLink_delete(goner);
981              
982             ################################################################################
983             #
984             # METHOD: iterator / reverse_iterator
985             #
986             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
987             # CHANGED BY: ON:
988             #
989             ################################################################################
990              
991             void
992             IXHV::iterator()
993             ALIAS:
994             reverse_iterator = 1
995              
996             PREINIT:
997 4           THI_METHOD(iterator);
998             Iterator *it;
999              
1000             PPCODE:
1001             THI_DEBUG_METHOD;
1002              
1003 4           New(0, it, 1, Iterator);
1004 4 100         it->cur = ix == 1 ? THIS->root->prev : THIS->root->next;
1005 4           it->end = THIS->root;
1006 4           it->reverse = ix == 1;
1007 4           it->serial = THIS->serial;
1008 4           it->orig_serial = SvIVX(it->serial);
1009              
1010 4           SvREFCNT_inc_simple_void_NN(it->serial);
1011              
1012 4           ST(0) = sv_newmortal();
1013 4           sv_setref_pv(ST(0), "Tie::Hash::Indexed::Iterator", (void *) it);
1014 4           XSRETURN(1);
1015              
1016             ################################################################################
1017             #
1018             # METHOD: preinc / postinc / predec / postdec
1019             #
1020             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
1021             # CHANGED BY: ON:
1022             #
1023             ################################################################################
1024              
1025             void
1026             IXHV::preinc(key)
1027             SV *key
1028              
1029             ALIAS:
1030             predec = 1
1031             postinc = 2
1032             postdec = 3
1033              
1034             PREINIT:
1035 8           THI_METHOD(preinc);
1036             IxLink *link;
1037 8           SV *orig = NULL;
1038              
1039             PPCODE:
1040             THI_DEBUG_METHOD;
1041              
1042 8           link = ixhv_store(aTHX_ THIS, key, NULL, SM_GET_NUM);
1043              
1044 8 100         if (ix >= 2 && GIMME_V != G_VOID)
    50          
    50          
1045             {
1046 4           orig = sv_mortalcopy(link->val);
1047             }
1048              
1049 8           switch (ix)
1050             {
1051             case 0:
1052 4           case 2: sv_inc(link->val);
1053 4           break;
1054              
1055             case 1:
1056 4           case 3: sv_dec(link->val);
1057 4           break;
1058             }
1059              
1060 8 50         SvSETMAGIC(link->val);
1061              
1062 8 50         if (GIMME_V == G_VOID)
    50          
1063             {
1064 0           XSRETURN(0);
1065             }
1066              
1067 8 100         ST(0) = orig ? orig : sv_mortalcopy(link->val);
1068 8           XSRETURN(1);
1069              
1070             ################################################################################
1071             #
1072             # METHOD: add / subtract / multiply / divide / modulo / concat / ...
1073             #
1074             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
1075             # CHANGED BY: ON:
1076             #
1077             ################################################################################
1078              
1079             void
1080             IXHV::add(key, val)
1081             SV *key
1082             SV *val
1083              
1084             ALIAS:
1085             subtract = 1
1086             multiply = 2
1087             divide = 3
1088             modulo = 4
1089             concat = 5
1090             dor_assign = 6
1091             dor_equals = 7
1092             or_assign = 8
1093             or_equals = 9
1094              
1095             PREINIT:
1096 13           THI_METHOD(add);
1097             IxLink *link;
1098             static const int ops[] = {
1099             OP_ADD,
1100             OP_SUBTRACT,
1101             OP_MULTIPLY,
1102             OP_DIVIDE,
1103             OP_MODULO,
1104             OP_CONCAT,
1105             MY_OP_DOR,
1106             MY_OP_DOR,
1107             OP_OR,
1108             OP_OR
1109             };
1110              
1111             PPCODE:
1112             THI_DEBUG_METHOD;
1113              
1114             assert(ix < (int)(sizeof(ops)/sizeof(ops[0])));
1115              
1116 13           link = ixhv_store(aTHX_ THIS, key, NULL, SM_GET);
1117             #if !HAS_OP_DOR
1118             if (ix == 6 || ix == 7)
1119             {
1120             if (!SvOK(link->val))
1121             {
1122             sv_setsv(link->val, val);
1123             SvSETMAGIC(link->val);
1124             }
1125             }
1126             else
1127             #endif
1128             {
1129             OP *oldop;
1130             BINOP myop;
1131              
1132 13           Zero(&myop, 1, struct op);
1133 13           myop.op_flags = OPf_STACKED;
1134 13           myop.op_type = ops[ix];
1135              
1136 13           ENTER;
1137 13           SAVETMPS;
1138              
1139 13 50         PUSHMARK(SP);
1140              
1141 13 100         if (myop.op_type == OP_OR || myop.op_type == MY_OP_DOR)
    100          
1142             {
1143 6 50         XPUSHs(val);
1144 6 50         XPUSHs(link->val);
1145             }
1146             else
1147             {
1148 7 50         XPUSHs(link->val);
1149 7 50         XPUSHs(val);
1150             }
1151              
1152 13           PUTBACK;
1153              
1154 13           oldop = PL_op;
1155 13           PL_op = (OP *) &myop;
1156             #if PERL_BCDVERSION < 0x5006000
1157             PL_ppaddr[PL_op->op_type](ARGS);
1158             #else
1159 13           PL_ppaddr[PL_op->op_type](aTHX);
1160             #endif
1161 13           PL_op = oldop;
1162              
1163 13 100         if (myop.op_type == OP_OR || myop.op_type == MY_OP_DOR)
    100          
1164             {
1165 6           SPAGAIN;
1166 6           sv_setsv(link->val, TOPs);
1167 6 50         SvSETMAGIC(link->val);
1168             }
1169              
1170 13           POPMARK;
1171 13 100         FREETMPS;
1172 13           LEAVE;
1173             }
1174              
1175 13 50         if (GIMME_V != G_VOID)
    100          
1176             {
1177 12           SPAGAIN;
1178 12           ST(0) = sv_mortalcopy(link->val);
1179 12           XSRETURN(1);
1180             }
1181              
1182             ################################################################################
1183             #
1184             # METHOD: STORABLE_freeze
1185             #
1186             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
1187             # CHANGED BY: ON:
1188             #
1189             ################################################################################
1190              
1191             void
1192             IXHV::STORABLE_freeze(cloning)
1193             int cloning;
1194              
1195             PREINIT:
1196 8           THI_METHOD(STORABLE_freeze);
1197             Serialized s;
1198             IxLink *cur;
1199             long num_keys;
1200              
1201             PPCODE:
1202             THI_DEBUG_METHOD1("%d", cloning);
1203 8 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
1204              
1205 8           Copy(THI_SERIAL_ID, &s.rev.id[0], 4, char);
1206 8           s.rev.major = THI_SERIAL_REV_MAJOR;
1207 8           s.rev.minor = THI_SERIAL_REV_MINOR;
1208              
1209 8 50         XPUSHs(sv_2mortal(newSVpvn((char *)&s, sizeof(Serialized))));
1210 8 50         num_keys = HvUSEDKEYS(THIS->hv);
1211 8 50         EXTEND(SP, 2*num_keys);
    50          
1212 80 100         for (cur = THIS->root->next; cur != THIS->root; cur = cur->next, num_keys--)
1213             {
1214 72           PUSHs(sv_2mortal(newRV_inc(cur->key)));
1215 72           PUSHs(sv_2mortal(newRV_inc(cur->val)));
1216             }
1217             assert(num_keys == 0);
1218              
1219             ################################################################################
1220             #
1221             # METHOD: STORABLE_thaw
1222             #
1223             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
1224             # CHANGED BY: ON:
1225             #
1226             ################################################################################
1227              
1228             void
1229             STORABLE_thaw(object, cloning, serialized, ...)
1230             SV *object;
1231             int cloning;
1232             SV *serialized;
1233              
1234             PREINIT:
1235 8           THI_METHOD(STORABLE_thaw);
1236             IXHV *THIS;
1237             Serialized *ps;
1238             STRLEN len;
1239             int i;
1240              
1241             PPCODE:
1242             THI_DEBUG_METHOD1("%d", cloning);
1243              
1244 8 50         if (!sv_isobject(object) || SvTYPE(SvRV(object)) != SVt_PVMG)
    50          
1245 0           Perl_croak(aTHX_ XSCLASS "::%s: THIS is not "
1246             "a blessed SV reference", method);
1247              
1248 8 50         ps = (Serialized *) SvPV(serialized, len);
1249              
1250 8 50         if (len < sizeof(SerialRev) ||
    50          
1251 8           strnNE(THI_SERIAL_ID, &ps->rev.id[0], 4))
1252 0           Perl_croak(aTHX_ "invalid frozen "
1253             XSCLASS " object (len=%zu)", len);
1254              
1255 8 50         if (ps->rev.major != THI_SERIAL_REV_MAJOR)
1256 0           Perl_croak(aTHX_ "cannot thaw incompatible "
1257             XSCLASS " object");
1258              
1259             /* TODO: implement minor revision handling */
1260              
1261 8           New(0, THIS, 1, IXHV);
1262 8           sv_setiv((SV*)SvRV(object), PTR2IV(THIS));
1263              
1264 8           THIS->serial = newSViv(0);
1265 8           THIS->signature = THI_SIGNATURE;
1266 8           THIS->hv = newHV();
1267 8           THIS->iter = NULL;
1268 8           IxLink_new(THIS->root);
1269              
1270 8 50         if ((items-3) % 2)
1271 0           Perl_croak(aTHX_ "odd number of items in STORABLE_thaw");
1272              
1273 80 100         for (i = 3; i < items; i+=2)
1274             {
1275             IxLink *cur;
1276             SV *key, *val;
1277              
1278 72           key = SvRV(ST(i));
1279 72           val = SvRV(ST(i+1));
1280              
1281 72           IxLink_new(cur);
1282 72           IxLink_push(THIS->root, cur);
1283              
1284 72           cur->key = newSVsv(key);
1285 72           cur->val = newSVsv(val);
1286              
1287 72           val = newSViv(PTR2IV(cur));
1288              
1289 72 50         if (hv_store_ent(THIS->hv, key, val, 0) == NULL)
1290             {
1291 0           SvREFCNT_dec(val);
1292 0           Perl_croak(aTHX_ "couldn't store value");
1293             }
1294             }
1295              
1296 8           XSRETURN_EMPTY;
1297              
1298             ################################################################################
1299             #
1300             # BOOTCODE
1301             #
1302             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
1303             # CHANGED BY: ON:
1304             #
1305             ################################################################################
1306              
1307             BOOT:
1308             #ifdef THI_DEBUGGING
1309             {
1310             const char *str;
1311             if ((str = getenv("THI_DEBUG_OPT")) != NULL)
1312             set_debug_opt(aTHX_ str);
1313             }
1314             #endif