File Coverage

Indexed.xs
Criterion Covered Total %
statement 287 301 95.3
branch 201 332 60.5
condition n/a
subroutine n/a
pod n/a
total 488 633 77.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) 2002-2016 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 88           static void ixlink_insert(IxLink *root, IxLink *cur, enum store_mode mode)
258             {
259 88 100         switch (mode)
260             {
261 2           case SM_UNSHIFT: IxLink_unshift(root, cur); break;
262 86           default: IxLink_push(root, cur); break;
263             }
264 88           }
265              
266 108           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 108 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 108           pair = HeVAL(he);
278              
279 108 100         if (SvTYPE(pair) == SVt_NULL)
280             {
281 86           IxLink_new(cur);
282              
283 86           ixlink_insert(THIS->root, cur, mode);
284              
285 86           sv_setiv(pair, PTR2IV(cur));
286              
287 86           cur->key = newSVsv(key);
288              
289 86 100         if (mode == SM_GET_NUM)
290             {
291 4           cur->val = newSViv(0);
292             }
293             else
294             {
295 82 50         if (mode == SM_GET && !value)
    0          
296             {
297 0           value = &PL_sv_undef;
298             }
299             assert(value);
300 86           cur->val = newSVsv(value);
301             }
302             }
303             else
304             {
305 22           cur = INT2PTR(IxLink *, SvIVX(pair));
306              
307 22 100         if (mode < SM_GET)
308             {
309 9 100         if (mode != SM_SET)
310             {
311 2           IxLink_extract(cur);
312 2           ixlink_insert(THIS->root, cur, mode);
313             }
314              
315 9           sv_setsv(cur->val, value);
316             }
317             }
318              
319 108           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 160           static IxLink *ixhv_find(pTHX_ IXHV *THIS, SV *key)
341             {
342             HE *he;
343              
344 160 50         if ((he = hv_fetch_ent(THIS->hv, key, 0, 0)) == NULL)
345             {
346 0           return NULL;
347             }
348              
349 160           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 3           SvREFCNT_dec(THIS->serial);
362 3           Safefree(THIS);
363              
364             void
365             Iterator::next()
366             ALIAS:
367             prev = 1
368              
369             PREINIT:
370 19           int rvnum = 0;
371              
372             PPCODE:
373 19 100         THI_CHECK_ITERATOR;
374              
375 18 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 18 100         THIS->cur = ix == THIS->reverse ? THIS->cur->next : THIS->cur->prev;
384              
385 18           XSRETURN(rvnum);
386              
387             bool
388             Iterator::valid()
389             CODE:
390 12 50         RETVAL = SvIVX(THIS->serial) == THIS->orig_serial &&
    100          
391 12           THIS->cur != THIS->end;
392              
393             OUTPUT:
394             RETVAL
395              
396             void
397             Iterator::key()
398             ALIAS:
399             value = 1
400              
401             PPCODE:
402 20 50         THI_CHECK_ITERATOR;
403 20 100         ST(0) = sv_mortalcopy(ix ? THIS->cur->val : THIS->cur->key);
404 20           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 12           THI_METHOD(TIEHASH);
429             SV **cur;
430             SV **end;
431              
432             CODE:
433             THI_DEBUG_METHOD;
434             (void) ix;
435              
436 12 50         if (items % 2 == 0)
437             {
438 0           Perl_croak(aTHX_ "odd number of arguments");
439             }
440              
441 12           New(0, RETVAL, 1, IXHV);
442 12           IxLink_new(RETVAL->root);
443 12           RETVAL->iter = NULL;
444 12           RETVAL->hv = newHV();
445 12           RETVAL->serial = newSViv(0);
446 12           RETVAL->signature = THI_SIGNATURE;
447              
448 12           end = &ST(items);
449 24 100         for (cur = &ST(1); cur < end; cur += 2)
450             {
451 12           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 20           THI_METHOD(DESTROY);
470             IxLink *cur;
471              
472             PPCODE:
473 20           PUTBACK;
474             THI_DEBUG_METHOD;
475 20 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
476              
477 20           THI_INVALIDATE_ITERATORS;
478              
479 151 100         for (cur = THIS->root->next; cur != THIS->root;)
480             {
481 131           IxLink *del = cur;
482 131           cur = cur->next;
483 131           SvREFCNT_dec_NN(del->key);
484 131           SvREFCNT_dec(del->val);
485 131           IxLink_delete(del);
486             }
487              
488 20           IxLink_delete(THIS->root);
489 20           SvREFCNT_dec(THIS->hv);
490 20           SvREFCNT_dec(THIS->serial);
491              
492 20           THIS->root = NULL;
493 20           THIS->iter = NULL;
494 20           THIS->hv = NULL;
495 20           THIS->serial = NULL;
496 20           THIS->signature = 0xDEADC0DE;
497              
498 20           Safefree(THIS);
499 20           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 160           THI_METHOD(FETCH);
519             IxLink *link;
520              
521             PPCODE:
522             THI_DEBUG_METHOD1("'%s'", SvPV_nolen(key));
523 160 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
524             (void) ix;
525              
526 160           link = ixhv_find(aTHX_ THIS, key);
527              
528 160 50         ST(0) = link == NULL ? &PL_sv_undef : sv_mortalcopy(link->val);
529              
530 160           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 66           THI_METHOD(STORE);
551              
552             PPCODE:
553 66           PUTBACK;
554             THI_DEBUG_METHOD2("'%s', '%s'", SvPV_nolen(key), SvPV_nolen(value));
555 66 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
556             (void) ix;
557              
558 66           THI_INVALIDATE_ITERATORS;
559              
560 66           ixhv_store(aTHX_ THIS, key, value, SM_SET);
561 66           return;
562              
563             ################################################################################
564             #
565             # METHOD: FIRSTKEY
566             #
567             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
568             # CHANGED BY: ON:
569             #
570             ################################################################################
571              
572             void
573             IXHV::FIRSTKEY()
574             PREINIT:
575 34           THI_METHOD(FIRSTKEY);
576              
577             PPCODE:
578             THI_DEBUG_METHOD;
579 34 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
580              
581 34           THIS->iter = THIS->root->next;
582              
583 34 100         if (THIS->iter->key == NULL)
584             {
585 2           XSRETURN_UNDEF;
586             }
587              
588 32           ST(0) = sv_mortalcopy(THIS->iter->key);
589 32           XSRETURN(1);
590              
591             ################################################################################
592             #
593             # METHOD: NEXTKEY
594             #
595             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
596             # CHANGED BY: ON:
597             #
598             ################################################################################
599              
600             void
601             IXHV::NEXTKEY(last)
602             SV *last
603              
604             PREINIT:
605 213           THI_METHOD(NEXTKEY);
606              
607             PPCODE:
608             THI_DEBUG_METHOD1("'%s'", SvPV_nolen(last));
609 213 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
610              
611 213           THIS->iter = THIS->iter->next;
612              
613 213 100         if (THIS->iter->key == NULL)
614             {
615 32           XSRETURN_UNDEF;
616             }
617              
618 181           ST(0) = sv_mortalcopy(THIS->iter->key);
619 181           XSRETURN(1);
620              
621             ################################################################################
622             #
623             # METHOD: EXISTS
624             #
625             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
626             # CHANGED BY: ON:
627             #
628             ################################################################################
629              
630             void
631             IXHV::EXISTS(key)
632             SV *key
633              
634             ALIAS:
635             exists = 1
636             has = 2
637              
638             PREINIT:
639 14           THI_METHOD(EXISTS);
640              
641             PPCODE:
642             THI_DEBUG_METHOD1("'%s'", SvPV_nolen(key));
643 14 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
644             (void) ix;
645              
646 14 100         if (hv_exists_ent(THIS->hv, key, 0))
647             {
648 8           XSRETURN_YES;
649             }
650             else
651             {
652 6           XSRETURN_NO;
653             }
654              
655             ################################################################################
656             #
657             # METHOD: DELETE
658             #
659             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
660             # CHANGED BY: ON:
661             #
662             ################################################################################
663              
664             void
665             IXHV::DELETE(key)
666             SV *key
667              
668             ALIAS:
669             delete = 1
670              
671             PREINIT:
672 11           THI_METHOD(DELETE);
673             IxLink *cur;
674             SV *sv;
675              
676             PPCODE:
677 11           SP++;
678 11           PUTBACK;
679             THI_DEBUG_METHOD1("'%s'", SvPV_nolen(key));
680 11 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
681             (void) ix;
682              
683 11 100         if ((sv = hv_delete_ent(THIS->hv, key, 0, 0)) == NULL)
684             {
685             THI_DEBUG(MAIN, ("key '%s' not found\n", SvPV_nolen(key)));
686 4           *SP = &PL_sv_undef;
687 4           return;
688             }
689              
690 7           THI_INVALIDATE_ITERATORS;
691              
692 7           cur = INT2PTR(IxLink *, SvIVX(sv));
693 7           *SP = sv_2mortal(cur->val);
694              
695 7 100         if (THIS->iter == cur)
696             {
697             THI_DEBUG(MAIN, ("need to move current iterator %p -> %p\n",
698             THIS->iter, cur->prev));
699 4           THIS->iter = cur->prev;
700             }
701              
702 7           IxLink_extract(cur);
703 7           SvREFCNT_dec_NN(cur->key);
704 7           IxLink_delete(cur);
705              
706             THI_DEBUG(MAIN, ("key '%s' deleted\n", SvPV_nolen(key)));
707              
708 7           return;
709              
710             ################################################################################
711             #
712             # METHOD: CLEAR
713             #
714             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
715             # CHANGED BY: ON:
716             #
717             ################################################################################
718              
719             void
720             IXHV::CLEAR()
721             ALIAS:
722             clear = 1
723              
724             PREINIT:
725 14           THI_METHOD(CLEAR);
726              
727             PPCODE:
728             THI_DEBUG_METHOD;
729 14 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
730             (void) ix;
731              
732 14           THI_INVALIDATE_ITERATORS;
733              
734 14           ixhv_clear(aTHX_ THIS);
735              
736 14 100         if (ix == 1 && GIMME_V != G_VOID)
    50          
    100          
737             {
738 1           XSRETURN(1);
739             }
740              
741             ################################################################################
742             #
743             # METHOD: SCALAR
744             #
745             # WRITTEN BY: Marcus Holland-Moritz ON: Jan 2004
746             # CHANGED BY: ON:
747             #
748             ################################################################################
749              
750             void
751             IXHV::SCALAR()
752             PREINIT:
753 5           THI_METHOD(SCALAR);
754              
755             PPCODE:
756             THI_DEBUG_METHOD;
757 5 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
758             #if defined(hv_scalar) && PERL_BCDVERSION < 0x5025003
759             ST(0) = hv_scalar(THIS->hv);
760             #else
761 5           ST(0) = sv_newmortal();
762 5 100         if (HvFILL(THIS->hv))
763             {
764 3           Perl_sv_setpvf(aTHX_ ST(0), "%ld/%ld", (long)HvFILL(THIS->hv),
765 3           (long)HvMAX(THIS->hv)+1);
766             }
767             else
768             {
769 2           sv_setiv(ST(0), 0);
770             }
771             #endif
772 5           XSRETURN(1);
773              
774             ################################################################################
775             #
776             # METHOD: items / as_list / keys / values
777             #
778             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
779             # CHANGED BY: ON:
780             #
781             ################################################################################
782              
783             void
784             IXHV::items(...)
785             ALIAS:
786             as_list = 0
787             keys = 1
788             values = 2
789              
790             PREINIT:
791 30           THI_METHOD(items);
792             long num_keys;
793             long num_items;
794              
795             PPCODE:
796             THI_DEBUG_METHOD;
797 30 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
798              
799 30 100         num_keys = items > 1 ? (items - 1) : HvUSEDKEYS(THIS->hv);
    50          
800 30 100         num_items = (ix == 0 ? 2 : 1)*num_keys;
801              
802 30 50         if (GIMME_V == G_SCALAR)
    100          
803             {
804 10 50         mXPUSHi(num_items);
805             }
806             else
807             {
808 20 100         if (items == 1) /* "vanilla" version */
809             {
810             IxLink *cur;
811              
812 17 50         EXTEND(SP, num_items);
    50          
813              
814 75 100         for (cur = THIS->root->next; cur != THIS->root; cur = cur->next, num_keys--)
815             {
816 58 100         if (ix != 2) PUSHs(sv_mortalcopy(cur->key));
817 58 100         if (ix != 1) PUSHs(sv_mortalcopy(cur->val));
818             }
819              
820             assert(num_keys == 0);
821             }
822             else /* slice version */
823             {
824             SV **end;
825             SV **key;
826             SV **beg;
827             HE *he;
828              
829 3 50         EXTEND(SP, num_items);
    50          
830              
831 3           end = &ST(num_items - 1);
832 3           key = &ST(num_keys - 1);
833 3           beg = &ST(0);
834              
835 3 50         Move(beg + 1, beg, items, SV *);
836              
837 9 100         for (; key >= beg; --key)
838             {
839 6 50         if ((he = hv_fetch_ent(THIS->hv, *key, 0, 0)) != NULL)
840             {
841 6 100         if (ix != 1)
842             {
843 6           *end-- = sv_mortalcopy((INT2PTR(IxLink *, SvIVX(HeVAL(he))))->val);
844             }
845             }
846             else
847             {
848 0 0         if (ix != 1)
849             {
850 0           *end-- = &PL_sv_undef;
851             }
852             }
853 6 100         if (ix != 2) *end-- = *key;
854             }
855             }
856 20           XSRETURN(num_items);
857             }
858              
859             ################################################################################
860             #
861             # METHOD: merge / assign / push / unshift
862             #
863             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
864             # CHANGED BY: ON:
865             #
866             ################################################################################
867              
868             void
869             IXHV::merge(...)
870             ALIAS:
871             assign = 1
872             push = 2
873             unshift = 3
874              
875             PREINIT:
876 5           THI_METHOD(merge);
877             SV **cur;
878             SV **end;
879 5           enum store_mode mode = SM_SET;
880              
881             PPCODE:
882             THI_DEBUG_METHOD;
883 5 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
884              
885 5 50         if (items % 2 == 0)
886             {
887 0           Perl_croak(aTHX_ "odd number of arguments");
888             }
889              
890 5           THI_INVALIDATE_ITERATORS;
891              
892 5           switch (ix)
893             {
894 1           case 1: ixhv_clear(aTHX_ THIS); break;
895 1           case 2: mode = SM_PUSH; break;
896 1           case 3: mode = SM_UNSHIFT; break;
897             }
898              
899 5 100         if (mode == SM_UNSHIFT)
900             {
901 1           end = &ST(0);
902 3 100         for (cur = &ST(items - 1); cur > end; cur -= 2)
903             {
904 2           ixhv_store(aTHX_ THIS, cur[-1], cur[0], mode);
905             }
906             }
907             else
908             {
909 4           end = &ST(items);
910 15 100         for (cur = &ST(1); cur < end; cur += 2)
911             {
912 11           ixhv_store(aTHX_ THIS, cur[0], cur[1], mode);
913             }
914             }
915              
916 5 50         if (GIMME_V != G_VOID)
    100          
917             {
918 4 50         XSRETURN_IV(HvUSEDKEYS(THIS->hv));
919             }
920              
921             ################################################################################
922             #
923             # METHOD: pop / shift
924             #
925             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
926             # CHANGED BY: ON:
927             #
928             ################################################################################
929              
930             void
931             IXHV::pop()
932             ALIAS:
933             shift = 1
934              
935             PREINIT:
936 6           THI_METHOD(pop);
937             IxLink *root;
938             IxLink *goner;
939              
940             PPCODE:
941             THI_DEBUG_METHOD;
942 6 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
943              
944 6           root = THIS->root;
945              
946 6 100         if (root->next == root)
947             {
948 1           XSRETURN_EMPTY;
949             }
950              
951 5           THI_INVALIDATE_ITERATORS;
952              
953 5 100         goner = ix == 0 ? root->prev : root->next;
954 5           IxLink_extract(goner);
955              
956 5           hv_delete_ent(THIS->hv, goner->key, 0, 0);
957              
958 5 50         if (GIMME_V == G_ARRAY)
    100          
959             {
960 2 50         XPUSHs(sv_2mortal(goner->key));
961             }
962             else
963             {
964 3           SvREFCNT_dec_NN(goner->key);
965             }
966              
967 5 50         XPUSHs(sv_2mortal(goner->val));
968              
969 5           IxLink_delete(goner);
970              
971             ################################################################################
972             #
973             # METHOD: iterator / reverse_iterator
974             #
975             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
976             # CHANGED BY: ON:
977             #
978             ################################################################################
979              
980             void
981             IXHV::iterator()
982             ALIAS:
983             reverse_iterator = 1
984              
985             PREINIT:
986 3           THI_METHOD(iterator);
987             Iterator *it;
988              
989             PPCODE:
990             THI_DEBUG_METHOD;
991              
992 3           New(0, it, 1, Iterator);
993 3 100         it->cur = ix == 1 ? THIS->root->prev : THIS->root->next;
994 3           it->end = THIS->root;
995 3           it->reverse = ix == 1;
996 3           it->serial = THIS->serial;
997 3           it->orig_serial = SvIVX(it->serial);
998              
999 3           SvREFCNT_inc_simple_void_NN(it->serial);
1000              
1001 3           ST(0) = sv_newmortal();
1002 3           sv_setref_pv(ST(0), "Tie::Hash::Indexed::Iterator", (void *) it);
1003 3           XSRETURN(1);
1004              
1005             ################################################################################
1006             #
1007             # METHOD: preinc / postinc / predec / postdec
1008             #
1009             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
1010             # CHANGED BY: ON:
1011             #
1012             ################################################################################
1013              
1014             void
1015             IXHV::preinc(key)
1016             SV *key
1017              
1018             ALIAS:
1019             predec = 1
1020             postinc = 2
1021             postdec = 3
1022              
1023             PREINIT:
1024 8           THI_METHOD(preinc);
1025             IxLink *link;
1026 8           SV *orig = NULL;
1027              
1028             PPCODE:
1029             THI_DEBUG_METHOD;
1030              
1031 8           link = ixhv_store(aTHX_ THIS, key, NULL, SM_GET_NUM);
1032              
1033 8 100         if (ix >= 2 && GIMME_V != G_VOID)
    50          
    50          
1034             {
1035 4           orig = sv_mortalcopy(link->val);
1036             }
1037              
1038 8           switch (ix)
1039             {
1040             case 0:
1041 4           case 2: sv_inc(link->val);
1042 4           break;
1043              
1044             case 1:
1045 4           case 3: sv_dec(link->val);
1046 4           break;
1047             }
1048              
1049 8 50         SvSETMAGIC(link->val);
1050              
1051 8 50         if (GIMME_V == G_VOID)
    50          
1052             {
1053 0           XSRETURN(0);
1054             }
1055              
1056 8 100         ST(0) = orig ? orig : sv_mortalcopy(link->val);
1057 8           XSRETURN(1);
1058              
1059             ################################################################################
1060             #
1061             # METHOD: add / subtract / multiply / divide / modulo / concat / ...
1062             #
1063             # WRITTEN BY: Marcus Holland-Moritz ON: May 2016
1064             # CHANGED BY: ON:
1065             #
1066             ################################################################################
1067              
1068             void
1069             IXHV::add(key, val)
1070             SV *key
1071             SV *val
1072              
1073             ALIAS:
1074             subtract = 1
1075             multiply = 2
1076             divide = 3
1077             modulo = 4
1078             concat = 5
1079             dor_assign = 6
1080             or_assign = 7
1081              
1082             PREINIT:
1083 9           THI_METHOD(add);
1084             IxLink *link;
1085             static const int ops[] = {
1086             OP_ADD,
1087             OP_SUBTRACT,
1088             OP_MULTIPLY,
1089             OP_DIVIDE,
1090             OP_MODULO,
1091             OP_CONCAT,
1092             MY_OP_DOR,
1093             OP_OR
1094             };
1095              
1096             PPCODE:
1097             THI_DEBUG_METHOD;
1098              
1099             assert(ix < (int)(sizeof(ops)/sizeof(ops[0])));
1100              
1101 9           link = ixhv_store(aTHX_ THIS, key, NULL, SM_GET);
1102             #if !HAS_OP_DOR
1103             if (ix == 6)
1104             {
1105             if (!SvOK(link->val))
1106             {
1107             sv_setsv(link->val, val);
1108             SvSETMAGIC(link->val);
1109             }
1110             }
1111             else
1112             #endif
1113             {
1114             OP *oldop;
1115             BINOP myop;
1116              
1117 9           Zero(&myop, 1, struct op);
1118 9           myop.op_flags = OPf_STACKED;
1119 9           myop.op_type = ops[ix];
1120              
1121 9           ENTER;
1122 9           SAVETMPS;
1123              
1124 9 50         PUSHMARK(SP);
1125              
1126 9 100         if (myop.op_type == OP_OR || myop.op_type == MY_OP_DOR)
    100          
1127             {
1128 3 50         XPUSHs(val);
1129 3 50         XPUSHs(link->val);
1130             }
1131             else
1132             {
1133 6 50         XPUSHs(link->val);
1134 6 50         XPUSHs(val);
1135             }
1136              
1137 9           PUTBACK;
1138              
1139 9           oldop = PL_op;
1140 9           PL_op = (OP *) &myop;
1141             #if PERL_BCDVERSION < 0x5006000
1142             PL_ppaddr[PL_op->op_type](ARGS);
1143             #else
1144 9           PL_ppaddr[PL_op->op_type](aTHX);
1145             #endif
1146 9           PL_op = oldop;
1147              
1148 9 100         if (myop.op_type == OP_OR || myop.op_type == MY_OP_DOR)
    100          
1149             {
1150 3           SPAGAIN;
1151 3           sv_setsv(link->val, TOPs);
1152 3 50         SvSETMAGIC(link->val);
1153             }
1154              
1155 9           POPMARK;
1156 9 100         FREETMPS;
1157 9           LEAVE;
1158             }
1159              
1160 9 50         if (GIMME_V != G_VOID)
    100          
1161             {
1162 8           SPAGAIN;
1163 8           ST(0) = sv_mortalcopy(link->val);
1164 8           XSRETURN(1);
1165             }
1166              
1167             ################################################################################
1168             #
1169             # METHOD: STORABLE_freeze
1170             #
1171             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
1172             # CHANGED BY: ON:
1173             #
1174             ################################################################################
1175              
1176             void
1177             IXHV::STORABLE_freeze(cloning)
1178             int cloning;
1179              
1180             PREINIT:
1181 8           THI_METHOD(STORABLE_freeze);
1182             Serialized s;
1183             IxLink *cur;
1184             long num_keys;
1185              
1186             PPCODE:
1187             THI_DEBUG_METHOD1("%d", cloning);
1188 8 50         THI_CHECK_OBJECT;
    50          
    0          
    50          
    50          
1189              
1190 8           Copy(THI_SERIAL_ID, &s.rev.id[0], 4, char);
1191 8           s.rev.major = THI_SERIAL_REV_MAJOR;
1192 8           s.rev.minor = THI_SERIAL_REV_MINOR;
1193              
1194 8 50         XPUSHs(sv_2mortal(newSVpvn((char *)&s, sizeof(Serialized))));
1195 8 50         num_keys = HvUSEDKEYS(THIS->hv);
1196 8 50         EXTEND(SP, 2*num_keys);
    50          
1197 80 100         for (cur = THIS->root->next; cur != THIS->root; cur = cur->next, num_keys--)
1198             {
1199 72           PUSHs(sv_2mortal(newRV_inc(cur->key)));
1200 72           PUSHs(sv_2mortal(newRV_inc(cur->val)));
1201             }
1202             assert(num_keys == 0);
1203              
1204             ################################################################################
1205             #
1206             # METHOD: STORABLE_thaw
1207             #
1208             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
1209             # CHANGED BY: ON:
1210             #
1211             ################################################################################
1212              
1213             void
1214             STORABLE_thaw(object, cloning, serialized, ...)
1215             SV *object;
1216             int cloning;
1217             SV *serialized;
1218              
1219             PREINIT:
1220 8           THI_METHOD(STORABLE_thaw);
1221             IXHV *THIS;
1222             Serialized *ps;
1223             STRLEN len;
1224             int i;
1225              
1226             PPCODE:
1227             THI_DEBUG_METHOD1("%d", cloning);
1228              
1229 8 50         if (!sv_isobject(object) || SvTYPE(SvRV(object)) != SVt_PVMG)
    50          
1230 0           Perl_croak(aTHX_ XSCLASS "::%s: THIS is not "
1231             "a blessed SV reference", method);
1232              
1233 8 50         ps = (Serialized *) SvPV(serialized, len);
1234              
1235 8 50         if (len < sizeof(SerialRev) ||
    50          
1236 8           strnNE(THI_SERIAL_ID, &ps->rev.id[0], 4))
1237 0           Perl_croak(aTHX_ "invalid frozen "
1238             XSCLASS " object (len=%zu)", len);
1239              
1240 8 50         if (ps->rev.major != THI_SERIAL_REV_MAJOR)
1241 0           Perl_croak(aTHX_ "cannot thaw incompatible "
1242             XSCLASS " object");
1243              
1244             /* TODO: implement minor revision handling */
1245              
1246 8           New(0, THIS, 1, IXHV);
1247 8           sv_setiv((SV*)SvRV(object), PTR2IV(THIS));
1248              
1249 8           THIS->serial = newSViv(0);
1250 8           THIS->signature = THI_SIGNATURE;
1251 8           THIS->hv = newHV();
1252 8           THIS->iter = NULL;
1253 8           IxLink_new(THIS->root);
1254              
1255 8 50         if ((items-3) % 2)
1256 0           Perl_croak(aTHX_ "odd number of items in STORABLE_thaw");
1257              
1258 80 100         for (i = 3; i < items; i+=2)
1259             {
1260             IxLink *cur;
1261             SV *key, *val;
1262              
1263 72           key = SvRV(ST(i));
1264 72           val = SvRV(ST(i+1));
1265              
1266 72           IxLink_new(cur);
1267 72           IxLink_push(THIS->root, cur);
1268              
1269 72           cur->key = newSVsv(key);
1270 72           cur->val = newSVsv(val);
1271              
1272 72           val = newSViv(PTR2IV(cur));
1273              
1274 72 50         if (hv_store_ent(THIS->hv, key, val, 0) == NULL)
1275             {
1276 0           SvREFCNT_dec(val);
1277 0           Perl_croak(aTHX_ "couldn't store value");
1278             }
1279             }
1280              
1281 8           XSRETURN_EMPTY;
1282              
1283             ################################################################################
1284             #
1285             # BOOTCODE
1286             #
1287             # WRITTEN BY: Marcus Holland-Moritz ON: Nov 2003
1288             # CHANGED BY: ON:
1289             #
1290             ################################################################################
1291              
1292             BOOT:
1293             #ifdef THI_DEBUGGING
1294             {
1295             const char *str;
1296             if ((str = getenv("THI_DEBUG_OPT")) != NULL)
1297             set_debug_opt(aTHX_ str);
1298             }
1299             #endif