ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Array-Heap/Heap.xs
Revision: 1.9
Committed: Wed Dec 7 12:06:35 2016 UTC (7 years, 4 months ago) by root
Branch: MAIN
CVS Tags: rel-3_22, HEAD
Changes since 1.8: +6 -2 lines
Log Message:
3.22

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