ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Array-Heap/Heap.xs
Revision: 1.8
Committed: Tue Jul 14 23:28:10 2015 UTC (8 years, 10 months ago) by root
Branch: MAIN
CVS Tags: rel-3_2, rel-3_21
Changes since 1.7: +3 -6 lines
Log Message:
3.2

File Contents

# Content
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 /* pre-5.10 compatibility */
6 #ifndef GV_NOTQUAL
7 # define GV_NOTQUAL 1
8 #endif
9 #ifndef gv_fetchpvs
10 # define gv_fetchpvs gv_fetchpv
11 #endif
12
13 /* pre-5.8 compatibility */
14 #ifndef PERL_MAGIC_tied
15 # define PERL_MAGIC_tied 'P'
16 #endif
17
18 #include "multicall.h"
19
20 /* workaround for buggy multicall API */
21 #ifndef cxinc
22 # define cxinc() Perl_cxinc (aTHX)
23 #endif
24
25 #define dCMP \
26 dMULTICALL; \
27 void *cmp_data; \
28 I32 gimme = G_SCALAR;
29
30 #define CMP_PUSH(sv) \
31 PUSH_MULTICALL (cmp_push_ (sv));\
32 cmp_data = multicall_cop;
33
34 #define CMP_POP \
35 POP_MULTICALL;
36
37 #define dCMP_CALL(data) \
38 OP *multicall_cop = (OP *)data;
39
40 static void *
41 cmp_push_ (SV *sv)
42 {
43 HV *st;
44 GV *gvp;
45 CV *cv;
46
47 cv = sv_2cv (sv, &st, &gvp, 0);
48
49 if (!cv)
50 croak ("%s: callback must be a CODE reference or another callable object", SvPV_nolen (sv));
51
52 SAVESPTR (PL_firstgv ); PL_firstgv = gv_fetchpv ("a", GV_ADD | GV_NOTQUAL, SVt_PV); SAVESPTR (GvSV (PL_firstgv ));
53 SAVESPTR (PL_secondgv); PL_secondgv = gv_fetchpv ("b", GV_ADD | GV_NOTQUAL, SVt_PV); SAVESPTR (GvSV (PL_secondgv));
54
55 return cv;
56 }
57
58 /*****************************************************************************/
59
60 static SV *
61 sv_first (SV *sv)
62 {
63 if (SvROK (sv) && SvTYPE (SvRV (sv)) == SVt_PVAV)
64 {
65 AV *av = (AV *)SvRV (sv);
66
67 sv = AvFILLp (av) < 0 ? &PL_sv_undef : AvARRAY (av)[0];
68 }
69
70 return sv;
71 }
72
73 static void
74 set_idx (SV *sv, int idx)
75 {
76 if (!SvROK (sv))
77 return;
78
79 sv = SvRV (sv);
80
81 if (SvTYPE (sv) != SVt_PVAV)
82 return;
83
84 if (AvFILL ((AV *)sv) < 1 || AvARRAY ((AV *)sv)[1] == &PL_sv_undef)
85 av_store ((AV *)sv, 1, newSViv (idx));
86 else
87 {
88 sv = AvARRAY ((AV *)sv)[1];
89
90 if (SvTYPE (sv) == SVt_IV)
91 SvIV_set (sv, idx);
92 else
93 sv_setiv (sv, idx);
94 }
95 }
96
97 #define set_heap(idx,he) \
98 do { \
99 if (flags) \
100 set_idx (he, idx); \
101 heap [idx] = he; \
102 } while (0)
103
104 static int
105 cmp_nv (SV *a, SV *b, void *cmp_data)
106 {
107 a = sv_first (a);
108 b = sv_first (b);
109
110 return SvNV (a) > SvNV (b);
111 }
112
113 static int
114 cmp_sv (SV *a, SV *b, void *cmp_data)
115 {
116 a = sv_first (a);
117 b = sv_first (b);
118
119 return sv_cmp (a, b) > 0;
120 }
121
122 static int
123 cmp_custom (SV *a, SV *b, void *cmp_data)
124 {
125 dCMP_CALL (cmp_data);
126
127 GvSV (PL_firstgv ) = a;
128 GvSV (PL_secondgv) = b;
129
130 MULTICALL;
131
132 if (SvTRUE (ERRSV))
133 croak (NULL);
134
135 {
136 dSP;
137 return TOPi > 0;
138 }
139 }
140
141 /*****************************************************************************/
142
143 typedef int (*f_cmp)(SV *a, SV *b, void *cmp_data);
144
145 static AV *
146 array (SV *ref)
147 {
148 if (SvROK (ref)
149 && SvTYPE (SvRV (ref)) == SVt_PVAV
150 && !SvTIED_mg (SvRV (ref), PERL_MAGIC_tied))
151 return (AV *)SvRV (ref);
152
153 croak ("argument 'heap' must be a (non-tied) array");
154 }
155
156 #define gt(a,b) cmp ((a), (b), cmp_data)
157
158 /*****************************************************************************/
159
160 /* away from the root */
161 static void
162 downheap (AV *av, f_cmp cmp, void *cmp_data, int N, int k, int flags)
163 {
164 SV **heap = AvARRAY (av);
165 SV *he = heap [k];
166
167 for (;;)
168 {
169 int c = (k << 1) + 1;
170
171 if (c >= N)
172 break;
173
174 c += c + 1 < N && gt (heap [c], heap [c + 1])
175 ? 1 : 0;
176
177 if (!(gt (he, heap [c])))
178 break;
179
180 set_heap (k, heap [c]);
181
182 k = c;
183 }
184
185 set_heap (k, he);
186 }
187
188 /* towards the root */
189 static void
190 upheap (AV *av, f_cmp cmp, void *cmp_data, int k, int flags)
191 {
192 SV **heap = AvARRAY (av);
193 SV *he = heap [k];
194
195 while (k)
196 {
197 int p = (k - 1) >> 1;
198
199 if (!(gt (heap [p], he)))
200 break;
201
202 set_heap (k, heap [p]);
203 k = p;
204 }
205
206 set_heap (k, he);
207 }
208
209 /* move an element suitably so it is in a correct place */
210 static void
211 adjustheap (AV *av, f_cmp cmp, void *cmp_data, int N, int k, int flags)
212 {
213 SV **heap = AvARRAY (av);
214
215 if (k > 0 && !gt (heap [k], heap [(k - 1) >> 1]))
216 upheap (av, cmp, cmp_data, k, flags);
217 else
218 downheap (av, cmp, cmp_data, N, k, flags);
219 }
220
221 /*****************************************************************************/
222
223 static void
224 make_heap (AV *av, f_cmp cmp, void *cmp_data, int flags)
225 {
226 int i, len = AvFILLp (av);
227
228 /* do not use floyds algorithm, as I expect the simpler and more cache-efficient */
229 /* upheap is actually faster */
230 for (i = 0; i <= len; ++i)
231 upheap (av, cmp, cmp_data, i, flags);
232 }
233
234 static void
235 push_heap (AV *av, f_cmp cmp, void *cmp_data, SV **elems, int nelems, int flags)
236 {
237 int i;
238
239 av_extend (av, AvFILLp (av) + nelems);
240
241 /* we do it in two steps, as the perl cmp function might copy the stack */
242 for (i = 0; i < nelems; ++i)
243 AvARRAY (av)[++AvFILLp (av)] = newSVsv (elems [i]);
244
245 for (i = 0; i < nelems; ++i)
246 upheap (av, cmp, cmp_data, AvFILLp (av) - i, flags);
247 }
248
249 static SV *
250 pop_heap (AV *av, f_cmp cmp, void *cmp_data, int flags)
251 {
252 int len = AvFILLp (av);
253
254 if (len < 0)
255 return &PL_sv_undef;
256 else if (len == 0)
257 return av_pop (av);
258 else
259 {
260 SV *top = av_pop (av);
261 SV *result = AvARRAY (av)[0];
262 AvARRAY (av)[0] = top;
263 downheap (av, cmp, cmp_data, len, 0, flags);
264 return result;
265 }
266 }
267
268 static SV *
269 splice_heap (AV *av, f_cmp cmp, void *cmp_data, int idx, int flags)
270 {
271 int len = AvFILLp (av);
272
273 if (idx < 0 || idx > len)
274 return &PL_sv_undef;
275 else if (idx == len)
276 return av_pop (av); /* the last element */
277 else
278 {
279 SV *top = av_pop (av);
280 SV *result = AvARRAY (av)[idx];
281 AvARRAY (av)[idx] = top;
282 adjustheap (av, cmp, cmp_data, len, idx, flags);
283 return result;
284 }
285 }
286
287 static void
288 adjust_heap (AV *av, f_cmp cmp, void *cmp_data, int idx, int flags)
289 {
290 int len = AvFILLp (av);
291
292 if (idx > len)
293 croak ("Array::Heap::adjust_heap: index out of array bounds");
294
295 adjustheap (av, cmp, cmp_data, len + 1, idx, flags);
296 }
297
298 MODULE = Array::Heap PACKAGE = Array::Heap
299
300 void
301 make_heap (SV *heap)
302 PROTOTYPE: \@
303 ALIAS:
304 make_heap_idx = 1
305 CODE:
306 make_heap (array (heap), cmp_nv, 0, ix);
307
308 void
309 make_heap_lex (SV *heap)
310 PROTOTYPE: \@
311 CODE:
312 make_heap (array (heap), cmp_sv, 0, 0);
313
314 void
315 make_heap_cmp (SV *cmp, SV *heap)
316 PROTOTYPE: &\@
317 CODE:
318 {
319 dCMP;
320 CMP_PUSH (cmp);
321 make_heap (array (heap), cmp_custom, cmp_data, 0);
322 CMP_POP;
323 }
324
325 void
326 push_heap (SV *heap, ...)
327 PROTOTYPE: \@@
328 ALIAS:
329 push_heap_idx = 1
330 CODE:
331 push_heap (array (heap), cmp_nv, 0, &(ST(1)), items - 1, ix);
332
333 void
334 push_heap_lex (SV *heap, ...)
335 PROTOTYPE: \@@
336 CODE:
337 push_heap (array (heap), cmp_sv, 0, &(ST(1)), items - 1, 0);
338
339 void
340 push_heap_cmp (SV *cmp, SV *heap, ...)
341 PROTOTYPE: &\@@
342 CODE:
343 {
344 SV **st_2 = &(ST(2)); /* multicall.h uses PUSHSTACK */
345 dCMP;
346 CMP_PUSH (cmp);
347 push_heap (array (heap), cmp_custom, cmp_data, st_2, items - 2, 0);
348 CMP_POP;
349 }
350
351 SV *
352 pop_heap (SV *heap)
353 PROTOTYPE: \@
354 ALIAS:
355 pop_heap_idx = 1
356 CODE:
357 RETVAL = pop_heap (array (heap), cmp_nv, 0, ix);
358 OUTPUT:
359 RETVAL
360
361 SV *
362 pop_heap_lex (SV *heap)
363 PROTOTYPE: \@
364 CODE:
365 RETVAL = pop_heap (array (heap), cmp_sv, 0, 0);
366 OUTPUT:
367 RETVAL
368
369 SV *
370 pop_heap_cmp (SV *cmp, SV *heap)
371 PROTOTYPE: &\@
372 CODE:
373 {
374 dCMP;
375 CMP_PUSH (cmp);
376 RETVAL = pop_heap (array (heap), cmp_custom, cmp_data, 0);
377 CMP_POP;
378 }
379 OUTPUT:
380 RETVAL
381
382 SV *
383 splice_heap (SV *heap, int idx)
384 PROTOTYPE: \@$
385 ALIAS:
386 splice_heap_idx = 1
387 CODE:
388 RETVAL = splice_heap (array (heap), cmp_nv, 0, idx, ix);
389 OUTPUT:
390 RETVAL
391
392 SV *
393 splice_heap_lex (SV *heap, int idx)
394 PROTOTYPE: \@$
395 CODE:
396 RETVAL = splice_heap (array (heap), cmp_sv, 0, idx, 0);
397 OUTPUT:
398 RETVAL
399
400 SV *
401 splice_heap_cmp (SV *cmp, SV *heap, int idx)
402 PROTOTYPE: &\@$
403 CODE:
404 {
405 dCMP;
406 CMP_PUSH (cmp);
407 RETVAL = splice_heap (array (heap), cmp_custom, cmp_data, idx, 0);
408 CMP_POP;
409 }
410 OUTPUT:
411 RETVAL
412
413 void
414 adjust_heap (SV *heap, int idx)
415 PROTOTYPE: \@$
416 ALIAS:
417 adjust_heap_idx = 1
418 CODE:
419 adjust_heap (array (heap), cmp_nv, 0, idx, ix);
420
421 void
422 adjust_heap_lex (SV *heap, int idx)
423 PROTOTYPE: \@$
424 CODE:
425 adjust_heap (array (heap), cmp_sv, 0, idx, 0);
426
427 void
428 adjust_heap_cmp (SV *cmp, SV *heap, int idx)
429 PROTOTYPE: &\@$
430 CODE:
431 {
432 dCMP;
433 CMP_PUSH (cmp);
434 adjust_heap (array (heap), cmp_custom, cmp_data, idx, 0);
435 CMP_POP;
436 }
437