Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2021 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Operators for general-purpose algorithms. For now, only sorting. */ |
18 | | #include "ghost.h" |
19 | | #include "gserrors.h" |
20 | | #include "oper.h" |
21 | | #include "store.h" |
22 | | #include "estack.h" |
23 | | |
24 | | /* ========================================================================= */ |
25 | | |
26 | | /* |
27 | | * The "heap sort" algorithm, as implementation of the .sort operator |
28 | | * |
29 | | * The implementation follows Algorithm H from Donald Knuth's |
30 | | * "The Art of Computer Programming", volume 3, section 5.2.3 |
31 | | * |
32 | | * Notes: |
33 | | * i. Execution time: O(n log n) in the average and worst cases. |
34 | | * ii. The sort is not "stable" (the relative order of elements with |
35 | | * equal keys is not necessarily preserved). |
36 | | * iii. Status variables: |
37 | | * - stored on the e-stack; |
38 | | * - "l", "r", "i", "j" and "R" correspond directly to variables in |
39 | | * Algorithm H (including the fact that indices are 1-based); |
40 | | * - variable "K" from Algorithm H is not used here, because we don't |
41 | | * distinguish a "key part" of the array elements; |
42 | | * - "H" indicates the step to execute; used when resuming after executing |
43 | | * <lt> (to execute it, we have to return to the interpreter). |
44 | | * - "array" and "lt" are refs to the parameters; avoids using them from the |
45 | | * o-stack after resuming, in case the predicate has odd side-efects |
46 | | */ |
47 | | |
48 | | static int zsort(i_ctx_t *i_ctx_p); |
49 | | static int zsort_continue(i_ctx_t *i_ctx_p); |
50 | | static int zsort_cleanup(i_ctx_t *i_ctx_p); |
51 | | |
52 | | /* <array> <lt> .sort <array> */ |
53 | | static int |
54 | | zsort(i_ctx_t *i_ctx_p) |
55 | 89.2k | { |
56 | 89.2k | os_ptr op = osp; |
57 | 89.2k | uint N; |
58 | | |
59 | | /* Check operands for type and access */ |
60 | | /* we can sort only writable [and unpacked] arrays */ |
61 | 89.2k | if (r_type(&op[-1]) == t_mixedarray || r_type(&op[-1]) == t_shortarray) |
62 | 0 | return_error(gs_error_invalidaccess); |
63 | 89.2k | check_write_type(op[-1], t_array); |
64 | | /* the predicate must be an executable array/ string/ name/ [pseudo-]operator */ |
65 | 89.2k | if (!r_has_attr(&op[0], a_executable)) |
66 | 89.2k | return_op_typecheck(&op[0]); |
67 | 89.2k | switch (r_btype(&op[0])) { |
68 | 0 | case t_array: |
69 | 0 | case t_mixedarray: |
70 | 89.2k | case t_shortarray: |
71 | 89.2k | case t_string: |
72 | 89.2k | if (!r_has_attr(&op[0], a_execute)) |
73 | 0 | return_error(gs_error_invalidaccess); |
74 | 89.2k | break; |
75 | 89.2k | case t_name: |
76 | 0 | case t_operator: |
77 | 0 | case t_oparray: |
78 | 0 | break; |
79 | 0 | default: |
80 | 0 | return_op_typecheck(&op[0]); |
81 | 89.2k | } |
82 | | /* |
83 | | * if array length <= 1, then nothing to sort |
84 | | * else prepare the status variables and launch the main sorting routine zsort_continue() |
85 | | */ |
86 | 89.2k | N = r_size(&op[-1]); |
87 | 89.2k | if (N <= 1) { |
88 | 0 | pop(1); |
89 | 0 | return 0; |
90 | 89.2k | } else { |
91 | 89.2k | check_estack(11); |
92 | 89.2k | push_mark_estack(es_other, zsort_cleanup); |
93 | 89.2k | /*H1:*/ make_int(&esp[1], N / 2 + 1); /* l */ |
94 | 89.2k | make_int(&esp[2], N); /* r */ |
95 | 89.2k | make_int(&esp[3], 0); /* i */ |
96 | 89.2k | make_int(&esp[4], 0); /* j */ |
97 | 89.2k | make_null(&esp[5]); /* R */ |
98 | 89.2k | make_int(&esp[6], 2); /* H */ |
99 | 89.2k | ref_assign(&esp[7], &op[0]); /* lt */ |
100 | 89.2k | ref_assign(&esp[8], &op[-1]); /* the array */ |
101 | 89.2k | esp += 8; |
102 | 89.2k | make_op_estack(&esp[1], zsort_continue); |
103 | 89.2k | make_null(&op[0]); /* result of <lt>, not used when H = 2 */ |
104 | 89.2k | return zsort_continue(i_ctx_p); |
105 | 89.2k | } |
106 | 89.2k | } |
107 | | |
108 | | /* Continuation operator for .sort */ |
109 | | static int |
110 | | zsort_continue(i_ctx_t *i_ctx_p) |
111 | 49.8M | { |
112 | 49.8M | os_ptr op = osp; |
113 | 49.8M | ref *status; |
114 | 49.8M | ref *Rn; |
115 | 49.8M | # define l (status[1].value.intval) |
116 | 67.0M | # define r (status[2].value.intval) |
117 | 49.8M | # define i (status[3].value.intval) |
118 | 138M | # define j (status[4].value.intval) |
119 | 49.8M | # define R (status[5]) |
120 | 99.6M | # define H (status[6].value.intval) |
121 | 49.8M | # define lt (status[7]) |
122 | 49.8M | # define arry (status[8]) |
123 | | |
124 | 49.8M | status = esp - 8; |
125 | 49.8M | Rn = arry.value.refs - 1; /* the -1 compensates for using 1-based indices */ |
126 | 49.8M | switch (H) { |
127 | 25.0M | case 6: |
128 | 25.0M | /*H6_cont:*/if (!r_has_type(&op[0], t_boolean)) { |
129 | 0 | esp -= 9; |
130 | 0 | return_error(gs_error_typecheck); |
131 | 0 | } |
132 | 25.0M | if (op[0].value.boolval) { |
133 | 22.6M | /* H7: */ ref_assign_old(&arry, &Rn[i], &Rn[j], ".sort(H7)"); |
134 | 22.6M | goto H4; |
135 | 22.6M | } |
136 | 8.21M | do { |
137 | 8.21M | /* H8: */ ref_assign_old(&arry, &Rn[i], &R, ".sort(H8)"); |
138 | | /* fallthrough */ |
139 | 8.30M | case 2: |
140 | 8.30M | /* H2: */ if (l > 1) { |
141 | 2.76M | l--; |
142 | 2.76M | ref_assign(&R, &Rn[l]); |
143 | 5.53M | } else { |
144 | 5.53M | ref_assign(&R, &Rn[r]); |
145 | 5.53M | ref_assign_old(&arry, &Rn[r], &Rn[1], ".sort(H2-a)"); |
146 | 5.53M | r--; |
147 | 5.53M | if (r <= 1) { |
148 | 89.2k | ref_assign_old(&arry, &Rn[1], &R, ".sort(H2-b)"); |
149 | 89.2k | esp -= 9; |
150 | 89.2k | pop(1); |
151 | 89.2k | return o_pop_estack; |
152 | 89.2k | } |
153 | 5.53M | } |
154 | | /* H3: */ j = l; |
155 | 30.8M | H4: i = j; |
156 | 30.8M | j <<= 1; |
157 | 30.8M | } while (j > r); |
158 | 25.0M | if (j == r) |
159 | 357k | goto H6; |
160 | | /* H5: */ H = 5; |
161 | 24.7M | push(1); |
162 | 24.7M | ref_assign(&op[-1], &Rn[j]); |
163 | 24.7M | ref_assign(&op[0], &Rn[j + 1]); |
164 | 24.7M | break; |
165 | 24.7M | case 5: |
166 | 24.7M | /*H5_cont:*/if (!r_has_type(&op[0], t_boolean)) |
167 | 0 | return_error(gs_error_typecheck); |
168 | 24.7M | if (op[0].value.boolval) |
169 | 12.1M | j++; |
170 | 25.0M | H6: H = 6; |
171 | 25.0M | push(1); |
172 | 25.0M | ref_assign(&op[-1], &R); |
173 | 25.0M | ref_assign(&op[0], &Rn[j]); |
174 | 25.0M | break; |
175 | 0 | default: |
176 | 0 | pop(1); |
177 | 0 | esp -= 9; |
178 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
179 | 49.8M | } |
180 | 49.8M | esp += 2; |
181 | 49.8M | ref_assign(esp, <); |
182 | 49.8M | return o_push_estack; |
183 | 49.8M | #undef l |
184 | 49.8M | #undef r |
185 | 49.8M | #undef i |
186 | 49.8M | #undef j |
187 | 49.8M | #undef R |
188 | 49.8M | #undef H |
189 | 49.8M | #undef lt |
190 | 49.8M | } |
191 | | |
192 | | /* No-op cleanup routine for .sort */ |
193 | | static int |
194 | | zsort_cleanup(i_ctx_t *i_ctx_p) |
195 | 0 | { |
196 | 0 | return 0; |
197 | 0 | } |
198 | | |
199 | | /* ------ Initialization procedure ------ */ |
200 | | |
201 | | const op_def zalg_op_defs[] = |
202 | | { |
203 | | {"2.sort", zsort}, |
204 | | /* Internal operators */ |
205 | | {"1%zsort_continue", zsort_continue}, |
206 | | op_def_end(0) |
207 | | }; |