ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Array-Heap/Heap.xs
Revision: 1.7
Committed: Mon Oct 27 22:33:50 2014 UTC (9 years, 7 months ago) by root
Branch: MAIN
CVS Tags: rel-3_1
Changes since 1.6: +6 -1 lines
Log Message:
3.1

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