blob: 228fd1117322fa7b63f929087aa21cf1ad7700a1 [file] [log] [blame]
Lev Walkin59b176e2005-11-26 11:25:14 +00001/*
Lev Walkin523de9e2006-08-18 01:34:18 +00002 * Copyright (c) 2005, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
Lev Walkin59b176e2005-11-26 11:25:14 +00003 * Redistribution and modifications are permitted subject to BSD license.
4 */
5#include <asn_system.h>
Lev Walkin9f5bb3a2006-08-18 01:46:46 +00006#include <asn_internal.h>
Lev Walkin59b176e2005-11-26 11:25:14 +00007#include <per_support.h>
8
9/*
Lev Walkin60364882005-11-28 06:58:11 +000010 * Extract a small number of bits (<= 31) from the specified PER data pointer.
Lev Walkin59b176e2005-11-26 11:25:14 +000011 */
12int32_t
13per_get_few_bits(asn_per_data_t *pd, int nbits) {
14 size_t off; /* Next after last bit offset */
Lev Walkin5b78e1c2007-06-24 06:26:47 +000015 ssize_t nleft;
Lev Walkin59b176e2005-11-26 11:25:14 +000016 uint32_t accum;
Lev Walkin1d9e8dd2005-12-07 05:46:03 +000017 const uint8_t *buf;
Lev Walkin59b176e2005-11-26 11:25:14 +000018
Lev Walkin5b78e1c2007-06-24 06:26:47 +000019 if(nbits < 0)
Lev Walkin59b176e2005-11-26 11:25:14 +000020 return -1;
Lev Walkin59b176e2005-11-26 11:25:14 +000021
Lev Walkin5b78e1c2007-06-24 06:26:47 +000022 nleft = pd->nbits - pd->nboff;
23 if(nbits > nleft) {
24 int32_t tailv, vhead;
25 if(!pd->refill || nbits > 31) return -1;
26 /* Accumulate unused bytes before refill */
27 ASN_DEBUG("Obtain the rest %d bits", nleft);
28 tailv = per_get_few_bits(pd, nleft);
29 if(tailv < 0) return -1;
30 /* Refill (replace pd contents with new data) */
31 if(pd->refill(pd))
32 return -1;
33 nbits -= nleft;
34 vhead = per_get_few_bits(pd, nbits);
35 /* Combine the rest of previous pd with the head of new one */
36 tailv = (tailv << nbits) | vhead; /* Could == -1 */
37 return tailv;
38 }
39
40 ASN_DEBUG("[PER get %d bits from %p+%d bits, %d available]",
41 nbits, pd->buffer, pd->nboff, nleft);
Lev Walkin0a8aa602006-09-18 20:05:55 +000042
Lev Walkin59b176e2005-11-26 11:25:14 +000043 /*
44 * Normalize position indicator.
45 */
46 if(pd->nboff >= 8) {
47 pd->buffer += (pd->nboff >> 3);
48 pd->nbits -= (pd->nboff & ~0x07);
49 pd->nboff &= 0x07;
50 }
51 off = (pd->nboff += nbits);
52 buf = pd->buffer;
53
54 /*
55 * Extract specified number of bits.
56 */
57 if(off <= 8)
Lev Walkin60364882005-11-28 06:58:11 +000058 accum = nbits ? (buf[0]) >> (8 - off) : 0;
Lev Walkin59b176e2005-11-26 11:25:14 +000059 else if(off <= 16)
60 accum = ((buf[0] << 8) + buf[1]) >> (16 - off);
61 else if(off <= 24)
62 accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off);
63 else if(off <= 31)
64 accum = ((buf[0] << 24) + (buf[1] << 16)
65 + (buf[2] << 8) + (buf[3])) >> (32 - off);
Lev Walkin60364882005-11-28 06:58:11 +000066 else if(nbits <= 31) {
67 asn_per_data_t tpd = *pd;
68 /* Here are we with our 31-bits limit plus 1..7 bits offset. */
69 tpd.nboff -= nbits;
70 accum = per_get_few_bits(&tpd, nbits - 24) << 24;
71 accum |= per_get_few_bits(&tpd, 24);
72 } else {
Lev Walkin59b176e2005-11-26 11:25:14 +000073 pd->nboff -= nbits; /* Oops, revert back */
74 return -1;
75 }
76
Lev Walkin60364882005-11-28 06:58:11 +000077 return (accum & (((uint32_t)1 << nbits) - 1));
Lev Walkin59b176e2005-11-26 11:25:14 +000078}
79
80/*
81 * Extract a large number of bits from the specified PER data pointer.
82 */
83int
84per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int alright, int nbits) {
85 int32_t value;
86
87 if(alright && (nbits & 7)) {
88 /* Perform right alignment of a first few bits */
89 value = per_get_few_bits(pd, nbits & 0x07);
90 if(value < 0) return -1;
91 *dst++ = value; /* value is already right-aligned */
92 nbits &= ~7;
93 }
94
95 while(nbits) {
96 if(nbits >= 24) {
97 value = per_get_few_bits(pd, 24);
98 if(value < 0) return -1;
99 *(dst++) = value >> 16;
100 *(dst++) = value >> 8;
101 *(dst++) = value;
102 nbits -= 24;
103 } else {
104 value = per_get_few_bits(pd, nbits);
105 if(value < 0) return -1;
Lev Walkin523de9e2006-08-18 01:34:18 +0000106 if(nbits & 7) { /* implies left alignment */
Lev Walkin59b176e2005-11-26 11:25:14 +0000107 value <<= 8 - (nbits & 7),
108 nbits += 8 - (nbits & 7);
109 if(nbits > 24)
110 *dst++ = value >> 24;
111 }
112 if(nbits > 16)
113 *dst++ = value >> 16;
114 if(nbits > 8)
115 *dst++ = value >> 8;
116 *dst++ = value;
117 break;
118 }
119 }
120
121 return 0;
122}
123
124/*
125 * Get the length "n" from the stream.
126 */
127ssize_t
128uper_get_length(asn_per_data_t *pd, int ebits, int *repeat) {
129 ssize_t value;
130
131 *repeat = 0;
132
133 if(ebits >= 0) return per_get_few_bits(pd, ebits);
134
135 value = per_get_few_bits(pd, 8);
136 if(value < 0) return -1;
137 if((value & 128) == 0) /* #10.9.3.6 */
138 return (value & 0x7F);
139 if((value & 64) == 0) { /* #10.9.3.7 */
140 value = ((value & 63) << 8) | per_get_few_bits(pd, 8);
141 if(value < 0) return -1;
142 return value;
143 }
144 value &= 63; /* this is "m" from X.691, #10.9.3.8 */
145 if(value < 1 || value > 4)
146 return -1;
147 *repeat = 1;
148 return (16384 * value);
149}
150
151/*
Lev Walkin5b78e1c2007-06-24 06:26:47 +0000152 * Get the normally small length "n".
153 * This procedure used to decode length of extensions bit-maps
154 * for SET and SEQUENCE types.
155 */
156ssize_t
157uper_get_nslength(asn_per_data_t *pd) {
158 ssize_t length;
159
160 if(per_get_few_bits(pd, 1) == 0) {
161 ASN_DEBUG("l=?");
162 length = per_get_few_bits(pd, 6);
163 ASN_DEBUG("l=%d", length);
164 if(length < 0) return -1;
165 return length + 1;
166 } else {
167 int repeat;
168 length = uper_get_length(pd, -1, &repeat);
169 if(length >= 0 && !repeat) return length;
170 return -1; /* Error, or do not support >16K extensions */
171 }
172}
173
174/*
Lev Walkin59b176e2005-11-26 11:25:14 +0000175 * Get the normally small non-negative whole number.
176 * X.691, #10.6
177 */
178ssize_t
179uper_get_nsnnwn(asn_per_data_t *pd) {
180 ssize_t value;
181
182 value = per_get_few_bits(pd, 7);
183 if(value & 64) { /* implicit (value < 0) */
184 value &= 63;
185 value <<= 2;
186 value |= per_get_few_bits(pd, 2);
187 if(value & 128) /* implicit (value < 0) */
188 return -1;
189 if(value == 0)
190 return 0;
191 if(value >= 3)
192 return -1;
193 value = per_get_few_bits(pd, 8 * value);
194 return value;
195 }
196
197 return value;
198}
Lev Walkin523de9e2006-08-18 01:34:18 +0000199
200/*
201 * Put the normally small non-negative whole number.
202 * X.691, #10.6
203 */
204int
205uper_put_nsnnwn(asn_per_outp_t *po, int n) {
206 int bytes;
207
208 if(n <= 63) {
209 if(n < 0) return -1;
210 return per_put_few_bits(po, n, 7);
211 }
212 if(n < 256)
213 bytes = 1;
214 else if(n < 65536)
215 bytes = 2;
216 else if(n < 256 * 65536)
217 bytes = 3;
218 else
219 return -1; /* This is not a "normally small" value */
220 if(per_put_few_bits(po, bytes, 8))
221 return -1;
222
223 return per_put_few_bits(po, n, 8 * bytes);
224}
225
226
227/*
228 * Put a small number of bits (<= 31).
229 */
230int
231per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) {
232 size_t off; /* Next after last bit offset */
233 size_t omsk; /* Existing last byte meaningful bits mask */
234 uint8_t *buf;
235
236 if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
237
Lev Walkin8b8381f2006-09-28 07:45:14 +0000238 ASN_DEBUG("[PER put %d bits %x to %p+%d bits]",
239 obits, bits, po->buffer, po->nboff);
Lev Walkin0a8aa602006-09-18 20:05:55 +0000240
Lev Walkin523de9e2006-08-18 01:34:18 +0000241 /*
242 * Normalize position indicator.
243 */
244 if(po->nboff >= 8) {
245 po->buffer += (po->nboff >> 3);
246 po->nbits -= (po->nboff & ~0x07);
247 po->nboff &= 0x07;
248 }
249
250 /*
251 * Flush whole-bytes output, if necessary.
252 */
253 if(po->nboff + obits > po->nbits) {
254 int complete_bytes = (po->buffer - po->tmpspace);
Lev Walkin8b8381f2006-09-28 07:45:14 +0000255 ASN_DEBUG("[PER output %d complete + %d]",
256 complete_bytes, po->flushed_bytes);
257 if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0)
Lev Walkin523de9e2006-08-18 01:34:18 +0000258 return -1;
259 if(po->nboff)
260 po->tmpspace[0] = po->buffer[0];
261 po->buffer = po->tmpspace;
262 po->nbits = 8 * sizeof(po->tmpspace);
Lev Walkinbc691772006-09-17 11:02:53 +0000263 po->flushed_bytes += complete_bytes;
Lev Walkin523de9e2006-08-18 01:34:18 +0000264 }
265
266 /*
267 * Now, due to sizeof(tmpspace), we are guaranteed large enough space.
268 */
269 buf = po->buffer;
270 omsk = ~((1 << (8 - po->nboff)) - 1);
271 off = (po->nboff += obits);
272
273 /* Clear data of debris before meaningful bits */
274 bits &= (((uint32_t)1 << obits) - 1);
275
276 ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits, bits, bits,
277 po->nboff - obits, off, buf[0], omsk&0xff, buf[0] & omsk);
278
279 if(off <= 8) /* Completely within 1 byte */
280 bits <<= (8 - off),
281 buf[0] = (buf[0] & omsk) | bits;
282 else if(off <= 16)
283 bits <<= (16 - off),
284 buf[0] = (buf[0] & omsk) | (bits >> 8),
285 buf[1] = bits;
286 else if(off <= 24)
287 bits <<= (24 - off),
288 buf[0] = (buf[0] & omsk) | (bits >> 16),
289 buf[1] = bits >> 8,
290 buf[2] = bits;
291 else if(off <= 31)
292 bits <<= (32 - off),
293 buf[0] = (buf[0] & omsk) | (bits >> 24),
294 buf[1] = bits >> 16,
295 buf[2] = bits >> 8,
296 buf[3] = bits;
297 else {
298 ASN_DEBUG("->[PER out split %d]", obits);
299 per_put_few_bits(po, bits >> 8, 24);
300 per_put_few_bits(po, bits, obits - 24);
301 ASN_DEBUG("<-[PER out split %d]", obits);
302 }
303
304 ASN_DEBUG("[PER out %u/%x => %02x buf+%d]",
305 bits, bits, buf[0], po->buffer - po->tmpspace);
306
307 return 0;
308}
309
310
311/*
312 * Output a large number of bits.
313 */
314int
315per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) {
316
317 while(nbits) {
318 uint32_t value;
319
320 if(nbits >= 24) {
321 value = (src[0] << 16) | (src[1] << 8) | src[2];
322 src += 3;
323 nbits -= 24;
324 if(per_put_few_bits(po, value, 24))
325 return -1;
326 } else {
327 value = src[0];
328 if(nbits > 8)
329 value = (value << 8) | src[1];
330 if(nbits > 16)
331 value = (value << 8) | src[2];
332 if(nbits & 0x07)
333 value >>= (8 - (nbits & 0x07));
334 if(per_put_few_bits(po, value, nbits))
335 return -1;
336 break;
337 }
338 }
339
340 return 0;
341}
342
343/*
344 * Put the length "n" (or part of it) into the stream.
345 */
346ssize_t
347uper_put_length(asn_per_outp_t *po, size_t length) {
348
349 if(length <= 127) /* #10.9.3.6 */
350 return per_put_few_bits(po, length, 8)
351 ? -1 : (ssize_t)length;
352 else if(length < 16384) /* #10.9.3.7 */
353 return per_put_few_bits(po, length|0x8000, 16)
354 ? -1 : (ssize_t)length;
355
356 length >>= 14;
357 if(length > 4) length = 4;
358
359 return per_put_few_bits(po, 0xC0 | length, 8)
360 ? -1 : (ssize_t)(length << 14);
361}
362
Lev Walkin62258e22007-06-23 23:50:25 +0000363
364/*
365 * Put the normally small length "n" into the stream.
366 * This procedure used to encode length of extensions bit-maps
367 * for SET and SEQUENCE types.
368 */
369int
370uper_put_nslength(asn_per_outp_t *po, size_t length) {
371
372 if(length <= 64) {
373 /* #10.9.3.4 */
374 if(length == 0) return -1;
375 return per_put_few_bits(po, length-1, 7) ? -1 : 0;
376 } else {
377 if(uper_put_length(po, length) != length) {
378 /* This might happen in case of >16K extensions */
379 return -1;
380 }
381 }
382
383 return 0;
384}
385