Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, 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 | 8.71k | { |
56 | 8.71k | os_ptr op = osp; |
57 | 8.71k | uint N; |
58 | | |
59 | 8.71k | check_op(2); |
60 | | /* Check operands for type and access */ |
61 | | /* we can sort only writable [and unpacked] arrays */ |
62 | 8.71k | if (r_type(&op[-1]) == t_mixedarray || r_type(&op[-1]) == t_shortarray) |
63 | 0 | return_error(gs_error_invalidaccess); |
64 | 8.71k | check_write_type(op[-1], t_array); |
65 | | /* the predicate must be an executable array/ string/ name/ [pseudo-]operator */ |
66 | 8.71k | if (!r_has_attr(&op[0], a_executable)) |
67 | 8.71k | return_op_typecheck(&op[0]); |
68 | 8.71k | switch (r_btype(&op[0])) { |
69 | 0 | case t_array: |
70 | 0 | case t_mixedarray: |
71 | 8.71k | case t_shortarray: |
72 | 8.71k | case t_string: |
73 | 8.71k | if (!r_has_attr(&op[0], a_execute)) |
74 | 0 | return_error(gs_error_invalidaccess); |
75 | 8.71k | break; |
76 | 8.71k | case t_name: |
77 | 0 | case t_operator: |
78 | 0 | case t_oparray: |
79 | 0 | break; |
80 | 0 | default: |
81 | 0 | return_op_typecheck(&op[0]); |
82 | 8.71k | } |
83 | | /* |
84 | | * if array length <= 1, then nothing to sort |
85 | | * else prepare the status variables and launch the main sorting routine zsort_continue() |
86 | | */ |
87 | 8.71k | N = r_size(&op[-1]); |
88 | 8.71k | if (N <= 1) { |
89 | 0 | pop(1); |
90 | 0 | return 0; |
91 | 8.71k | } else { |
92 | 8.71k | check_estack(11); |
93 | 8.71k | push_mark_estack(es_other, zsort_cleanup); |
94 | 8.71k | /*H1:*/ make_int(&esp[1], N / 2 + 1); /* l */ |
95 | 8.71k | make_int(&esp[2], N); /* r */ |
96 | 8.71k | make_int(&esp[3], 0); /* i */ |
97 | 8.71k | make_int(&esp[4], 0); /* j */ |
98 | 8.71k | make_null(&esp[5]); /* R */ |
99 | 8.71k | make_int(&esp[6], 2); /* H */ |
100 | 8.71k | ref_assign(&esp[7], &op[0]); /* lt */ |
101 | 8.71k | ref_assign(&esp[8], &op[-1]); /* the array */ |
102 | 8.71k | esp += 8; |
103 | 8.71k | make_op_estack(&esp[1], zsort_continue); |
104 | 8.71k | make_null(&op[0]); /* result of <lt>, not used when H = 2 */ |
105 | 8.71k | return zsort_continue(i_ctx_p); |
106 | 8.71k | } |
107 | 8.71k | } |
108 | | |
109 | | /* Continuation operator for .sort */ |
110 | | static int |
111 | | zsort_continue(i_ctx_t *i_ctx_p) |
112 | 6.05M | { |
113 | 6.05M | os_ptr op = osp; |
114 | 6.05M | ref *status; |
115 | 6.05M | ref *Rn; |
116 | 6.05M | # define l (status[1].value.intval) |
117 | 8.05M | # define r (status[2].value.intval) |
118 | 6.05M | # define i (status[3].value.intval) |
119 | 16.5M | # define j (status[4].value.intval) |
120 | 6.05M | # define R (status[5]) |
121 | 12.0M | # define H (status[6].value.intval) |
122 | 6.05M | # define lt (status[7]) |
123 | 6.05M | # define arry (status[8]) |
124 | | |
125 | 6.05M | status = esp - 8; |
126 | 6.05M | Rn = arry.value.refs - 1; /* the -1 compensates for using 1-based indices */ |
127 | 6.05M | switch (H) { |
128 | 3.04M | case 6: |
129 | 3.04M | /*H6_cont:*/if (!r_has_type(&op[0], t_boolean)) { |
130 | 0 | esp -= 9; |
131 | 0 | return_error(gs_error_typecheck); |
132 | 0 | } |
133 | 3.04M | if (op[0].value.boolval) { |
134 | 2.78M | /* H7: */ ref_assign_old(&arry, &Rn[i], &Rn[j], ".sort(H7)"); |
135 | 2.78M | goto H4; |
136 | 2.78M | } |
137 | 950k | do { |
138 | 950k | /* H8: */ ref_assign_old(&arry, &Rn[i], &R, ".sort(H8)"); |
139 | | /* fallthrough */ |
140 | 958k | case 2: |
141 | 958k | /* H2: */ if (l > 1) { |
142 | 322k | l--; |
143 | 322k | ref_assign(&R, &Rn[l]); |
144 | 636k | } else { |
145 | 636k | ref_assign(&R, &Rn[r]); |
146 | 636k | ref_assign_old(&arry, &Rn[r], &Rn[1], ".sort(H2-a)"); |
147 | 636k | r--; |
148 | 636k | if (r <= 1) { |
149 | 8.71k | ref_assign_old(&arry, &Rn[1], &R, ".sort(H2-b)"); |
150 | 8.71k | esp -= 9; |
151 | 8.71k | pop(1); |
152 | 8.71k | return o_pop_estack; |
153 | 8.71k | } |
154 | 636k | } |
155 | 950k | /* H3: */ j = l; |
156 | 3.74M | H4: i = j; |
157 | 3.74M | j <<= 1; |
158 | 3.74M | } while (j > r); |
159 | 3.04M | if (j == r) |
160 | 43.5k | goto H6; |
161 | 2.99M | /* H5: */ H = 5; |
162 | 2.99M | push(1); |
163 | 2.99M | ref_assign(&op[-1], &Rn[j]); |
164 | 2.99M | ref_assign(&op[0], &Rn[j + 1]); |
165 | 2.99M | break; |
166 | 2.99M | case 5: |
167 | 2.99M | /*H5_cont:*/if (!r_has_type(&op[0], t_boolean)) |
168 | 0 | return_error(gs_error_typecheck); |
169 | 2.99M | if (op[0].value.boolval) |
170 | 1.36M | j++; |
171 | 3.04M | H6: H = 6; |
172 | 3.04M | push(1); |
173 | 3.04M | ref_assign(&op[-1], &R); |
174 | 3.04M | ref_assign(&op[0], &Rn[j]); |
175 | 3.04M | break; |
176 | 0 | default: |
177 | 0 | pop(1); |
178 | 0 | esp -= 9; |
179 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
180 | 6.05M | } |
181 | 6.04M | esp += 2; |
182 | 6.04M | ref_assign(esp, <); |
183 | 6.04M | return o_push_estack; |
184 | 6.05M | #undef l |
185 | 6.05M | #undef r |
186 | 6.05M | #undef i |
187 | 6.05M | #undef j |
188 | 6.05M | #undef R |
189 | 6.05M | #undef H |
190 | 6.05M | #undef lt |
191 | 6.05M | } |
192 | | |
193 | | /* No-op cleanup routine for .sort */ |
194 | | static int |
195 | | zsort_cleanup(i_ctx_t *i_ctx_p) |
196 | 0 | { |
197 | 0 | return 0; |
198 | 0 | } |
199 | | |
200 | | /* ------ Initialization procedure ------ */ |
201 | | |
202 | | const op_def zalg_op_defs[] = |
203 | | { |
204 | | {"2.sort", zsort}, |
205 | | /* Internal operators */ |
206 | | {"1%zsort_continue", zsort_continue}, |
207 | | op_def_end(0) |
208 | | }; |