blob: 0eb381c8ab4068f59c4dd9ba6b9a734bdcfd1e05 [file] [log] [blame]
vlm337167e2005-11-26 11:25:14 +00001/*
vlm18dd82c2006-08-18 01:34:18 +00002 * Copyright (c) 2005, 2006 Lev Walkin <vlm@lionet.info>. All rights reserved.
vlm337167e2005-11-26 11:25:14 +00003 * Redistribution and modifications are permitted subject to BSD license.
4 */
5#include <asn_system.h>
6#include <per_support.h>
7
8/*
vlm17af7cc2005-11-28 06:58:11 +00009 * Extract a small number of bits (<= 31) from the specified PER data pointer.
vlm337167e2005-11-26 11:25:14 +000010 */
11int32_t
12per_get_few_bits(asn_per_data_t *pd, int nbits) {
13 size_t off; /* Next after last bit offset */
14 uint32_t accum;
vlm4d2ca122005-12-07 05:46:03 +000015 const uint8_t *buf;
vlm337167e2005-11-26 11:25:14 +000016
17 if(nbits < 0 || pd->nboff + nbits > pd->nbits)
18 return -1;
vlm337167e2005-11-26 11:25:14 +000019
20 /*
21 * Normalize position indicator.
22 */
23 if(pd->nboff >= 8) {
24 pd->buffer += (pd->nboff >> 3);
25 pd->nbits -= (pd->nboff & ~0x07);
26 pd->nboff &= 0x07;
27 }
28 off = (pd->nboff += nbits);
29 buf = pd->buffer;
30
31 /*
32 * Extract specified number of bits.
33 */
34 if(off <= 8)
vlm17af7cc2005-11-28 06:58:11 +000035 accum = nbits ? (buf[0]) >> (8 - off) : 0;
vlm337167e2005-11-26 11:25:14 +000036 else if(off <= 16)
37 accum = ((buf[0] << 8) + buf[1]) >> (16 - off);
38 else if(off <= 24)
39 accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off);
40 else if(off <= 31)
41 accum = ((buf[0] << 24) + (buf[1] << 16)
42 + (buf[2] << 8) + (buf[3])) >> (32 - off);
vlm17af7cc2005-11-28 06:58:11 +000043 else if(nbits <= 31) {
44 asn_per_data_t tpd = *pd;
45 /* Here are we with our 31-bits limit plus 1..7 bits offset. */
46 tpd.nboff -= nbits;
47 accum = per_get_few_bits(&tpd, nbits - 24) << 24;
48 accum |= per_get_few_bits(&tpd, 24);
49 } else {
vlm337167e2005-11-26 11:25:14 +000050 pd->nboff -= nbits; /* Oops, revert back */
51 return -1;
52 }
53
vlm17af7cc2005-11-28 06:58:11 +000054 return (accum & (((uint32_t)1 << nbits) - 1));
vlm337167e2005-11-26 11:25:14 +000055}
56
57/*
58 * Extract a large number of bits from the specified PER data pointer.
59 */
60int
61per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int alright, int nbits) {
62 int32_t value;
63
64 if(alright && (nbits & 7)) {
65 /* Perform right alignment of a first few bits */
66 value = per_get_few_bits(pd, nbits & 0x07);
67 if(value < 0) return -1;
68 *dst++ = value; /* value is already right-aligned */
69 nbits &= ~7;
70 }
71
72 while(nbits) {
73 if(nbits >= 24) {
74 value = per_get_few_bits(pd, 24);
75 if(value < 0) return -1;
76 *(dst++) = value >> 16;
77 *(dst++) = value >> 8;
78 *(dst++) = value;
79 nbits -= 24;
80 } else {
81 value = per_get_few_bits(pd, nbits);
82 if(value < 0) return -1;
vlm18dd82c2006-08-18 01:34:18 +000083 if(nbits & 7) { /* implies left alignment */
vlm337167e2005-11-26 11:25:14 +000084 value <<= 8 - (nbits & 7),
85 nbits += 8 - (nbits & 7);
86 if(nbits > 24)
87 *dst++ = value >> 24;
88 }
89 if(nbits > 16)
90 *dst++ = value >> 16;
91 if(nbits > 8)
92 *dst++ = value >> 8;
93 *dst++ = value;
94 break;
95 }
96 }
97
98 return 0;
99}
100
101/*
102 * Get the length "n" from the stream.
103 */
104ssize_t
105uper_get_length(asn_per_data_t *pd, int ebits, int *repeat) {
106 ssize_t value;
107
108 *repeat = 0;
109
110 if(ebits >= 0) return per_get_few_bits(pd, ebits);
111
112 value = per_get_few_bits(pd, 8);
113 if(value < 0) return -1;
114 if((value & 128) == 0) /* #10.9.3.6 */
115 return (value & 0x7F);
116 if((value & 64) == 0) { /* #10.9.3.7 */
117 value = ((value & 63) << 8) | per_get_few_bits(pd, 8);
118 if(value < 0) return -1;
119 return value;
120 }
121 value &= 63; /* this is "m" from X.691, #10.9.3.8 */
122 if(value < 1 || value > 4)
123 return -1;
124 *repeat = 1;
125 return (16384 * value);
126}
127
128/*
129 * Get the normally small non-negative whole number.
130 * X.691, #10.6
131 */
132ssize_t
133uper_get_nsnnwn(asn_per_data_t *pd) {
134 ssize_t value;
135
136 value = per_get_few_bits(pd, 7);
137 if(value & 64) { /* implicit (value < 0) */
138 value &= 63;
139 value <<= 2;
140 value |= per_get_few_bits(pd, 2);
141 if(value & 128) /* implicit (value < 0) */
142 return -1;
143 if(value == 0)
144 return 0;
145 if(value >= 3)
146 return -1;
147 value = per_get_few_bits(pd, 8 * value);
148 return value;
149 }
150
151 return value;
152}
vlm18dd82c2006-08-18 01:34:18 +0000153
154/*
155 * Put the normally small non-negative whole number.
156 * X.691, #10.6
157 */
158int
159uper_put_nsnnwn(asn_per_outp_t *po, int n) {
160 int bytes;
161
162 if(n <= 63) {
163 if(n < 0) return -1;
164 return per_put_few_bits(po, n, 7);
165 }
166 if(n < 256)
167 bytes = 1;
168 else if(n < 65536)
169 bytes = 2;
170 else if(n < 256 * 65536)
171 bytes = 3;
172 else
173 return -1; /* This is not a "normally small" value */
174 if(per_put_few_bits(po, bytes, 8))
175 return -1;
176
177 return per_put_few_bits(po, n, 8 * bytes);
178}
179
180
181/*
182 * Put a small number of bits (<= 31).
183 */
184int
185per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) {
186 size_t off; /* Next after last bit offset */
187 size_t omsk; /* Existing last byte meaningful bits mask */
188 uint8_t *buf;
189
190 if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
191
192 /*
193 * Normalize position indicator.
194 */
195 if(po->nboff >= 8) {
196 po->buffer += (po->nboff >> 3);
197 po->nbits -= (po->nboff & ~0x07);
198 po->nboff &= 0x07;
199 }
200
201 /*
202 * Flush whole-bytes output, if necessary.
203 */
204 if(po->nboff + obits > po->nbits) {
205 int complete_bytes = (po->buffer - po->tmpspace);
206 if(po->outper(po->buffer, complete_bytes, po->op_key) < 0)
207 return -1;
208 if(po->nboff)
209 po->tmpspace[0] = po->buffer[0];
210 po->buffer = po->tmpspace;
211 po->nbits = 8 * sizeof(po->tmpspace);
212 }
213
214 /*
215 * Now, due to sizeof(tmpspace), we are guaranteed large enough space.
216 */
217 buf = po->buffer;
218 omsk = ~((1 << (8 - po->nboff)) - 1);
219 off = (po->nboff += obits);
220
221 /* Clear data of debris before meaningful bits */
222 bits &= (((uint32_t)1 << obits) - 1);
223
224 ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits, bits, bits,
225 po->nboff - obits, off, buf[0], omsk&0xff, buf[0] & omsk);
226
227 if(off <= 8) /* Completely within 1 byte */
228 bits <<= (8 - off),
229 buf[0] = (buf[0] & omsk) | bits;
230 else if(off <= 16)
231 bits <<= (16 - off),
232 buf[0] = (buf[0] & omsk) | (bits >> 8),
233 buf[1] = bits;
234 else if(off <= 24)
235 bits <<= (24 - off),
236 buf[0] = (buf[0] & omsk) | (bits >> 16),
237 buf[1] = bits >> 8,
238 buf[2] = bits;
239 else if(off <= 31)
240 bits <<= (32 - off),
241 buf[0] = (buf[0] & omsk) | (bits >> 24),
242 buf[1] = bits >> 16,
243 buf[2] = bits >> 8,
244 buf[3] = bits;
245 else {
246 ASN_DEBUG("->[PER out split %d]", obits);
247 per_put_few_bits(po, bits >> 8, 24);
248 per_put_few_bits(po, bits, obits - 24);
249 ASN_DEBUG("<-[PER out split %d]", obits);
250 }
251
252 ASN_DEBUG("[PER out %u/%x => %02x buf+%d]",
253 bits, bits, buf[0], po->buffer - po->tmpspace);
254
255 return 0;
256}
257
258
259/*
260 * Output a large number of bits.
261 */
262int
263per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) {
264
265 while(nbits) {
266 uint32_t value;
267
268 if(nbits >= 24) {
269 value = (src[0] << 16) | (src[1] << 8) | src[2];
270 src += 3;
271 nbits -= 24;
272 if(per_put_few_bits(po, value, 24))
273 return -1;
274 } else {
275 value = src[0];
276 if(nbits > 8)
277 value = (value << 8) | src[1];
278 if(nbits > 16)
279 value = (value << 8) | src[2];
280 if(nbits & 0x07)
281 value >>= (8 - (nbits & 0x07));
282 if(per_put_few_bits(po, value, nbits))
283 return -1;
284 break;
285 }
286 }
287
288 return 0;
289}
290
291/*
292 * Put the length "n" (or part of it) into the stream.
293 */
294ssize_t
295uper_put_length(asn_per_outp_t *po, size_t length) {
296
297 if(length <= 127) /* #10.9.3.6 */
298 return per_put_few_bits(po, length, 8)
299 ? -1 : (ssize_t)length;
300 else if(length < 16384) /* #10.9.3.7 */
301 return per_put_few_bits(po, length|0x8000, 16)
302 ? -1 : (ssize_t)length;
303
304 length >>= 14;
305 if(length > 4) length = 4;
306
307 return per_put_few_bits(po, 0xC0 | length, 8)
308 ? -1 : (ssize_t)(length << 14);
309}
310