File Coverage

lib/Doubly/Linked.xs
Criterion Covered Total %
statement 221 246 89.8
branch 76 114 66.6
condition n/a
subroutine n/a
pod n/a
total 297 360 82.5


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT // we'll define thread context if necessary (faster)
2             #include "EXTERN.h" // globals/constant import locations
3             #include "perl.h" // Perl symbols, structures and constants definition
4             #include "XSUB.h" // xsubpp functions and macros
5              
6 1890           int _defined (HV * hash, char * key, int len) {
7             dTHX;
8 1890           SV * val = *hv_fetch(hash, key, len, 0);
9 1890           return SvOK(val) ? 1 : 0;
10             }
11              
12 1794           int _is_undef (SV * self) {
13             dTHX;
14 1794           HV * hash = (HV*)SvRV(self);
15              
16 1794 100         if (SvOK(*hv_fetch(hash, "data", 4, 0))) {
17 1020           return 0;
18             }
19              
20 774 50         if (SvOK(*hv_fetch(hash, "prev", 4, 0))) {
21 0           return 0;
22             }
23              
24 774 50         if (SvOK(*hv_fetch(hash, "next", 4, 0))) {
25 0           return 0;
26             }
27              
28 774           return 1;
29             }
30              
31 470           SV * _set_data(SV * self, SV * data) {
32             dTHX;
33 470           HV * hash = (HV*)SvRV(self);
34 470           hv_store(hash, "data", 4, newSVsv(data), 0);
35 470           return newSVsv(self);
36             }
37              
38 907           SV * _new (SV * pkg, SV * data) {
39             dTHX;
40 907           HV * hash = newHV();
41 907 100         if (SvTYPE(pkg) != SVt_PV) {
42 335 50         char * name = HvNAME(SvSTASH(SvRV(pkg)));
    50          
    50          
    0          
    50          
    50          
43 335           pkg = newSVpv(name, strlen(name));
44             }
45 907           SV * undef = &PL_sv_undef;
46 907           hv_store(hash, "data", 4, data, 0);
47 907           hv_store(hash, "next", 4, undef, 0);
48 907           hv_store(hash, "prev", 4, undef, 0);
49 907           return sv_bless(newRV_noinc((SV*)hash), gv_stashsv(pkg, 0));
50             }
51              
52 680           SV * _goto_start ( SV * self ) {
53             dTHX;
54 680           HV * hash = (HV*)SvRV(self);
55              
56 898 100         while (_defined(hash, "prev", 4)) {
57 218           self = *hv_fetch(hash, "prev", 4, 0);
58 218           hash = (HV*)SvRV(self);
59             }
60              
61 680           return self;
62             }
63              
64              
65 4           SV * _is_start ( SV * self ) {
66             dTHX;
67 4           HV * hash = (HV*)SvRV(self);
68              
69 4 50         if (_defined(hash, "prev", 4)) {
70 0           return newSViv(0);
71             }
72 4           return newSViv(1);
73             }
74              
75 122           SV * _goto_end ( SV * self ) {
76             dTHX;
77 122           HV * hash = (HV*)SvRV(self);
78              
79 129 100         while (_defined(hash, "next", 4)) {
80 7           self = *hv_fetch(hash, "next", 4, 0);
81 7           hash = (HV*)SvRV(self);
82             }
83              
84 122           return self;
85             }
86              
87 2           SV * _is_end ( SV * self ) {
88             dTHX;
89 2           HV * hash = (HV*)SvRV(self);
90              
91 2 50         if (_defined(hash, "next", 4)) {
92 0           return newSViv(0);
93             }
94 2           return newSViv(1);
95             }
96              
97 0           SV * _length (SV * self) {
98             dTHX;
99 0           self = _goto_start(self);
100 0           HV * hash = (HV*)SvRV(self);
101              
102 0 0         int len = _defined(hash, "next", 4) ? 1 : _defined(hash, "data", 4) ? 1 : 0;;
    0          
103              
104 0 0         while (_defined(hash, "next", 4)) {
105 0           self = *hv_fetch(hash, "next", 4, 0);
106 0           hash = (HV*)SvRV(self);
107 0           len++;
108             }
109              
110 0           return newSViv(len);
111             }
112              
113 3           SV * _find ( SV * self, SV * cb ) {
114             dTHX;
115              
116 3           self = _goto_start(self);
117 3           HV * hash = (HV*)SvRV(self);
118 3           SV * data = *hv_fetch(hash, "data", 4, 0);
119              
120 3           dSP;
121 3 50         PUSHMARK(SP);
122 3 50         XPUSHs(data);
123 3           PUTBACK;
124 3           call_sv(cb, G_SCALAR);
125 3 100         if (SvTRUEx(*PL_stack_sp)) {
126 2           return newSVsv(self);
127             }
128 1           POPs;
129              
130 1 50         while (_defined(hash, "next", 4)) {
131 1           self = *hv_fetch(hash, "next", 4, 0);
132 1           hash = (HV*)SvRV(self);
133 1           data = *hv_fetch(hash, "data", 4, 0);
134 1           dSP;
135 1 50         PUSHMARK(SP);
136 1 50         XPUSHs(data);
137 1           PUTBACK;
138 1           call_sv(cb, G_SCALAR);
139 1 50         if (SvTRUEx(*PL_stack_sp)) {
140 1           return newSVsv(self);
141             }
142 0           POPs;
143             }
144              
145 0           return &PL_sv_undef;
146             }
147              
148 5           SV * _insert_before (SV * self, SV * data) {
149             dTHX;
150 5 100         if (_is_undef(self)) {
151 1           return _set_data(self, data);
152             }
153              
154 4           HV * hash = (HV*)SvRV(self);
155              
156 4           SV * node = _new(self, data);
157 4           HV * hash_node = (HV*)SvRV(node);
158              
159 4           hv_store(hash_node, "next", 4, newSVsv(self), 0);
160              
161 4           SV * prev = newSVsv(*hv_fetch(hash, "prev", 4, 0));
162              
163 4 50         if (SvOK(prev) && SvROK(prev)) {
    0          
164 0           hv_store((HV*)SvRV(prev), "next", 4, newSVsv(node), 0);
165             }
166              
167 4           hv_store(hash_node, "prev", 4, prev, 0);
168              
169 4           hv_store(hash, "prev", 4, newSVsv(node), 0);
170              
171 4           return node;
172             }
173              
174 114           SV * _insert_after (SV * self, SV * data) {
175             dTHX;
176 114 100         if (_is_undef(self)) {
177 1           return _set_data(self, data);
178             }
179              
180 113           HV * hash = (HV*)SvRV(self);
181              
182 113           SV * node = _new(self, data);
183 113           HV * hash_node = (HV*)SvRV(node);
184              
185 113           hv_store(hash_node, "prev", 4, newSVsv(self), 0);
186              
187 113           SV * next = newSVsv(*hv_fetch(hash, "next", 4, 0));
188              
189 113 50         if (SvOK(next) && SvROK(next)) {
    0          
190 0           hv_store((HV*)SvRV(next), "prev", 4, newSVsv(node), 0);
191             }
192              
193 113           hv_store(hash_node, "next", 4, next, 0);
194              
195 113           hv_store(hash, "next", 4, newSVsv(node), 0);
196              
197 113           return node;
198             }
199              
200 3           SV * _insert_find (SV * self, SV * cb, SV * data) {
201             dTHX;
202 3 100         if (_is_undef(self)) {
203 1           return _set_data(self, data);
204             }
205              
206 2           self = _find(self, cb);
207              
208 2 50         if (!SvOK(self)) {
209 0           croak("No match found for insert cb");
210             }
211              
212 2           return _insert_before(self, data);
213             }
214              
215 103           SV * _insert_at_pos (SV * self, int pos, SV * data) {
216             dTHX;
217 103 100         if (_is_undef(self)) {
218 1           return _set_data(self, data);
219             }
220              
221 102           self = _goto_start(self);
222 102           HV * hash = (HV*)SvRV(self);
223              
224 102           int i = 0;
225 305 100         for (i = 0; i < pos; i++) {
226 203 100         if (_defined(hash, "next", 4)) {
227 201           self = *hv_fetch(hash, "next", 4, 0);
228 201           hash = (HV*)SvRV(self);
229             }
230             }
231              
232 102           return _insert_after(self, data);
233             }
234              
235 114           SV * _insert_at_start (SV * self, SV * data) {
236             dTHX;
237 114 100         if (_is_undef(self)) {
238 12           return _set_data(self, data);
239             }
240              
241 102           self = _goto_start(self);
242 102           HV * hash = (HV*)SvRV(self);
243              
244 102           SV * node = _new(self, data);
245 102           HV * hash_node = (HV*)SvRV(node);
246              
247 102           hv_store(hash_node, "next", 4, newSVsv(self), 0);
248 102           hv_store(hash, "prev", 4, newSVsv(node), 0);
249              
250 102           return node;
251             }
252              
253 570           SV * _insert_at_end (SV * self, SV * data) {
254             dTHX;
255 570 100         if (_is_undef(self)) {
256 454           return _set_data(self, data);
257             }
258              
259 116           self = _goto_end(self);
260 116           HV * hash = (HV*)SvRV(self);
261              
262 116           SV * node = _new(self, data);
263 116           HV * hash_node = (HV*)SvRV(node);
264              
265 116           hv_store(hash_node, "prev", 4, newSVsv(self), 0);
266 116           hv_store(hash, "next", 4, newSVsv(node), 0);
267              
268 116           return node;
269             }
270              
271 121           SV * _remove (SV * self) {
272             dTHX;
273              
274 121 100         if (_is_undef(self)) {
275 2           return &PL_sv_undef;
276             }
277              
278 119           HV * hash = (HV*)SvRV(self);
279 119           SV * undef = &PL_sv_undef;
280 119           SV * prev = *hv_fetch(hash, "prev", 4, 0);
281 119           SV * next = *hv_fetch(hash, "next", 4, 0);
282 119           SV * data = newSVsv(*hv_fetch(hash, "data", 4, 0));
283              
284 119 100         if (SvOK(prev) || SvOK(next)) {
    100          
285 12 100         if (SvOK(prev)) {
286 6           HV * previous = (HV*)SvRV(prev);
287 6 100         if (SvROK(next)) {
288 1           sv_setsv(self, next);
289 1           HV * nexting = (HV*)SvRV(next);
290 1           hv_store(previous, "next", 4, newSVsv(next), 0);
291 1           hv_store(nexting, "prev", 4, newSVsv(prev), 0);
292             } else {
293 5           sv_setsv(self, prev);
294 5           hv_store(previous, "next", 4, undef, 0);
295             }
296 6 50         } else if (SvOK(next)) {
297 6           sv_setsv(self, next);
298 6           HV * nexting = (HV*)SvRV(next);
299 6           hv_store(nexting, "prev", 4, undef, 0);
300             }
301             } else {
302 107           hv_store(hash, "data", 4, undef, 0);
303             }
304              
305 119           return data;
306             }
307              
308 107           SV * _remove_from_start(SV * self) {
309             dTHX;
310 107 100         if (_is_undef(self)) {
311 1           return &PL_sv_undef;
312             }
313              
314 106           self = _goto_start(self);
315              
316 106           return _remove(self);
317             }
318              
319 103           SV * _remove_from_end(SV * self) {
320             dTHX;
321              
322 103 100         if (_is_undef(self)) {
323 100           return &PL_sv_undef;
324             }
325              
326 3           self = _goto_end(self);
327              
328 3           return _remove(self);
329             }
330              
331 4           SV * _remove_from_pos (SV * self, int pos) {
332             dTHX;
333              
334 4 100         if (_is_undef(self)) {
335 1           return &PL_sv_undef;
336             }
337              
338 3           self = _goto_start(self);
339 3           HV * hash = (HV*)SvRV(self);
340              
341 3           int i = 0;
342 6 100         for (i = 0; i < pos; i++) {
343 3 50         if (_defined(hash, "next", 4)) {
344 3           self = *hv_fetch(hash, "next", 4, 0);
345 3           hash = (HV*)SvRV(self);
346             }
347             }
348              
349 3           return _remove(self);
350             }
351              
352 550           void _destroy (SV * self) {
353             dTHX;
354              
355 550 100         if (_is_undef(self)) {
356 200           return;
357             }
358              
359             /* Collect all nodes first, incrementing refcounts to keep them alive */
360 350           AV * nodes = newAV();
361 350           self = _goto_start(self);
362 350           HV * hash = (HV*)SvRV(self);
363              
364 350           av_push(nodes, SvREFCNT_inc(self));
365 650 100         while (_defined(hash, "next", 4)) {
366 300           self = *hv_fetch(hash, "next", 4, 0);
367 300           av_push(nodes, SvREFCNT_inc(self));
368 300           hash = (HV*)SvRV(self);
369             }
370              
371             /* Now clear all links in all nodes */
372             int i;
373 1000 100         for (i = 0; i <= av_len(nodes); i++) {
374 650           SV ** node_ptr = av_fetch(nodes, i, 0);
375 650 50         if (node_ptr && SvROK(*node_ptr)) {
    50          
376 650           HV * h = (HV*)SvRV(*node_ptr);
377 650           hv_store(h, "prev", 4, newSV(0), 0);
378 650           hv_store(h, "next", 4, newSV(0), 0);
379 650           hv_store(h, "data", 4, newSV(0), 0);
380             }
381             }
382              
383             /* Free the nodes array (and decrement all the refcounts we incremented) */
384 350           SvREFCNT_dec((SV*)nodes);
385             }
386              
387              
388             MODULE = Doubly::Linked PACKAGE = Doubly::Linked
389             PROTOTYPES: DISABLE
390              
391             SV *
392             new(...)
393             CODE:
394 572 100         RETVAL = _new(ST(0), items > 1 ? newSVsv(ST(1)) : &PL_sv_undef);
395             OUTPUT:
396             RETVAL
397              
398             SV *
399             length(self)
400             SV * self
401             CODE:
402 0           RETVAL = _length(self);
403             OUTPUT:
404             RETVAL
405              
406             SV *
407             data(self, ...)
408             SV * self
409             CODE:
410 53           HV * hash = (HV*)SvRV(self);
411 53 100         if (items > 1) {
412 4           hv_store(hash, "data", 4, newSVsv(ST(1)), 0);
413             }
414 53           SV * data = newSVsv(*hv_fetch(hash, "data", 4, 0));
415 53           RETVAL = data;
416             OUTPUT:
417             RETVAL
418              
419             SV *
420             start(self)
421             SV * self
422             CODE:
423 14           RETVAL = newSVsv(_goto_start(self));
424             OUTPUT:
425             RETVAL
426              
427             SV *
428             is_start(self)
429             SV * self
430             CODE:
431 4           RETVAL = _is_start(self);
432             OUTPUT:
433             RETVAL
434              
435             SV *
436             end(self)
437             SV * self
438             CODE:
439 3           RETVAL = newSVsv(_goto_end(self));
440             OUTPUT:
441             RETVAL
442              
443             SV *
444             is_end(self)
445             SV * self
446             CODE:
447 2           RETVAL = _is_end(self);
448             OUTPUT:
449             RETVAL
450              
451             SV *
452             next(self)
453             SV * self
454             CODE:
455 34           HV * hash = (HV*)SvRV(self);
456 34           SV * next = newSVsv(*hv_fetch(hash, "next", 4, 0));
457 34           RETVAL = next;
458             OUTPUT:
459             RETVAL
460              
461             SV *
462             prev(self)
463             SV * self
464             CODE:
465 6           HV * hash = (HV*)SvRV(self);
466 6           SV * prev = newSVsv(*hv_fetch(hash, "prev", 4, 0));
467 6           RETVAL = prev;
468             OUTPUT:
469             RETVAL
470              
471             SV *
472             bulk_add(self, ...)
473             SV * self
474             CODE:
475 0           self = _goto_end(self);
476 0 0         if (items > 1) {
477 0           int i = 1;
478 0 0         for (i = 1; i < items; i++) {
479 0           self = _insert_after(self, newSVsv(ST(i)));
480             }
481             }
482 0           RETVAL = self;
483             OUTPUT:
484             RETVAL
485              
486             SV *
487             add(self, ...)
488             SV * self
489             CODE:
490 458           RETVAL = _insert_at_end(self, newSVsv(ST(1)));
491             OUTPUT:
492             RETVAL
493              
494             SV *
495             insert(self, cb, ...)
496             SV * self
497             SV * cb
498             CODE:
499 3           RETVAL = newSVsv(_insert_find(self, cb, newSVsv(ST(2))));
500             OUTPUT:
501             RETVAL
502              
503             SV *
504             insert_before(self, ...)
505             SV * self
506             CODE:
507 3           RETVAL = _insert_before(self, newSVsv(ST(1)));
508             OUTPUT:
509             RETVAL
510              
511             SV *
512             insert_after(self, ...)
513             SV * self
514             CODE:
515 12           RETVAL = _insert_after(self, newSVsv(ST(1)));
516             OUTPUT:
517             RETVAL
518              
519             SV *
520             insert_at_start(self, ...)
521             SV * self
522             CODE:
523 114           RETVAL = _insert_at_start(self, newSVsv(ST(1)));
524             OUTPUT:
525             RETVAL
526              
527             SV *
528             insert_at_end(self, ...)
529             SV * self
530             CODE:
531 112           RETVAL = _insert_at_end(self, newSVsv(ST(1)));
532             OUTPUT:
533             RETVAL
534              
535             SV *
536             insert_at_pos(self, ...)
537             SV * self
538             CODE:
539 103           RETVAL = _insert_at_pos(self, SvIV(ST(1)), newSVsv(ST(2)));
540             OUTPUT:
541             RETVAL
542              
543             SV *
544             remove(...)
545             CODE:
546 9           RETVAL = _remove(ST(0));
547             OUTPUT:
548             RETVAL
549              
550             SV *
551             remove_from_start(...)
552             CODE:
553 107           RETVAL = _remove_from_start(ST(0));
554             OUTPUT:
555             RETVAL
556              
557             SV *
558             remove_from_end(...)
559             CODE:
560 103           RETVAL = _remove_from_end(ST(0));
561             OUTPUT:
562             RETVAL
563              
564             SV *
565             remove_from_pos(...)
566             CODE:
567 4           RETVAL = _remove_from_pos(ST(0), SvIV(ST(1)));
568             OUTPUT:
569             RETVAL
570              
571             SV *
572             find(self, cb)
573             SV * self
574             SV * cb
575             CODE:
576 1           RETVAL = _find(self, cb);
577             OUTPUT:
578             RETVAL
579              
580             void
581             destroy(self)
582             SV * self
583             CODE:
584 550           _destroy(self);
585