/src/ghostpdl/base/sa85d.c
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 | | /* ASCII85Decode filter */ |
18 | | #include "std.h" |
19 | | #include "strimpl.h" |
20 | | #include "sa85d.h" |
21 | | #include "scanchar.h" |
22 | | |
23 | | /* ------ ASCII85Decode ------ */ |
24 | | |
25 | | private_st_A85D_state(); |
26 | | |
27 | | /* Initialize the state */ |
28 | | static int |
29 | | s_A85D_init(stream_state * st) |
30 | 519 | { |
31 | 519 | stream_A85D_state *const ss = (stream_A85D_state *) st; |
32 | | |
33 | 519 | s_A85D_init_inline(ss); |
34 | 519 | return 0; |
35 | 519 | } |
36 | | |
37 | | /* Process a buffer */ |
38 | | static int a85d_finish(int, ulong, stream_cursor_write *); |
39 | | static int |
40 | | s_A85D_process(stream_state * st, stream_cursor_read * pr, |
41 | | stream_cursor_write * pw, bool last) |
42 | 574k | { |
43 | 574k | stream_A85D_state *const ss = (stream_A85D_state *) st; |
44 | 574k | register const byte *p = pr->ptr; |
45 | 574k | register byte *q = pw->ptr; |
46 | | /* stop processing early unless the target is empty (last == true) */ |
47 | | /* to make sure we consume the EOD marker correctly. The EOD marker */ |
48 | | /* might be as many as 6 characters after the last valid data char */ |
49 | | /* D <cr> <lf> '~' <cr> <lf> '>' where 'D' is a data character. */ |
50 | 574k | const byte *rlimit = pr->limit - (last ? 0 : 7); /* max EOD len + 1 */ |
51 | 574k | const byte *r = max(p, rlimit); |
52 | 574k | byte *wlimit = pw->limit; |
53 | 574k | int ccount = ss->odd; |
54 | 574k | ulong word = ss->word; |
55 | 574k | int status = 0; |
56 | | |
57 | | /* scan to make sure that an EOD isn't fully contained in the */ |
58 | | /* last part of the buffer (between rlimit and pr->limit). */ |
59 | 1.31M | while (r < pr->limit) { |
60 | 743k | if (*++r == '~') |
61 | 3.82M | while (r < pr->limit) |
62 | 3.26M | if (*++r == '>') { |
63 | | /* we have both characters of a complete EOD. */ |
64 | 586 | rlimit = pr->limit; /* go ahead and process everything */ |
65 | 586 | r = rlimit; /* break out of the while loops */ |
66 | 586 | break; |
67 | 586 | } |
68 | 743k | } |
69 | 6.71M | while (p < rlimit) { |
70 | 6.70M | int ch = *++p; |
71 | 6.70M | uint ccode = ch - '!'; |
72 | | |
73 | 6.70M | if (ccode < 85) { /* catches ch < '!' as well */ |
74 | 6.10M | if (ccount == 4) { |
75 | | /* |
76 | | * We've completed a 32-bit group. Make sure we have |
77 | | * room for it in the output. |
78 | | */ |
79 | 1.22M | if (wlimit - q < 4) { |
80 | 1.39k | p--; |
81 | 1.39k | status = 1; |
82 | 1.39k | break; |
83 | 1.39k | } |
84 | | /* Check for overflow condition, throw ioerror if so */ |
85 | 1.22M | if (word >= 0x03030303 && ccode > 0) { |
86 | 165 | status = ERRC; |
87 | 165 | break; |
88 | 165 | } |
89 | 1.22M | word = word * 85 + ccode; |
90 | 1.22M | q[1] = (byte) (word >> 24); |
91 | 1.22M | q[2] = (byte) (word >> 16); |
92 | 1.22M | q[3] = (byte) ((uint) word >> 8); |
93 | 1.22M | q[4] = (byte) word; |
94 | 1.22M | q += 4; |
95 | 1.22M | word = 0; |
96 | 1.22M | ccount = 0; |
97 | 4.88M | } else { |
98 | 4.88M | word = word * 85 + ccode; |
99 | 4.88M | ++ccount; |
100 | 4.88M | } |
101 | 6.10M | } else if (ch == 'z' && ccount == 0) { |
102 | 313 | if (wlimit - q < 4) { |
103 | 0 | p--; |
104 | 0 | status = 1; |
105 | 0 | break; |
106 | 0 | } |
107 | 313 | q[1] = q[2] = q[3] = q[4] = 0, |
108 | 313 | q += 4; |
109 | 599k | } else if (scan_char_decoder[ch] == ctype_space) |
110 | 599k | DO_NOTHING; |
111 | 567k | else if (ch == '~') { |
112 | 567k | int i = 1; |
113 | | |
114 | 567k | rlimit = pr->limit; /* Here we use the real "limit" */ |
115 | | /* Handle odd bytes. */ |
116 | 567k | if (p == rlimit) { |
117 | 18 | if (!last) |
118 | 0 | p--; |
119 | 18 | else if (ss->pdf_rules) |
120 | 0 | goto finish; |
121 | 18 | else |
122 | 18 | status = ERRC; |
123 | 18 | break; |
124 | 18 | } |
125 | 567k | if ((int)(wlimit - q) < ccount - 1) { |
126 | 113 | status = 1; |
127 | 113 | p--; |
128 | 113 | break; |
129 | 113 | } |
130 | | |
131 | | /* According to PLRM 3rd, if the A85 filter encounters '~', |
132 | | * the next character must be '>'. |
133 | | * And any other characters should raise an ioerror. |
134 | | * But Adobe Acrobat allows CR/LF between ~ and >. |
135 | | * So we allow CR/LF between them. */ |
136 | | /* PDF further relaxes the requirements and accepts bare '~'. |
137 | | */ |
138 | 569k | while ((p + i <= rlimit) && (p[i] == 13 || p[i] == 10)) |
139 | 2.56k | i++; |
140 | 567k | if (p + i <= rlimit && p[i] != '>') { |
141 | 567k | if (ss->pdf_rules) { |
142 | 0 | if (p[i] == 13 || p[i] == 10) { |
143 | 0 | if (!last) |
144 | 0 | break; |
145 | 0 | } else { |
146 | 0 | p--; |
147 | 0 | } |
148 | 567k | } else { |
149 | 567k | if (p + i == rlimit) { |
150 | 16 | if (last) |
151 | 16 | status = ERRC; |
152 | 0 | else |
153 | 0 | p--; /* we'll see the '~' after filling the buffer */ |
154 | 16 | } |
155 | 567k | break; |
156 | 567k | } |
157 | 567k | } |
158 | 123 | finish: |
159 | 123 | if (p + i <= rlimit) |
160 | 83 | p += i; /* advance to the '>' */ |
161 | 40 | else |
162 | 40 | p = rlimit; /* Can happen if the '>' is missing */ |
163 | 123 | pw->ptr = q; |
164 | 123 | status = a85d_finish(ccount, word, pw); |
165 | 123 | q = pw->ptr; |
166 | 123 | break; |
167 | 567k | } else { /* syntax error or exception */ |
168 | 469 | status = ERRC; |
169 | 469 | break; |
170 | 469 | } |
171 | 6.70M | } |
172 | 574k | pw->ptr = q; |
173 | 574k | if (status == 0 && last) { |
174 | 630 | if ((int)(wlimit - q) < ccount - 1) |
175 | 0 | status = 1; |
176 | 630 | else if (ss->require_eod) |
177 | 623 | status = ERRC; |
178 | 7 | else |
179 | 7 | status = a85d_finish(ccount, word, pw); |
180 | 630 | } |
181 | 574k | pr->ptr = p; |
182 | 574k | ss->odd = ccount; |
183 | 574k | ss->word = word; |
184 | 574k | return status; |
185 | 574k | } |
186 | | /* Handle the end of input data. */ |
187 | | static int |
188 | | a85d_finish(int ccount, ulong word, stream_cursor_write * pw) |
189 | 130 | { |
190 | | /* Assume there is enough room in the output buffer! */ |
191 | 130 | byte *q = pw->ptr; |
192 | 130 | int status = EOFC; |
193 | | |
194 | 130 | switch (ccount) { |
195 | 28 | case 0: |
196 | 28 | break; |
197 | 15 | case 1: /* syntax error */ |
198 | 15 | status = ERRC; |
199 | 15 | break; |
200 | 43 | case 2: /* 1 odd byte */ |
201 | 43 | word = word * (85L * 85 * 85) + 85L * 85 * 85 - 1L; |
202 | 43 | goto o1; |
203 | 28 | case 3: /* 2 odd bytes */ |
204 | 28 | word = word * (85L * 85) + 85L * 85L - 1L; |
205 | 28 | goto o2; |
206 | 16 | case 4: /* 3 odd bytes */ |
207 | 16 | word = word * 85L + 84L; |
208 | 16 | q[3] = (byte) (word >> 8); |
209 | 44 | o2: q[2] = (byte) (word >> 16); |
210 | 87 | o1: q[1] = (byte) (word >> 24); |
211 | 87 | q += ccount - 1; |
212 | 87 | pw->ptr = q; |
213 | 130 | } |
214 | 130 | return status; |
215 | 130 | } |
216 | | |
217 | | /* Stream template */ |
218 | | const stream_template s_A85D_template = { |
219 | | &st_A85D_state, s_A85D_init, s_A85D_process, 2, 4 |
220 | | }; |