Commit | Line | Data |
---|---|---|
55b42c18 MW |
1 | ### -*-pyrex-*- |
2 | ### | |
3 | ### Generic encoder/decoder | |
4 | ### | |
5 | ### (c) 2005 Straylight/Edgeware | |
6 | ### | |
7 | ||
8 | ###----- Licensing notice --------------------------------------------------- | |
9 | ### | |
10 | ### This file is part of the Python interface to mLib. | |
11 | ### | |
12 | ### mLib/Python is free software; you can redistribute it and/or modify | |
13 | ### it under the terms of the GNU General Public License as published by | |
14 | ### the Free Software Foundation; either version 2 of the License, or | |
15 | ### (at your option) any later version. | |
16 | ### | |
17 | ### mLib/Python is distributed in the hope that it will be useful, | |
18 | ### but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ### GNU General Public License for more details. | |
21 | ### | |
22 | ### You should have received a copy of the GNU General Public License | |
23 | ### along with mLib/Python; if not, write to the Free Software Foundation, | |
24 | ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
25 | ||
26 | ###-------------------------------------------------------------------------- | |
965caf5f MW |
27 | ### Constants. |
28 | ||
29 | CDCF_LOWERC = _CDCF_LOWERC | |
30 | CDCF_IGNCASE = _CDCF_IGNCASE | |
31 | CDCF_NOEQPAD = _CDCF_NOEQPAD | |
32 | CDCF_IGNEQPAD = _CDCF_IGNEQPAD | |
33 | CDCF_IGNEQMID = _CDCF_IGNEQMID | |
34 | CDCF_IGNZPAD = _CDCF_IGNZPAD | |
35 | CDCF_IGNNEWL = _CDCF_IGNNEWL | |
36 | CDCF_IGNSPC = _CDCF_IGNSPC | |
37 | CDCF_IGNINVCH = _CDCF_IGNINVCH | |
38 | CDCF_IGNJUNK = _CDCF_IGNJUNK | |
55b42c18 MW |
39 | |
40 | class CDCF: | |
965caf5f MW |
41 | ## DEPRECATED. Use the original names at module level. |
42 | LOWERC = _CDCF_LOWERC | |
43 | IGNCASE = _CDCF_IGNCASE | |
44 | NOEQPAD = _CDCF_NOEQPAD | |
45 | IGNEQPAD = _CDCF_IGNEQPAD | |
46 | IGNEQMID = _CDCF_IGNEQMID | |
47 | IGNZPAD = _CDCF_IGNZPAD | |
48 | IGNNEWL = _CDCF_IGNNEWL | |
49 | IGNSPC = _CDCF_IGNSPC | |
50 | IGNINVCH = _CDCF_IGNINVCH | |
51 | IGNJUNK = _CDCF_IGNJUNK | |
52 | ||
53 | CDCERR_OK = _CDCERR_OK | |
54 | CDCERR_INVCH = _CDCERR_INVCH | |
55 | CDCERR_INVEQPAD = _CDCERR_INVEQPAD | |
56 | CDCERR_INVZPAD = _CDCERR_INVZPAD | |
55b42c18 MW |
57 | |
58 | class CDCERR: | |
965caf5f MW |
59 | ## DEPRECATED. Use the original names at module level. |
60 | OK = _CDCERR_OK | |
61 | INVCH = _CDCERR_INVCH | |
62 | INVEQPAD = _CDCERR_INVEQPAD | |
63 | INVZPAD = _CDCERR_INVZPAD | |
64 | ||
65 | ###-------------------------------------------------------------------------- | |
66 | ### Base classes. | |
55b42c18 MW |
67 | |
68 | class CodecError (Exception): | |
addc0c37 MW |
69 | """ |
70 | Exception from decoding operation. | |
71 | ||
965caf5f | 72 | Attributes: `err' = `CDCERR_*' code, `msg' = message string |
addc0c37 | 73 | """ |
55b42c18 MW |
74 | def __init__(me, err): |
75 | me.err = err | |
965caf5f | 76 | me.msg = <str>_codec_strerror(err) |
55b42c18 MW |
77 | def __str__(me): |
78 | return me.msg | |
79 | ||
80 | def codec_strerror(err): | |
965caf5f MW |
81 | """codec_strerror(ERR) -> STR: message for `CDCERR_*' code""" |
82 | return <str>_codec_strerror(err) | |
55b42c18 MW |
83 | |
84 | cdef class _BaseCodec: | |
addc0c37 | 85 | """Abstract superclass for codecs.""" |
55b42c18 MW |
86 | cdef codec *c |
87 | def __cinit__(me, *hunoz, **hukairz): | |
88 | me.c = NULL | |
55b42c18 MW |
89 | def __dealloc__(me): |
90 | if me.c is not NULL: | |
91 | me.c.ops.destroy(me.c) | |
965caf5f MW |
92 | def __init__(me, *hunoz, **hukairz): |
93 | raise TypeError('abstract class') | |
94 | cdef int _code(me, const void *p, Py_ssize_t sz, | |
95 | dstr *d, bint finishp) except -1: | |
55b42c18 MW |
96 | cdef int err |
97 | if me.c is NULL: | |
965caf5f MW |
98 | raise ValueError('Encoding finished') |
99 | err = me.c.ops.code(me.c, p, sz, d) | |
100 | if err: | |
101 | raise CodecError(err) | |
102 | if finishp: | |
103 | err = me.c.ops.code(me.c, p, sz, d) | |
104 | me.c.ops.destroy(me.c) | |
105 | me.c = NULL | |
106 | if err: | |
107 | raise CodecError(err) | |
108 | return 0 | |
109 | ||
55b42c18 | 110 | def done(me): |
addc0c37 | 111 | """C.done() -> OUT: final output""" |
55b42c18 MW |
112 | me.code('', True) |
113 | ||
114 | cdef class _BaseEncoder (_BaseCodec): | |
965caf5f | 115 | def encode(me, input, finishp = False): |
addc0c37 | 116 | """C.encode(IN, [finishp = False]) -> OUT: continue/finish encoding""" |
965caf5f MW |
117 | cdef const void *p |
118 | cdef Py_ssize_t sz | |
119 | cdef dstr d | |
120 | DCREATE(&d) | |
121 | try: | |
122 | PyObject_AsReadBuffer(input, &p, &sz) | |
123 | me._code(p, sz, &d, finishp) | |
124 | return TEXT_FROMSTRLEN(d.buf, d.len) | |
125 | finally: | |
126 | dstr_destroy(&d) | |
127 | def done(me): | |
128 | """C.done() -> OUT: final output""" | |
129 | cdef dstr d | |
130 | DCREATE(&d) | |
131 | try: | |
132 | me._code(NULL, 0, &d, True) | |
133 | return TEXT_FROMSTRLEN(d.buf, d.len) | |
134 | finally: | |
135 | dstr_destroy(&d) | |
55b42c18 MW |
136 | |
137 | cdef class _BaseDecoder (_BaseCodec): | |
965caf5f | 138 | def decode(me, input, finishp = False): |
addc0c37 | 139 | """C.decode(IN, [finishp = False]) -> OUT: continue/finish decoding""" |
965caf5f MW |
140 | cdef const char *p |
141 | cdef Py_ssize_t sz | |
142 | cdef dstr d | |
143 | DCREATE(&d) | |
144 | try: | |
145 | TEXT_PTRLEN(input, &p, &sz) | |
146 | me._code(p, sz, &d, finishp) | |
147 | return BIN_FROMSTRLEN(d.buf, d.len) | |
148 | finally: | |
149 | dstr_destroy(&d) | |
150 | def done(me): | |
151 | """C.done() -> OUT: final output""" | |
152 | cdef dstr d | |
153 | DCREATE(&d) | |
154 | try: | |
155 | me._code(NULL, 0, &d, True) | |
156 | return BIN_FROMSTRLEN(d.buf, d.len) | |
157 | finally: | |
158 | dstr_destroy(&d) | |
55b42c18 MW |
159 | |
160 | ###-------------------------------------------------------------------------- | |
161 | ### Base64. | |
162 | ||
163 | cdef extern from 'mLib/base64.h': | |
164 | codec_class base64_class | |
165 | codec_class file64_class | |
166 | codec_class base64url_class | |
167 | ||
168 | cdef class Base64Encoder (_BaseEncoder): | |
addc0c37 | 169 | """ |
965caf5f | 170 | Base64Encoder([indent = '\\n'], [maxline = 72], [flags = 0]) |
addc0c37 MW |
171 | |
172 | Base64 encoder. | |
173 | """ | |
965caf5f MW |
174 | def __init__(me, indent = '\n', maxline = 72, flags = 0): |
175 | me.c = base64_class.encoder(flags, TEXT_PTR(indent), maxline) | |
55b42c18 MW |
176 | |
177 | cdef class Base64Decoder (_BaseDecoder): | |
addc0c37 | 178 | """ |
965caf5f | 179 | Base64Decoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL]) |
addc0c37 MW |
180 | |
181 | Base64 decoder. | |
182 | """ | |
965caf5f | 183 | def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL): |
55b42c18 MW |
184 | me.c = base64_class.decoder(flags) |
185 | ||
186 | cdef class File64Encoder (_BaseEncoder): | |
addc0c37 | 187 | """ |
965caf5f | 188 | File64Encoder([indent = '\\n'], [maxline = 72], [flags = 0]) |
addc0c37 MW |
189 | |
190 | Base64 encoder, using `%' instead of `/', so encoded strings are safe as | |
191 | filenames. | |
192 | """ | |
965caf5f MW |
193 | def __init__(me, indent = '\n', maxline = 72, flags = 0): |
194 | me.c = file64_class.encoder(flags, TEXT_PTR(indent), maxline) | |
55b42c18 MW |
195 | |
196 | cdef class File64Decoder (_BaseDecoder): | |
addc0c37 | 197 | """ |
965caf5f | 198 | File64Decoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL]) |
addc0c37 MW |
199 | |
200 | Base64 encoder, using `%' instead of `/', so encoded strings are safe as | |
201 | filenames. | |
202 | """ | |
965caf5f | 203 | def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL): |
55b42c18 MW |
204 | me.c = file64_class.decoder(flags) |
205 | ||
206 | cdef class Base64URLEncoder (_BaseEncoder): | |
addc0c37 | 207 | """ |
965caf5f | 208 | Base64URLEncoder([indent = '\\n'], [maxline = 72], [flags = 0]) |
addc0c37 MW |
209 | |
210 | Base64 encoder, using `-' and `_' instead of `+' and `/', so encoded | |
211 | strings are safe as URL components. | |
212 | """ | |
965caf5f MW |
213 | def __init__(me, indent = '\n', maxline = 72, flags = 0): |
214 | me.c = base64url_class.encoder(flags, TEXT_PTR(indent), maxline) | |
55b42c18 MW |
215 | |
216 | cdef class Base64URLDecoder (_BaseDecoder): | |
addc0c37 | 217 | """ |
965caf5f | 218 | Base64URLDecoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL]) |
addc0c37 MW |
219 | |
220 | Base64 decoder, using `-' and `_' instead of `+' and `/', so encoded | |
221 | strings are safe as URL components. | |
222 | """ | |
965caf5f | 223 | def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL): |
55b42c18 MW |
224 | me.c = base64url_class.decoder(flags) |
225 | ||
226 | ###-------------------------------------------------------------------------- | |
227 | ### Base32. | |
228 | ||
229 | cdef extern from 'mLib/base32.h': | |
230 | codec_class base32_class | |
231 | codec_class base32hex_class | |
232 | ||
233 | cdef class Base32Encoder (_BaseEncoder): | |
addc0c37 | 234 | """ |
965caf5f | 235 | Base32Encoder([indent = '\\n'], [maxline = 72], [flags = 0]) |
addc0c37 MW |
236 | |
237 | Base32 encoder. | |
238 | """ | |
965caf5f MW |
239 | def __init__(me, indent = '\n', maxline = 72, flags = 0): |
240 | me.c = base32_class.encoder(flags, TEXT_PTR(indent), maxline) | |
55b42c18 MW |
241 | |
242 | cdef class Base32Decoder (_BaseDecoder): | |
addc0c37 | 243 | """ |
965caf5f | 244 | Base32Decoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL | CDCF_IGNCASE]) |
addc0c37 MW |
245 | |
246 | Base32 decoder. | |
247 | """ | |
965caf5f | 248 | def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL | _CDCF_IGNCASE): |
55b42c18 MW |
249 | me.c = base32_class.decoder(flags) |
250 | ||
251 | cdef class Base32HexEncoder (_BaseEncoder): | |
addc0c37 | 252 | """ |
965caf5f | 253 | Base32Encoder([indent = '\\n'], [maxline = 72], [flags = 0]) |
addc0c37 MW |
254 | |
255 | Base32 encoder, using digits and letters in ascending order, rather than | |
256 | avoiding digits which visually resemble letters. | |
257 | """ | |
965caf5f MW |
258 | def __init__(me, indent = '\n', maxline = 72, flags = 0): |
259 | me.c = base32hex_class.encoder(flags, TEXT_PTR(indent), maxline) | |
55b42c18 MW |
260 | |
261 | cdef class Base32HexDecoder (_BaseDecoder): | |
addc0c37 | 262 | """ |
965caf5f | 263 | Base32Decoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL | CDCF_IGNCASE]) |
addc0c37 MW |
264 | |
265 | Base32 decoder, using digits and letters in ascending order, rather than | |
266 | avoiding digits which visually resemble letters. | |
267 | """ | |
965caf5f | 268 | def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL | _CDCF_IGNCASE): |
55b42c18 MW |
269 | me.c = base32hex_class.decoder(flags) |
270 | ||
271 | ###-------------------------------------------------------------------------- | |
272 | ### Hex. | |
273 | ||
274 | cdef extern from 'mLib/hex.h': | |
275 | codec_class hex_class | |
276 | ||
277 | cdef class HexEncoder (_BaseEncoder): | |
addc0c37 | 278 | """ |
965caf5f | 279 | HexEncoder([indent = '\\n'], [maxline = 72], [flags = CDCF_LOWERC]) |
addc0c37 MW |
280 | |
281 | Hexadecimal encoder. | |
282 | """ | |
965caf5f MW |
283 | def __init__(me, indent = '\n', maxline = 72, flags = _CDCF_LOWERC): |
284 | me.c = hex_class.encoder(flags, TEXT_PTR(indent), maxline) | |
55b42c18 MW |
285 | |
286 | cdef class HexDecoder (_BaseDecoder): | |
addc0c37 | 287 | """ |
965caf5f | 288 | HexDecoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL | CDCF_IGNCASE]) |
addc0c37 MW |
289 | |
290 | Hexadecimal decoder. | |
291 | """ | |
965caf5f | 292 | def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL | _CDCF_IGNCASE): |
55b42c18 MW |
293 | me.c = hex_class.decoder(flags) |
294 | ||
295 | ###----- That's all, folks -------------------------------------------------- |