blob: c9c9594b02a3dd99f6b6b7d02cbd5734c32bcd57 [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
Lev Walkind00657f2007-06-26 02:51:10 +00009void
10per_get_undo(asn_per_data_t *pd, int nbits) {
Lev Walkin5ce11fd2007-06-26 03:16:35 +000011 if((ssize_t)pd->nboff < nbits) {
12 assert((ssize_t)pd->nboff < nbits);
Lev Walkind00657f2007-06-26 02:51:10 +000013 } else {
14 pd->nboff -= nbits;
15 pd->moved -= nbits;
16 }
17}
18
Lev Walkin59b176e2005-11-26 11:25:14 +000019/*
Lev Walkin60364882005-11-28 06:58:11 +000020 * Extract a small number of bits (<= 31) from the specified PER data pointer.
Lev Walkin59b176e2005-11-26 11:25:14 +000021 */
22int32_t
23per_get_few_bits(asn_per_data_t *pd, int nbits) {
24 size_t off; /* Next after last bit offset */
Lev Walkind00657f2007-06-26 02:51:10 +000025 ssize_t nleft; /* Number of bits left in this stream */
Lev Walkin59b176e2005-11-26 11:25:14 +000026 uint32_t accum;
Lev Walkin1d9e8dd2005-12-07 05:46:03 +000027 const uint8_t *buf;
Lev Walkin59b176e2005-11-26 11:25:14 +000028
Lev Walkin5b78e1c2007-06-24 06:26:47 +000029 if(nbits < 0)
Lev Walkin59b176e2005-11-26 11:25:14 +000030 return -1;
Lev Walkin59b176e2005-11-26 11:25:14 +000031
Lev Walkin5b78e1c2007-06-24 06:26:47 +000032 nleft = pd->nbits - pd->nboff;
33 if(nbits > nleft) {
34 int32_t tailv, vhead;
35 if(!pd->refill || nbits > 31) return -1;
36 /* Accumulate unused bytes before refill */
37 ASN_DEBUG("Obtain the rest %d bits", nleft);
38 tailv = per_get_few_bits(pd, nleft);
39 if(tailv < 0) return -1;
40 /* Refill (replace pd contents with new data) */
41 if(pd->refill(pd))
42 return -1;
43 nbits -= nleft;
44 vhead = per_get_few_bits(pd, nbits);
45 /* Combine the rest of previous pd with the head of new one */
46 tailv = (tailv << nbits) | vhead; /* Could == -1 */
47 return tailv;
48 }
49
50 ASN_DEBUG("[PER get %d bits from %p+%d bits, %d available]",
51 nbits, pd->buffer, pd->nboff, nleft);
Lev Walkin0a8aa602006-09-18 20:05:55 +000052
Lev Walkin59b176e2005-11-26 11:25:14 +000053 /*
54 * Normalize position indicator.
55 */
56 if(pd->nboff >= 8) {
57 pd->buffer += (pd->nboff >> 3);
58 pd->nbits -= (pd->nboff & ~0x07);
59 pd->nboff &= 0x07;
60 }
Lev Walkind00657f2007-06-26 02:51:10 +000061 pd->moved += nbits;
62 pd->nboff += nbits;
63 off = pd->nboff;
Lev Walkin59b176e2005-11-26 11:25:14 +000064 buf = pd->buffer;
65
66 /*
67 * Extract specified number of bits.
68 */
69 if(off <= 8)
Lev Walkin60364882005-11-28 06:58:11 +000070 accum = nbits ? (buf[0]) >> (8 - off) : 0;
Lev Walkin59b176e2005-11-26 11:25:14 +000071 else if(off <= 16)
72 accum = ((buf[0] << 8) + buf[1]) >> (16 - off);
73 else if(off <= 24)
74 accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off);
75 else if(off <= 31)
76 accum = ((buf[0] << 24) + (buf[1] << 16)
77 + (buf[2] << 8) + (buf[3])) >> (32 - off);
Lev Walkin60364882005-11-28 06:58:11 +000078 else if(nbits <= 31) {
79 asn_per_data_t tpd = *pd;
80 /* Here are we with our 31-bits limit plus 1..7 bits offset. */
Lev Walkind00657f2007-06-26 02:51:10 +000081 per_get_undo(&tpd, nbits);
82 /* The number of available bits in the stream allow
83 * for the following operations to take place without
84 * invoking the ->refill() function */
Lev Walkin60364882005-11-28 06:58:11 +000085 accum = per_get_few_bits(&tpd, nbits - 24) << 24;
86 accum |= per_get_few_bits(&tpd, 24);
87 } else {
Lev Walkind00657f2007-06-26 02:51:10 +000088 per_get_undo(pd, nbits);
Lev Walkin59b176e2005-11-26 11:25:14 +000089 return -1;
90 }
91
Lev Walkin60364882005-11-28 06:58:11 +000092 return (accum & (((uint32_t)1 << nbits) - 1));
Lev Walkin59b176e2005-11-26 11:25:14 +000093}
94
95/*
96 * Extract a large number of bits from the specified PER data pointer.
97 */
98int
99per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int alright, int nbits) {
100 int32_t value;
101
102 if(alright && (nbits & 7)) {
103 /* Perform right alignment of a first few bits */
104 value = per_get_few_bits(pd, nbits & 0x07);
105 if(value < 0) return -1;
106 *dst++ = value; /* value is already right-aligned */
107 nbits &= ~7;
108 }
109
110 while(nbits) {
111 if(nbits >= 24) {
112 value = per_get_few_bits(pd, 24);
113 if(value < 0) return -1;
114 *(dst++) = value >> 16;
115 *(dst++) = value >> 8;
116 *(dst++) = value;
117 nbits -= 24;
118 } else {
119 value = per_get_few_bits(pd, nbits);
120 if(value < 0) return -1;
Lev Walkin523de9e2006-08-18 01:34:18 +0000121 if(nbits & 7) { /* implies left alignment */
Lev Walkin59b176e2005-11-26 11:25:14 +0000122 value <<= 8 - (nbits & 7),
123 nbits += 8 - (nbits & 7);
124 if(nbits > 24)
125 *dst++ = value >> 24;
126 }
127 if(nbits > 16)
128 *dst++ = value >> 16;
129 if(nbits > 8)
130 *dst++ = value >> 8;
131 *dst++ = value;
132 break;
133 }
134 }
135
136 return 0;
137}
138
139/*
140 * Get the length "n" from the stream.
141 */
142ssize_t
143uper_get_length(asn_per_data_t *pd, int ebits, int *repeat) {
144 ssize_t value;
145
146 *repeat = 0;
147
148 if(ebits >= 0) return per_get_few_bits(pd, ebits);
149
150 value = per_get_few_bits(pd, 8);
151 if(value < 0) return -1;
152 if((value & 128) == 0) /* #10.9.3.6 */
153 return (value & 0x7F);
154 if((value & 64) == 0) { /* #10.9.3.7 */
155 value = ((value & 63) << 8) | per_get_few_bits(pd, 8);
156 if(value < 0) return -1;
157 return value;
158 }
159 value &= 63; /* this is "m" from X.691, #10.9.3.8 */
160 if(value < 1 || value > 4)
161 return -1;
162 *repeat = 1;
163 return (16384 * value);
164}
165
166/*
Lev Walkin5b78e1c2007-06-24 06:26:47 +0000167 * Get the normally small length "n".
168 * This procedure used to decode length of extensions bit-maps
169 * for SET and SEQUENCE types.
170 */
171ssize_t
172uper_get_nslength(asn_per_data_t *pd) {
173 ssize_t length;
174
175 if(per_get_few_bits(pd, 1) == 0) {
176 ASN_DEBUG("l=?");
177 length = per_get_few_bits(pd, 6);
178 ASN_DEBUG("l=%d", length);
179 if(length < 0) return -1;
180 return length + 1;
181 } else {
182 int repeat;
183 length = uper_get_length(pd, -1, &repeat);
184 if(length >= 0 && !repeat) return length;
185 return -1; /* Error, or do not support >16K extensions */
186 }
187}
188
189/*
Lev Walkin59b176e2005-11-26 11:25:14 +0000190 * Get the normally small non-negative whole number.
191 * X.691, #10.6
192 */
193ssize_t
194uper_get_nsnnwn(asn_per_data_t *pd) {
195 ssize_t value;
196
197 value = per_get_few_bits(pd, 7);
198 if(value & 64) { /* implicit (value < 0) */
199 value &= 63;
200 value <<= 2;
201 value |= per_get_few_bits(pd, 2);
202 if(value & 128) /* implicit (value < 0) */
203 return -1;
204 if(value == 0)
205 return 0;
206 if(value >= 3)
207 return -1;
208 value = per_get_few_bits(pd, 8 * value);
209 return value;
210 }
211
212 return value;
213}
Lev Walkin523de9e2006-08-18 01:34:18 +0000214
215/*
216 * Put the normally small non-negative whole number.
217 * X.691, #10.6
218 */
219int
220uper_put_nsnnwn(asn_per_outp_t *po, int n) {
221 int bytes;
222
223 if(n <= 63) {
224 if(n < 0) return -1;
225 return per_put_few_bits(po, n, 7);
226 }
227 if(n < 256)
228 bytes = 1;
229 else if(n < 65536)
230 bytes = 2;
231 else if(n < 256 * 65536)
232 bytes = 3;
233 else
234 return -1; /* This is not a "normally small" value */
235 if(per_put_few_bits(po, bytes, 8))
236 return -1;
237
238 return per_put_few_bits(po, n, 8 * bytes);
239}
240
241
242/*
243 * Put a small number of bits (<= 31).
244 */
245int
246per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) {
247 size_t off; /* Next after last bit offset */
248 size_t omsk; /* Existing last byte meaningful bits mask */
249 uint8_t *buf;
250
251 if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
252
Lev Walkin8b8381f2006-09-28 07:45:14 +0000253 ASN_DEBUG("[PER put %d bits %x to %p+%d bits]",
254 obits, bits, po->buffer, po->nboff);
Lev Walkin0a8aa602006-09-18 20:05:55 +0000255
Lev Walkin523de9e2006-08-18 01:34:18 +0000256 /*
257 * Normalize position indicator.
258 */
259 if(po->nboff >= 8) {
260 po->buffer += (po->nboff >> 3);
261 po->nbits -= (po->nboff & ~0x07);
262 po->nboff &= 0x07;
263 }
264
265 /*
266 * Flush whole-bytes output, if necessary.
267 */
268 if(po->nboff + obits > po->nbits) {
269 int complete_bytes = (po->buffer - po->tmpspace);
Lev Walkin8b8381f2006-09-28 07:45:14 +0000270 ASN_DEBUG("[PER output %d complete + %d]",
271 complete_bytes, po->flushed_bytes);
272 if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0)
Lev Walkin523de9e2006-08-18 01:34:18 +0000273 return -1;
274 if(po->nboff)
275 po->tmpspace[0] = po->buffer[0];
276 po->buffer = po->tmpspace;
277 po->nbits = 8 * sizeof(po->tmpspace);
Lev Walkinbc691772006-09-17 11:02:53 +0000278 po->flushed_bytes += complete_bytes;
Lev Walkin523de9e2006-08-18 01:34:18 +0000279 }
280
281 /*
282 * Now, due to sizeof(tmpspace), we are guaranteed large enough space.
283 */
284 buf = po->buffer;
285 omsk = ~((1 << (8 - po->nboff)) - 1);
286 off = (po->nboff += obits);
287
288 /* Clear data of debris before meaningful bits */
289 bits &= (((uint32_t)1 << obits) - 1);
290
291 ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits, bits, bits,
292 po->nboff - obits, off, buf[0], omsk&0xff, buf[0] & omsk);
293
294 if(off <= 8) /* Completely within 1 byte */
295 bits <<= (8 - off),
296 buf[0] = (buf[0] & omsk) | bits;
297 else if(off <= 16)
298 bits <<= (16 - off),
299 buf[0] = (buf[0] & omsk) | (bits >> 8),
300 buf[1] = bits;
301 else if(off <= 24)
302 bits <<= (24 - off),
303 buf[0] = (buf[0] & omsk) | (bits >> 16),
304 buf[1] = bits >> 8,
305 buf[2] = bits;
306 else if(off <= 31)
307 bits <<= (32 - off),
308 buf[0] = (buf[0] & omsk) | (bits >> 24),
309 buf[1] = bits >> 16,
310 buf[2] = bits >> 8,
311 buf[3] = bits;
312 else {
313 ASN_DEBUG("->[PER out split %d]", obits);
314 per_put_few_bits(po, bits >> 8, 24);
315 per_put_few_bits(po, bits, obits - 24);
316 ASN_DEBUG("<-[PER out split %d]", obits);
317 }
318
319 ASN_DEBUG("[PER out %u/%x => %02x buf+%d]",
320 bits, bits, buf[0], po->buffer - po->tmpspace);
321
322 return 0;
323}
324
325
326/*
327 * Output a large number of bits.
328 */
329int
330per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) {
331
332 while(nbits) {
333 uint32_t value;
334
335 if(nbits >= 24) {
336 value = (src[0] << 16) | (src[1] << 8) | src[2];
337 src += 3;
338 nbits -= 24;
339 if(per_put_few_bits(po, value, 24))
340 return -1;
341 } else {
342 value = src[0];
343 if(nbits > 8)
344 value = (value << 8) | src[1];
345 if(nbits > 16)
346 value = (value << 8) | src[2];
347 if(nbits & 0x07)
348 value >>= (8 - (nbits & 0x07));
349 if(per_put_few_bits(po, value, nbits))
350 return -1;
351 break;
352 }
353 }
354
355 return 0;
356}
357
358/*
359 * Put the length "n" (or part of it) into the stream.
360 */
361ssize_t
362uper_put_length(asn_per_outp_t *po, size_t length) {
363
364 if(length <= 127) /* #10.9.3.6 */
365 return per_put_few_bits(po, length, 8)
366 ? -1 : (ssize_t)length;
367 else if(length < 16384) /* #10.9.3.7 */
368 return per_put_few_bits(po, length|0x8000, 16)
369 ? -1 : (ssize_t)length;
370
371 length >>= 14;
372 if(length > 4) length = 4;
373
374 return per_put_few_bits(po, 0xC0 | length, 8)
375 ? -1 : (ssize_t)(length << 14);
376}
377
Lev Walkin62258e22007-06-23 23:50:25 +0000378
379/*
380 * Put the normally small length "n" into the stream.
381 * This procedure used to encode length of extensions bit-maps
382 * for SET and SEQUENCE types.
383 */
384int
385uper_put_nslength(asn_per_outp_t *po, size_t length) {
386
387 if(length <= 64) {
388 /* #10.9.3.4 */
389 if(length == 0) return -1;
390 return per_put_few_bits(po, length-1, 7) ? -1 : 0;
391 } else {
Lev Walkin5ce11fd2007-06-26 03:16:35 +0000392 if(uper_put_length(po, length) != (ssize_t)length) {
Lev Walkin62258e22007-06-23 23:50:25 +0000393 /* This might happen in case of >16K extensions */
394 return -1;
395 }
396 }
397
398 return 0;
399}
400