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