File Coverage

Object.xs
Criterion Covered Total %
statement 388 427 90.8
branch 245 314 78.0
condition n/a
subroutine n/a
pod n/a
total 633 741 85.4


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