File Coverage

Object.xs
Criterion Covered Total %
statement 389 427 91.1
branch 283 416 68.0
condition n/a
subroutine n/a
pod n/a
total 672 843 79.7


line stmt bran cond sub pod time code
1             #ifdef __cplusplus
2             extern "C" {
3             #endif
4             #define PERL_POLLUTE
5             #include "EXTERN.h"
6             #include "perl.h"
7             #include "XSUB.h"
8             #ifdef __cplusplus
9             }
10             #endif
11              
12             #include "ppport.h"
13              
14             #if __GNUC__ >= 3 /* I guess. */
15             #define _warn(msg, e...) warn("# (" __FILE__ ":%d): " msg, __LINE__, ##e)
16             #else
17             #define _warn warn
18             #endif
19              
20             #ifdef SET_DEBUG
21             /* for debugging object-related functions */
22             #define IF_DEBUG(e) e
23             /* for debugging scalar-related functions */
24             #define IF_REMOVE_DEBUG(e) e
25             #define IF_INSERT_DEBUG(e) e
26             /* for debugging weakref-related functions */
27             #define IF_SPELL_DEBUG(e) e
28             #else
29             #define IF_DEBUG(e)
30             #define IF_REMOVE_DEBUG(e)
31             #define IF_INSERT_DEBUG(e)
32             #define IF_SPELL_DEBUG(e)
33             #endif
34              
35             #if (PERL_VERSION > 7) || ( (PERL_VERSION == 7)&&( PERL_SUBVERSION > 2))
36             #define SET_OBJECT_MAGIC_backref (int)((char)0x9f)
37             #else
38             #define SET_OBJECT_MAGIC_backref '~'
39             #endif
40              
41             #define __PACKAGE__ "Set::Object"
42              
43             typedef struct _BUCKET
44             {
45             SV** sv;
46             int n;
47             } BUCKET;
48              
49             typedef struct _ISET
50             {
51             BUCKET* bucket;
52             I32 buckets, elems;
53             SV* is_weak;
54             HV* flat;
55             } ISET;
56              
57             #ifdef USE_ITHREADS
58             # define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
59             # ifndef MY_CXT_CLONE
60             # define MY_CXT_CLONE \
61             dMY_CXT_SV; \
62             my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
63             Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
64             sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
65             # endif
66              
67             typedef struct {
68             ISET *s;
69             } my_cxt_t;
70              
71             STATIC perl_mutex iset_mutex;
72              
73             START_MY_CXT
74             # define THR_LOCK MUTEX_LOCK(&iset_mutex)
75             # define THR_UNLOCK MUTEX_UNLOCK(&iset_mutex)
76              
77             #else
78             # define THR_LOCK
79             # define THR_UNLOCK
80             #endif
81              
82             #define ISET_HASH(el) ((PTR2UV(el)) >> 4)
83              
84             #define ISET_INSERT(s, item) \
85             ( SvROK(item) \
86             ? iset_insert_one(s, item) \
87             : iset_insert_scalar(s, item) )
88              
89             int iset_remove_one(ISET* s, SV* el, int spell_in_progress);
90              
91              
92 282           int insert_in_bucket(BUCKET* pb, SV* sv)
93             {
94 282 100         if (!pb->sv)
95             {
96 215           New(0, pb->sv, 1, SV*);
97 215           pb->sv[0] = sv;
98 215           pb->n = 1;
99             IF_DEBUG(_warn("inserting %p in bucket %p offset %d", sv, pb, 0));
100             }
101             else
102             {
103 67           SV **iter = pb->sv, **last = pb->sv + pb->n, **hole = 0;
104              
105 138 100         for (; iter != last; ++iter)
106             {
107 81 50         if (*iter)
108             {
109 81 100         if (*iter == sv)
110 10           return 0;
111             }
112             else
113 0           hole = iter;
114             }
115              
116 57 50         if (!hole)
117             {
118 57 50         Renew(pb->sv, pb->n + 1, SV*);
119 57           hole = pb->sv + pb->n;
120 57           ++pb->n;
121             }
122              
123 57           *hole = sv;
124              
125             IF_DEBUG(_warn("inserting %p in bucket %p offset %ld", sv, pb, iter - pb->sv));
126             }
127 272           return 1;
128             }
129              
130 617           int iset_insert_scalar(ISET* s, SV* sv)
131             {
132             STRLEN len;
133 617           char* key = 0;
134              
135 617 100         if (!s->flat) {
136             IF_INSERT_DEBUG(_warn("iset_insert_scalar(%p): creating scalar hash", s));
137 160           s->flat = newHV();
138             }
139              
140 617 100         if (!SvOK(sv))
    50          
    50          
141 19           return 0;
142              
143 598 100         key = SvPV(sv, len);
144             IF_INSERT_DEBUG(_warn("iset_insert_scalar(%p): sv (%p, rc = %d, str= '%s')!", s, sv, SvREFCNT(sv), SvPV_nolen(sv)));
145              
146             THR_LOCK;
147 598 100         if (!hv_exists(s->flat, key, len)) {
148 579 50         if (!hv_store(s->flat, key, len, &PL_sv_undef, 0)) {
149             THR_UNLOCK;
150 0           _warn("hv store failed[?] set=%p", s);
151             } else {
152             THR_UNLOCK;
153             }
154             IF_INSERT_DEBUG(_warn("iset_insert_scalar(%p): inserted OK!", s));
155 579           return 1;
156             }
157             else {
158             THR_UNLOCK;
159             IF_INSERT_DEBUG(_warn("iset_insert_scalar(%p): already there!", s));
160 617           return 0;
161             }
162             }
163              
164 36           int iset_remove_scalar(ISET* s, SV* sv)
165             {
166             STRLEN len;
167 36           char* key = 0;
168              
169 36 50         if (!s->flat || !HvKEYS(s->flat)) {
    50          
    50          
170             //IF_REMOVE_DEBUG(_warn("iset_remove_scalar(%p):'%s' (no hash)", s, SvPV_nolen(sv)));
171 0           return 0;
172             }
173              
174             IF_REMOVE_DEBUG(_warn("iset_remove_scalar(%p): sv (%p, rc=%d, str='%s')"
175             #ifdef USE_ITHREADS
176             " interp=%p"
177             #endif
178             , s, sv, SvREFCNT(sv), SvPV_nolen(sv)
179             #ifdef USE_ITHREADS
180             , PERL_GET_CONTEXT
181             #endif
182             ));
183 36 50         key = SvPV(sv, len);
184              
185             THR_LOCK;
186 36 50         if ( hv_delete(s->flat, key, len, 0) ) {
187             THR_UNLOCK;
188             IF_REMOVE_DEBUG(_warn("iset_remove_scalar(%p): deleted key '%s'", s, key));
189 36           return 1;
190              
191             } else {
192             THR_UNLOCK;
193             IF_REMOVE_DEBUG(_warn("iset_remove_scalar(%p): key '%s' not found", s, key));
194 36           return 0;
195             }
196            
197             }
198              
199 254           bool iset_includes_scalar(ISET* s, SV* sv)
200             {
201 254 100         if (s->flat && HvKEYS(s->flat)) {
    50          
    100          
202             STRLEN len;
203 247 100         char* key = SvPV(sv, len);
204 247           return hv_exists(s->flat, key, len);
205             }
206             else {
207 7           return 0;
208             }
209             }
210              
211             void _cast_magic(ISET* s, SV* sv);
212              
213 247           int iset_insert_one(ISET* s, SV* rv)
214             {
215             I32 hash, index;
216             SV* el;
217 247           int ins = 0;
218              
219 247 50         if (!SvROK(rv))
220 0           Perl_croak(aTHX_ "Tried to insert a non-reference into a Set::Object");
221              
222 247           el = SvRV(rv);
223              
224 247 100         if (!s->buckets)
225             {
226 79           Newz(0, s->bucket, 8, BUCKET);
227 79           s->buckets = 8;
228             }
229              
230 247           hash = ISET_HASH(el);
231 247           index = hash & (s->buckets - 1);
232              
233 247 100         if (insert_in_bucket(s->bucket + index, el))
234             {
235 237           ++s->elems;
236 237           ++ins;
237 237 100         if (s->is_weak) {
238             IF_DEBUG(_warn("rc of %p left as-is, casting magic", el));
239 13           _cast_magic(s, el);
240             } else {
241 224           SvREFCNT_inc(el);
242             IF_DEBUG(_warn("rc of %p bumped to %d", el, SvREFCNT(el)));
243             }
244             }
245              
246 247 100         if (s->elems > s->buckets)
247             {
248 4           int oldn = s->buckets;
249 4           int newn = oldn << 1;
250              
251             BUCKET *bucket_first, *bucket_iter, *bucket_last, *new_bucket;
252             int i;
253              
254             IF_DEBUG(_warn("Reindexing, n = %d", s->elems));
255              
256 4 50         Renew(s->bucket, newn, BUCKET);
257 4 50         Zero(s->bucket + oldn, oldn, BUCKET);
258 4           s->buckets = newn;
259              
260 4           bucket_first = s->bucket;
261 4           bucket_iter = bucket_first;
262 4           bucket_last = bucket_iter + oldn;
263              
264 52 100         for (i = 0; bucket_iter != bucket_last; ++bucket_iter, ++i)
265             {
266             SV **el_iter, **el_last, **el_out_iter;
267             I32 new_bucket_size;
268              
269 48 100         if (!bucket_iter->sv)
270 16           continue;
271              
272 32           el_iter = bucket_iter->sv;
273 32           el_last = el_iter + bucket_iter->n;
274 32           el_out_iter = el_iter;
275              
276 84 100         for (; el_iter != el_last; ++el_iter)
277             {
278 52           SV* sv = *el_iter;
279 52           I32 hash = ISET_HASH(sv);
280 52           I32 index = hash & (newn - 1);
281              
282 52 100         if (index == i)
283             {
284 17           *el_out_iter++ = *el_iter;
285 17           continue;
286             }
287              
288 35           new_bucket = bucket_first + index;
289             IF_DEBUG(_warn("%p moved from bucket %d:%p to %d:%p",
290             sv, i, bucket_iter, index, new_bucket));
291 35           insert_in_bucket(new_bucket, sv);
292             }
293            
294 32           new_bucket_size = el_out_iter - bucket_iter->sv;
295              
296 32 100         if (!new_bucket_size)
297             {
298 19           Safefree(bucket_iter->sv);
299 19           bucket_iter->sv = 0;
300 19           bucket_iter->n = 0;
301             }
302              
303 13 100         else if (new_bucket_size < bucket_iter->n)
304             {
305 4 50         Renew(bucket_iter->sv, new_bucket_size, SV*);
306 4           bucket_iter->n = new_bucket_size;
307             }
308             }
309             }
310 247           return ins;
311             }
312              
313             void _dispel_magic(ISET* s, SV* sv);
314              
315 4284           void iset_clear(ISET* s)
316             {
317 4284           BUCKET* bucket_iter = s->bucket;
318 4284           BUCKET* bucket_last = bucket_iter + s->buckets;
319              
320 4964 100         for (; bucket_iter != bucket_last; ++bucket_iter)
321             {
322             SV **el_iter, **el_last;
323              
324 680 100         if (!bucket_iter->sv)
325 484           continue;
326              
327 196           el_iter = bucket_iter->sv;
328 196           el_last = el_iter + bucket_iter->n;
329              
330 433 100         for (; el_iter != el_last; ++el_iter)
331             {
332 237 100         if (*el_iter)
333             {
334             IF_DEBUG(_warn("freeing %p, rc = %d, bucket = %p(%ld)) pos = %ld",
335             *el_iter, SvREFCNT(*el_iter),
336             bucket_iter, bucket_iter - s->bucket,
337             el_iter - bucket_iter->sv));
338              
339 210 100         if (s->is_weak) {
340             IF_SPELL_DEBUG(_warn("dispelling magic"));
341 6           _dispel_magic(s,*el_iter);
342             } else {
343             IF_SPELL_DEBUG(_warn("removing element"));
344 204           SvREFCNT_dec(*el_iter);
345             }
346 210           *el_iter = 0;
347             }
348             }
349              
350 196           Safefree(bucket_iter->sv);
351              
352 196           bucket_iter->sv = 0;
353 196           bucket_iter->n = 0;
354             }
355              
356 4284           Safefree(s->bucket);
357 4284           s->bucket = 0;
358 4284           s->buckets = 0;
359 4284           s->elems = 0;
360 4284           }
361              
362              
363             MAGIC*
364 31           _detect_magic(SV* sv) {
365 31 100         if (SvMAGICAL(sv))
366 13           return mg_find(sv, SET_OBJECT_MAGIC_backref);
367             else
368 18           return NULL;
369             }
370              
371             void
372 7           _dispel_magic(ISET* s, SV* sv) {
373 7           SV* self_svrv = s->is_weak;
374 7           MAGIC* mg = _detect_magic(sv);
375             IF_SPELL_DEBUG(_warn("dispelling magic from %p (self = %p, mg = %p)",
376             sv, self_svrv, mg));
377 7 50         if (mg) {
378 7           AV* wand = (void *)(mg->mg_obj);
379 7           SV ** const svp = AvARRAY(wand);
380 7           I32 i = AvFILLp(wand);
381 7           int c = 0;
382              
383             assert( SvTYPE(wand) == SVt_PVAV );
384              
385 14 100         while (i >= 0) {
386 7 50         if (svp[i] && SvIOK(svp[i]) && SvIV(svp[i])) {
    50          
    50          
    100          
387 1 50         ISET* o = INT2PTR(ISET*, SvIV(svp[i]));
388 1 50         if (s == o) {
389             /*
390             SPELL_DEBUG("dropping RC of %p from %d to %d",
391             svp[i], SvREFCNT(svp[i]), SvREFCNT(svp[i])-1);
392             SvREFCNT_dec(svp[i]);
393             */
394 1           svp[i] = newSViv(0);
395             } else {
396 0           c++;
397             }
398             }
399 7           i--;
400             }
401 7 50         if (!c) {
402 7           sv_unmagic(sv, SET_OBJECT_MAGIC_backref);
403 7           SvREFCNT_dec(wand);
404             }
405             }
406 7           }
407              
408             void
409 19           _fiddle_strength(ISET* s, const int strong) {
410              
411 19           BUCKET* bucket_iter = s->bucket;
412 19           BUCKET* bucket_last = bucket_iter + s->buckets;
413              
414             THR_LOCK;
415 67 100         for (; bucket_iter != bucket_last; ++bucket_iter)
416             {
417             SV **el_iter, **el_last;
418              
419 48 100         if (!bucket_iter->sv)
420 41           continue;
421              
422 7           el_iter = bucket_iter->sv;
423 7           el_last = el_iter + bucket_iter->n;
424              
425 14 100         for (; el_iter != el_last; ++el_iter)
426 7 100         if (*el_iter) {
427 6 100         if (strong) {
428             THR_UNLOCK;
429 1           _dispel_magic(s, *el_iter);
430 1           SvREFCNT_inc(*el_iter);
431             IF_DEBUG(_warn("bumped RC of %p to %d", *el_iter,
432             SvREFCNT(*el_iter)));
433             THR_LOCK;
434             }
435             else {
436             THR_UNLOCK;
437 5 50         if ( SvREFCNT(*el_iter) > 1 )
438 5           _cast_magic(s, *el_iter);
439 5           SvREFCNT_dec(*el_iter);
440             IF_DEBUG(_warn("reduced RC of %p to %d", *el_iter,
441             SvREFCNT(*el_iter)));
442             THR_LOCK;
443             }
444             }
445             }
446             THR_UNLOCK;
447 19           }
448              
449             int
450 18           _spell_effect(pTHX_ SV *sv, MAGIC *mg)
451             {
452 18           AV * const av = (AV*)mg->mg_obj;
453 18           SV ** const svp = AvARRAY(av);
454 18           I32 i = AvFILLp(av);
455              
456             IF_SPELL_DEBUG(_warn("_spell_effect (SV=%p, av_len=%d)", sv,
457             av_len(av)));
458              
459 36 100         while (i >= 0) {
460             IF_SPELL_DEBUG(_warn("_spell_effect %d", i));
461 18 50         if (svp[i] && SvIOK(svp[i]) && SvIV(svp[i])) {
    50          
    50          
    100          
462 11 50         ISET* s = INT2PTR(ISET*, SvIV(svp[i]));
463             IF_SPELL_DEBUG(_warn("_spell_effect i = %d, SV = %p", i, svp[i]));
464 11 50         if (!s->is_weak)
465 0           Perl_croak(aTHX_ "panic: set_object_magic_killbackrefs (flags=%"UVxf")",
466 0           (UV)SvFLAGS(svp[i]));
467             /* SvREFCNT_dec(svp[i]); */
468 11           svp[i] = newSViv(0);
469 11 50         if (iset_remove_one(s, sv, 1) != 1) {
470 0           _warn("Set::Object magic backref hook called on non-existent item (%p, self = %p)", sv, s->is_weak);
471             };
472             }
473 18           i--;
474             }
475 18           return 0;
476             }
477              
478             static MGVTBL SET_OBJECT_vtbl_backref =
479             {0, 0, 0, 0, MEMBER_TO_FPTR(_spell_effect)};
480              
481             void
482 18           _cast_magic(ISET* s, SV* sv) {
483 18           SV* self_svrv = s->is_weak;
484             AV* wand;
485 18           MGVTBL *vtable = &SET_OBJECT_vtbl_backref;
486             MAGIC* mg;
487             SV ** svp;
488 18           int how = SET_OBJECT_MAGIC_backref;
489             I32 i,l,free;
490              
491 18           mg = _detect_magic(sv);
492 18 50         if (mg) {
493             IF_SPELL_DEBUG(_warn("sv_magicext reusing wand %p for %p", wand, sv));
494 0           wand = (AV *)mg->mg_obj;
495             assert( SvTYPE(wand) == SVt_PVAV );
496             }
497             else {
498 18           wand=newAV();
499             IF_SPELL_DEBUG(_warn("sv_magicext(%p, %p, %d, %p, NULL, 0)", sv, wand, how, vtable));
500             #if (PERL_VERSION > 7) || ( (PERL_VERSION == 7)&&( PERL_SUBVERSION > 2) )
501 18           mg = sv_magicext(sv, (SV *)wand, how, vtable, NULL, 0);
502             #else
503             sv_magic(sv, wand, how, NULL, 0);
504             mg = mg_find(sv, SET_OBJECT_MAGIC_backref);
505             mg->mg_virtual = &SET_OBJECT_vtbl_backref;
506             #endif
507 18           mg->mg_flags |= MGf_REFCOUNTED;
508 18           SvRMAGICAL_on(sv);
509             }
510              
511 18           svp = AvARRAY(wand);
512 18           i = AvFILLp(wand);
513 18           free = -1;
514              
515 18 50         while (i >= 0) {
516 0 0         if (svp[i] && SvIV(svp[i])) {
    0          
    0          
    0          
517 0 0         ISET* o = INT2PTR(ISET*, SvIV(svp[i]));
518 0 0         if (s == o)
519 0           return;
520             } else {
521 0 0         if ( svp[i] ) SvREFCNT_dec(svp[i]);
522 0           svp[i] = NULL;
523 0           free = i;
524             }
525 0           i = i - 1;
526             }
527              
528 18 50         if (free == -1) {
529             IF_SPELL_DEBUG(_warn("casting self %p with av_push to the end", self_svrv));
530 18           av_push(wand, self_svrv);
531             } else {
532             IF_SPELL_DEBUG(_warn("casting self %p to slot %d", self_svrv, free));
533 0           svp[free] = self_svrv;
534             }
535              
536             /*
537             SvREFCNT_inc(self_svrv);
538             */
539             }
540              
541             int
542 70           iset_remove_one(ISET* s, SV* el, int spell_in_progress)
543             {
544             SV *referant;
545             I32 hash, index;
546             SV **el_iter, **el_last, **el_out_iter;
547             BUCKET* bucket;
548              
549             IF_DEBUG(_warn("removing scalar %p from set %p", el, s));
550              
551             /* note an object being destroyed is not SvOK */
552 70 100         if (!spell_in_progress && !SvOK(el))
    100          
    50          
    50          
553 2           return 0;
554              
555 68 100         if (SvOK(el) && !SvROK(el)) {
    50          
    50          
    100          
556             IF_DEBUG(_warn("scalar is not a ref (flags = 0x%x)", SvFLAGS(el)));
557 37 100         if (s->flat && HvKEYS(s->flat)) {
    50          
    50          
558             IF_DEBUG(_warn("calling remove_scalar for %p", el));
559 36 50         if (iset_remove_scalar(s, el))
560 36           return 1;
561             }
562 1           return 0;
563             }
564              
565 31 100         referant = (spell_in_progress ? el : SvRV(el));
566 31           hash = ISET_HASH(referant);
567 31           index = hash & (s->buckets - 1);
568 31           bucket = s->bucket + index;
569              
570 31 100         if (s->buckets == 0)
571 1           return 0;
572              
573 30 100         if (!bucket->sv)
574 2           return 0;
575              
576 28           el_iter = bucket->sv;
577 28           el_out_iter = el_iter;
578 28           el_last = el_iter + bucket->n;
579             IF_DEBUG(_warn("remove: el_last = %p, el_iter = %p", el_last, el_iter));
580              
581             THR_LOCK;
582 29 100         for (; el_iter != el_last; ++el_iter) {
583 28 100         if (*el_iter == referant) {
584 27 100         if (s->is_weak) {
585             THR_UNLOCK;
586 11 50         if (!spell_in_progress) {
587             IF_SPELL_DEBUG(_warn("Removing ST(%p) magic", referant));
588 0           _dispel_magic(s,referant);
589             } else {
590             IF_SPELL_DEBUG(_warn("Not removing ST(%p) magic (spell in progress)", referant));
591             }
592             THR_LOCK;
593             } else {
594             THR_UNLOCK;
595             IF_SPELL_DEBUG(_warn("Not removing ST(%p) magic from Muggle", referant));
596             THR_LOCK;
597 16           SvREFCNT_dec(referant);
598             }
599 27           *el_iter = 0;
600 27           --s->elems;
601             THR_UNLOCK;
602 27           return 1;
603             }
604             else {
605             THR_UNLOCK;
606             IF_SPELL_DEBUG(_warn("ST(%p) != %p", referant, *el_iter));
607             THR_LOCK;
608             }
609             }
610             THR_UNLOCK;
611 1           return 0;
612             }
613            
614             MODULE = Set::Object PACKAGE = Set::Object
615              
616             PROTOTYPES: DISABLE
617              
618             void
619             new(pkg, ...)
620             SV* pkg;
621              
622             PPCODE:
623              
624             {
625             SV* self;
626             ISET* s;
627             I32 item;
628             SV* isv;
629            
630 2267           New(0, s, 1, ISET);
631 2267           s->elems = 0;
632 2267           s->buckets = 0;
633 2267           s->bucket = NULL;
634 2267           s->flat = Nullhv;
635 2267           s->is_weak = Nullsv;
636              
637 2267           isv = newSViv( PTR2IV(s) );
638 2267           sv_2mortal(isv);
639              
640 2267           self = newRV_inc(isv);
641 2267           sv_2mortal(self);
642              
643 2267           sv_bless(self, gv_stashsv(pkg, FALSE));
644              
645 3012 100         for (item = 1; item < items; ++item) {
646 745           SV* el = ST(item);
647 745 100         SvGETMAGIC(el);
    50          
648 745 100         ISET_INSERT(s, el);
649             }
650              
651             IF_DEBUG(_warn("set!"));
652              
653 2267           PUSHs(self);
654 2267           XSRETURN(1);
655             }
656              
657             void
658             insert(self, ...)
659             SV* self;
660              
661             PPCODE:
662 58 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
663             I32 item;
664 58           int inserted = 0;
665              
666 164 100         for (item = 1; item < items; ++item)
667             {
668 106           SV* el = ST(item);
669 106 50         if ((SV*)s == el) {
670 0           _warn("INSERTING SET UP OWN ARSE");
671             }
672 106 50         SvGETMAGIC(el);
    0          
673 106 100         if ISET_INSERT(s, el)
    100          
674 87           inserted++;
675             IF_DEBUG(_warn("inserting %p %p size = %d", el, SvRV(el), s->elems));
676             }
677              
678 58           XSRETURN_IV(inserted);
679            
680             void
681             remove(self, ...)
682             SV* self;
683              
684             PPCODE:
685              
686 37 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
687             I32 hash, index, item;
688             SV **el_iter, **el_last, **el_out_iter;
689             BUCKET* bucket;
690 37           int removed = 0;
691              
692 96 100         for (item = 1; item < items; ++item)
693             {
694 59           SV* el = ST(item);
695 59 50         SvGETMAGIC(el);
    0          
696 59           removed += iset_remove_one(s, el, 0);
697             }
698 37           XSRETURN_IV(removed);
699              
700             int
701             is_null(self)
702             SV* self;
703              
704             CODE:
705 15 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
706 15 50         if (s->elems)
707 0           XSRETURN_UNDEF;
708 15 100         if (s->flat) {
709 13 50         if (HvKEYS(s->flat)) {
    100          
710 11           XSRETURN_UNDEF;
711             }
712             }
713 4           RETVAL = 1;
714              
715             OUTPUT: RETVAL
716              
717             int
718             size(self)
719             SV* self;
720              
721             CODE:
722 170 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
723 170 100         RETVAL = s->elems + (s->flat ? HvKEYS(s->flat) : 0);
    50          
724              
725             OUTPUT: RETVAL
726              
727             int
728             rc(self)
729             SV* self;
730              
731             CODE:
732 0           RETVAL = SvREFCNT(self);
733              
734             OUTPUT: RETVAL
735              
736             int
737             rvrc(self)
738             SV* self;
739              
740             CODE:
741            
742 0 0         if (SvROK(self)) {
743 0           RETVAL = SvREFCNT(SvRV(self));
744             } else {
745 0           XSRETURN_UNDEF;
746             }
747              
748             OUTPUT: RETVAL
749              
750             void
751             includes(self, ...)
752             SV* self;
753              
754             PPCODE:
755              
756 384 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
757             I32 hash, index, item;
758             SV **el_iter, **el_last;
759             BUCKET* bucket;
760              
761 585 100         for (item = 1; item < items; ++item)
762             {
763 397           SV* el = ST(item);
764             SV* rv;
765              
766 397 100         if (!SvOK(el))
    50          
    50          
767 2           XSRETURN_NO;
768              
769 395 50         SvGETMAGIC(el);
    0          
770 395 100         if (!SvROK(el)) {
771             IF_DEBUG(_warn("includes! el = %s", SvPV_nolen(el)));
772 254 100         if (!iset_includes_scalar(s, el))
773 133           XSRETURN_NO;
774 121           goto next;
775             }
776              
777 141           rv = SvRV(el);
778              
779 141 100         if (!s->buckets)
780 41           XSRETURN_NO;
781              
782 100           hash = ISET_HASH(rv);
783 100           index = hash & (s->buckets - 1);
784 100           bucket = s->bucket + index;
785              
786             IF_DEBUG(_warn("includes: looking for %p in bucket %d:%p",
787             rv, index, bucket));
788              
789 100 100         if (!bucket->sv)
790 20           XSRETURN_NO;
791              
792 80           el_iter = bucket->sv;
793 80           el_last = el_iter + bucket->n;
794              
795 82 50         for (; el_iter != el_last; ++el_iter)
796 82 100         if (*el_iter == rv)
797 80           goto next;
798            
799 0           XSRETURN_NO;
800              
801             next: ;
802             }
803              
804 188           XSRETURN_YES;
805              
806              
807             void
808             members(self)
809             SV* self
810            
811             PPCODE:
812              
813 2422 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
814 2422           BUCKET* bucket_iter = s->bucket;
815 2422           BUCKET* bucket_last = bucket_iter + s->buckets;
816              
817 2422 100         EXTEND(sp, s->elems + (s->flat ? HvKEYS(s->flat) : 0) );
    50          
    100          
    50          
    50          
    0          
    0          
818              
819 3110 100         for (; bucket_iter != bucket_last; ++bucket_iter)
820             {
821             SV **el_iter, **el_last;
822              
823 688 100         if (!bucket_iter->sv)
824 493           continue;
825              
826 195           el_iter = bucket_iter->sv;
827 195           el_last = el_iter + bucket_iter->n;
828              
829 412 100         for (; el_iter != el_last; ++el_iter)
830             {
831 217 100         if (*el_iter) {
832 212           SV* el = newRV(*el_iter);
833 212 100         if (SvOBJECT(*el_iter)) {
834 204           sv_bless(el, SvSTASH(*el_iter));
835             }
836 212           PUSHs(sv_2mortal(el));
837             }
838             }
839             }
840              
841 2422 100         if (s->flat) {
842 300           int i = 0, num = hv_iterinit(s->flat);
843              
844 1298 100         while (i++ < num) {
845 998           HE* he = hv_iternext(s->flat);
846              
847 998 50         PUSHs(HeSVKEY_force(he));
    50          
    50          
848             }
849             }
850              
851             void
852             clear(self)
853             SV* self
854              
855             CODE:
856 12 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
857              
858 12           iset_clear(s);
859 12 100         if (s->flat) {
860 9           hv_clear(s->flat);
861             IF_REMOVE_DEBUG(_warn("iset_clear(%p): cleared", s));
862             }
863            
864             void
865             DESTROY(self)
866             SV* self
867              
868             CODE:
869 4275 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
870 4275 100         if ( s ) {
871 4272           sv_setiv(SvRV(self), 0);
872             IF_DEBUG(_warn("DESTROY s"));
873 4272           iset_clear(s);
874 4272 100         if (s->flat) {
875 160           hv_undef(s->flat);
876 160           SvREFCNT_dec(s->flat);
877             }
878 4272           Safefree(s);
879             }
880            
881             int
882             is_weak(self)
883             SV* self
884              
885             CODE:
886 2005 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
887              
888 2005           RETVAL = !!s->is_weak;
889              
890             OUTPUT: RETVAL
891              
892             void
893             _weaken(self)
894             SV* self
895              
896             CODE:
897 19 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
898              
899 19 100         if (s->is_weak)
900 1           XSRETURN_UNDEF;
901              
902             IF_DEBUG(_warn("weakening set (%p)", SvRV(self)));
903              
904 18           s->is_weak = SvRV(self);
905              
906 18           _fiddle_strength(s, 0);
907              
908             void
909             _strengthen(self)
910             SV* self
911              
912             CODE:
913 1 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(self)));
914              
915 1 50         if (!s->is_weak)
916 0           XSRETURN_UNDEF;
917              
918             IF_DEBUG(_warn("strengthening set (%p)", SvRV(self)));
919              
920 1           _fiddle_strength(s, 1);
921              
922 1           s->is_weak = 0;
923              
924             /* Here are some functions from Scalar::Util; they are so simple,
925             that it isn't worth making a dependancy on that module. */
926              
927             int
928             is_int(sv)
929             SV *sv
930             PROTOTYPE: $
931             CODE:
932 22 50         SvGETMAGIC(sv);
    0          
933 22 100         if ( !SvIOKp(sv) )
934 15           XSRETURN_UNDEF;
935              
936 7           RETVAL = 1;
937             OUTPUT:
938             RETVAL
939              
940             int
941             is_string(sv)
942             SV *sv
943             PROTOTYPE: $
944             CODE:
945 9 50         SvGETMAGIC(sv);
    0          
946 9 100         if ( !SvPOKp(sv) )
947 5           XSRETURN_UNDEF;
948              
949 4           RETVAL = 1;
950             OUTPUT:
951             RETVAL
952              
953             int
954             is_double(sv)
955             SV *sv
956             PROTOTYPE: $
957             CODE:
958 14 50         SvGETMAGIC(sv);
    0          
959 14 100         if ( !SvNOKp(sv) )
960 5           XSRETURN_UNDEF;
961              
962 9           RETVAL = 1;
963             OUTPUT:
964             RETVAL
965              
966             void
967             get_magic(sv)
968             SV *sv
969             PROTOTYPE: $
970             CODE:
971             MAGIC* mg;
972             SV* magic;
973 6 50         if (! SvROK(sv)) {
974 0           _warn("tried to get magic from non-reference");
975 0           XSRETURN_UNDEF;
976             }
977              
978 6 100         if (! (mg = _detect_magic(SvRV(sv))) )
979 3           XSRETURN_UNDEF;
980              
981             IF_SPELL_DEBUG(_warn("found magic on %p - %p", sv, mg));
982             IF_SPELL_DEBUG(_warn("mg_obj = %p", mg->mg_obj));
983              
984             /*magic = newSV(0);
985             SvRV(magic) = mg->mg_obj;
986             SvROK_on(magic); */
987 3           POPs;
988 3           magic = newRV_inc(mg->mg_obj);
989 3           PUSHs(magic);
990 3           XSRETURN(1);
991              
992             SV*
993             get_flat(sv)
994             SV* sv
995             PROTOTYPE: $
996             CODE:
997 2 50         ISET* s = INT2PTR(ISET*, SvIV(SvRV(sv)));
998 2 100         if (s->flat) {
999 1           RETVAL = newRV_inc((SV *)s->flat);
1000             } else {
1001 1           XSRETURN_UNDEF;
1002             }
1003             OUTPUT:
1004             RETVAL
1005              
1006             const char *
1007             blessed(sv)
1008             SV * sv
1009             PROTOTYPE: $
1010             CODE:
1011             {
1012 12 50         if (SvMAGICAL(sv))
1013 0           mg_get(sv);
1014 12 100         if(!sv_isobject(sv)) {
1015 2           XSRETURN_UNDEF;
1016             }
1017 10           RETVAL = sv_reftype(SvRV(sv),TRUE);
1018             }
1019             OUTPUT:
1020             RETVAL
1021              
1022             const char *
1023             reftype(sv)
1024             SV * sv
1025             PROTOTYPE: $
1026             CODE:
1027             {
1028 6 50         if (SvMAGICAL(sv))
1029 0           mg_get(sv);
1030 6 50         if(!SvROK(sv)) {
1031 0           XSRETURN_UNDEF;
1032             }
1033 6           RETVAL = sv_reftype(SvRV(sv),FALSE);
1034             }
1035             OUTPUT:
1036             RETVAL
1037              
1038             UV
1039             refaddr(sv)
1040             SV * sv
1041             PROTOTYPE: $
1042             CODE:
1043             {
1044 5 50         if(SvROK(sv)) {
1045 5           RETVAL = PTR2UV(SvRV(sv));
1046             } else {
1047 0           RETVAL = 0;
1048             }
1049             }
1050             OUTPUT:
1051             RETVAL
1052              
1053              
1054             int
1055             _ish_int(sv)
1056             SV *sv
1057             PROTOTYPE: $
1058             CODE:
1059             double dutch;
1060             int innit;
1061             STRLEN lp;
1062             SV * MH;
1063             /* This function returns the integer value of a passed scalar, as
1064             long as the scalar can reasonably considered to already be a
1065             representation of an integer. This means if you want strings to
1066             be interpreted as integers, you're going to have to add 0 to
1067             them. */
1068              
1069 26 100         if (SvMAGICAL(sv)) {
1070             /* probably a tied scalar */
1071 1           Perl_croak(aTHX_ "Tied variables not supported");
1072             }
1073              
1074 25 100         if (SvAMAGIC(sv)) {
    50          
    50          
1075             /* an overloaded variable. need to actually call a function to
1076             get its value. */
1077 3           Perl_croak(aTHX_ "Overloaded variables not supported");
1078             }
1079              
1080 22 100         if (SvNIOKp(sv)) {
1081             /* NOK - the scalar is a double */
1082              
1083 16 100         if (SvPOKp(sv)) {
1084             /* POK - the scalar is also a string. */
1085              
1086             /* we have to be careful; a scalar "2am" or, even worse, "2e6"
1087             may satisfy this condition if it has been evaluated in
1088             numeric context. Remember, we are testing that the value
1089             could already be considered an _integer_, and AFAIC 2e6 and
1090             2.0 are floats, end of story. */
1091              
1092             /* So, we stringify the numeric part of the passed SV, turn off
1093             the NOK bit on the scalar, so as to perform a string
1094             comparison against the passed in value. If it is not the
1095             same, then we almost certainly weren't given an integer. */
1096              
1097 7 100         if (SvIOKp(sv)) {
1098 5 100         MH = newSViv(SvIV(sv));
1099 2 50         } else if (SvNOKp(sv)) {
1100 2 50         MH = newSVnv(SvNV(sv));
1101             }
1102 7           sv_2pv(MH, &lp);
1103 7           SvPOK_only(MH);
1104              
1105 7 100         if (sv_cmp(MH, sv) != 0) {
1106 10           XSRETURN_UNDEF;
1107             }
1108             }
1109              
1110 13 100         if (SvNOKp(sv)) {
1111             /* How annoying - it's a double */
1112 9 50         dutch = SvNV(sv);
1113 9 100         if (SvIOKp(sv)) {
1114 1 50         innit = SvIV(sv);
1115             } else {
1116 8           innit = (int)dutch;
1117             }
1118 9 100         if (dutch - innit < (0.000000001)) {
1119 8           RETVAL = innit;
1120             } else {
1121 1           XSRETURN_UNDEF;
1122             }
1123 4 50         } else if (SvIOKp(sv)) {
1124 12 50         RETVAL = SvIV(sv);
1125             }
1126             } else {
1127 6           XSRETURN_UNDEF;
1128             }
1129             OUTPUT:
1130             RETVAL
1131              
1132             int
1133             is_overloaded(sv)
1134             SV *sv
1135             PROTOTYPE: $
1136             CODE:
1137 14 50         SvGETMAGIC(sv);
    0          
1138 14 100         if ( !SvAMAGIC(sv) )
    100          
    50          
1139 9           XSRETURN_UNDEF;
1140 5           RETVAL = 1;
1141             OUTPUT:
1142             RETVAL
1143              
1144             int
1145             is_object(sv)
1146             SV *sv
1147             PROTOTYPE: $
1148             CODE:
1149 0 0         SvGETMAGIC(sv);
    0          
1150 0 0         if ( !SvOBJECT(sv) )
1151 0           XSRETURN_UNDEF;
1152 0           RETVAL = 1;
1153             OUTPUT:
1154             RETVAL
1155              
1156             void
1157             _STORABLE_thaw(obj, cloning, serialized, ...)
1158             SV* obj;
1159              
1160             PPCODE:
1161              
1162             {
1163             ISET* s;
1164             I32 item;
1165             SV* isv;
1166            
1167 2005           New(0, s, 1, ISET);
1168 2005           s->elems = 0;
1169 2005           s->bucket = 0;
1170 2005           s->buckets = 0;
1171 2005           s->flat = NULL;
1172 2005           s->is_weak = 0;
1173              
1174 2005 50         if (!SvROK(obj)) {
1175 0           Perl_croak(aTHX_ "Set::Object::STORABLE_thaw passed a non-reference");
1176             }
1177              
1178             /* FIXME - some random segfaults with 5.6.1, Storable 2.07,
1179             freezing closures, and back-references to
1180             overloaded objects. One day I might even
1181             understand why :-)
1182              
1183             Bug in Storable... that's why. old news.
1184             */
1185 2005           isv = SvRV(obj);
1186 2005           SvIV_set(isv, PTR2IV(s) );
1187 2005           SvIOK_on(isv);
1188              
1189 2018 100         for (item = 3; item < items; ++item)
1190             {
1191 13           SV* el = ST(item);
1192 13 50         SvGETMAGIC(el);
    0          
1193 13 100         ISET_INSERT(s, el);
1194             }
1195              
1196             IF_DEBUG(_warn("set!"));
1197              
1198 2005           PUSHs(obj);
1199 2005           XSRETURN(1);
1200             }
1201              
1202             BOOT:
1203             {
1204             #ifdef USE_ITHREADS
1205             MY_CXT_INIT;
1206             MY_CXT.s = NULL;
1207             MUTEX_INIT(&iset_mutex);
1208             #endif
1209             }
1210              
1211             #ifdef USE_ITHREADS
1212              
1213             void
1214             CLONE(...)
1215             PROTOTYPE: DISABLE
1216             PREINIT:
1217             ISET *old_s;
1218             PPCODE:
1219             {
1220             dMY_CXT;
1221             old_s = MY_CXT.s;
1222             }
1223             {
1224             MY_CXT_CLONE;
1225             MY_CXT.s = old_s;
1226             }
1227             XSRETURN(0);
1228              
1229             #endif