2 * Copyright (c) 2005, 2006, 2007 Lev Walkin <vlm@lionet.info>.
4 * Redistribution and modifications are permitted subject to BSD license.
6 #include <asn_system.h>
7 #include <asn_internal.h>
8 #include <per_support.h>
11 per_data_string(asn_per_data_t
*pd
) {
12 static char buf
[2][32];
15 snprintf(buf
[n
], sizeof(buf
),
16 "{m=%ld span %+ld[%d..%d] (%d)}",
18 (((long)pd
->buffer
) & 0xf),
19 (int)pd
->nboff
, (int)pd
->nbits
,
20 (int)(pd
->nbits
- pd
->nboff
));
25 per_get_undo(asn_per_data_t
*pd
, int nbits
) {
26 if((ssize_t
)pd
->nboff
< nbits
) {
27 assert((ssize_t
)pd
->nboff
< nbits
);
35 * Extract a small number of bits (<= 31) from the specified PER data pointer.
38 per_get_few_bits(asn_per_data_t
*pd
, int nbits
) {
39 size_t off
; /* Next after last bit offset */
40 ssize_t nleft
; /* Number of bits left in this stream */
47 nleft
= pd
->nbits
- pd
->nboff
;
50 if(!pd
->refill
|| nbits
> 31) return -1;
51 /* Accumulate unused bytes before refill */
52 ASN_DEBUG("Obtain the rest %d bits (want %d)",
53 (int)nleft
, (int)nbits
);
54 tailv
= per_get_few_bits(pd
, nleft
);
55 if(tailv
< 0) return -1;
56 /* Refill (replace pd contents with new data) */
60 vhead
= per_get_few_bits(pd
, nbits
);
61 /* Combine the rest of previous pd with the head of new one */
62 tailv
= (tailv
<< nbits
) | vhead
; /* Could == -1 */
67 * Normalize position indicator.
70 pd
->buffer
+= (pd
->nboff
>> 3);
71 pd
->nbits
-= (pd
->nboff
& ~0x07);
80 * Extract specified number of bits.
83 accum
= nbits
? (buf
[0]) >> (8 - off
) : 0;
85 accum
= ((buf
[0] << 8) + buf
[1]) >> (16 - off
);
87 accum
= ((buf
[0] << 16) + (buf
[1] << 8) + buf
[2]) >> (24 - off
);
89 accum
= ((buf
[0] << 24) + (buf
[1] << 16)
90 + (buf
[2] << 8) + (buf
[3])) >> (32 - off
);
91 else if(nbits
<= 31) {
92 asn_per_data_t tpd
= *pd
;
93 /* Here are we with our 31-bits limit plus 1..7 bits offset. */
94 per_get_undo(&tpd
, nbits
);
95 /* The number of available bits in the stream allow
96 * for the following operations to take place without
97 * invoking the ->refill() function */
98 accum
= per_get_few_bits(&tpd
, nbits
- 24) << 24;
99 accum
|= per_get_few_bits(&tpd
, 24);
101 per_get_undo(pd
, nbits
);
105 accum
&= (((uint32_t)1 << nbits
) - 1);
107 ASN_DEBUG(" [PER got %2d<=%2d bits => span %d %+ld[%d..%d]:%02x (%d) => 0x%x]",
108 (int)nbits
, (int)nleft
,
110 (((long)pd
->buffer
) & 0xf),
111 (int)pd
->nboff
, (int)pd
->nbits
,
113 (int)(pd
->nbits
- pd
->nboff
),
120 * Extract a large number of bits from the specified PER data pointer.
123 per_get_many_bits(asn_per_data_t
*pd
, uint8_t *dst
, int alright
, int nbits
) {
126 if(alright
&& (nbits
& 7)) {
127 /* Perform right alignment of a first few bits */
128 value
= per_get_few_bits(pd
, nbits
& 0x07);
129 if(value
< 0) return -1;
130 *dst
++ = value
; /* value is already right-aligned */
136 value
= per_get_few_bits(pd
, 24);
137 if(value
< 0) return -1;
138 *(dst
++) = value
>> 16;
139 *(dst
++) = value
>> 8;
143 value
= per_get_few_bits(pd
, nbits
);
144 if(value
< 0) return -1;
145 if(nbits
& 7) { /* implies left alignment */
146 value
<<= 8 - (nbits
& 7),
147 nbits
+= 8 - (nbits
& 7);
149 *dst
++ = value
>> 24;
152 *dst
++ = value
>> 16;
164 * Get the length "n" from the stream.
167 uper_get_length(asn_per_data_t
*pd
, int ebits
, int *repeat
) {
172 if(ebits
>= 0) return per_get_few_bits(pd
, ebits
);
174 value
= per_get_few_bits(pd
, 8);
175 if(value
< 0) return -1;
176 if((value
& 128) == 0) /* #10.9.3.6 */
177 return (value
& 0x7F);
178 if((value
& 64) == 0) { /* #10.9.3.7 */
179 value
= ((value
& 63) << 8) | per_get_few_bits(pd
, 8);
180 if(value
< 0) return -1;
183 value
&= 63; /* this is "m" from X.691, #10.9.3.8 */
184 if(value
< 1 || value
> 4)
187 return (16384 * value
);
191 * Get the normally small length "n".
192 * This procedure used to decode length of extensions bit-maps
193 * for SET and SEQUENCE types.
196 uper_get_nslength(asn_per_data_t
*pd
) {
199 ASN_DEBUG("Getting normally small length");
201 if(per_get_few_bits(pd
, 1) == 0) {
202 length
= per_get_few_bits(pd
, 6) + 1;
203 if(length
<= 0) return -1;
204 ASN_DEBUG("l=%d", (int)length
);
208 length
= uper_get_length(pd
, -1, &repeat
);
209 if(length
>= 0 && !repeat
) return length
;
210 return -1; /* Error, or do not support >16K extensions */
215 * Get the normally small non-negative whole number.
219 uper_get_nsnnwn(asn_per_data_t
*pd
) {
222 value
= per_get_few_bits(pd
, 7);
223 if(value
& 64) { /* implicit (value < 0) */
226 value
|= per_get_few_bits(pd
, 2);
227 if(value
& 128) /* implicit (value < 0) */
233 value
= per_get_few_bits(pd
, 8 * value
);
241 * Put the normally small non-negative whole number.
245 uper_put_nsnnwn(asn_per_outp_t
*po
, int n
) {
250 return per_put_few_bits(po
, n
, 7);
256 else if(n
< 256 * 65536)
259 return -1; /* This is not a "normally small" value */
260 if(per_put_few_bits(po
, bytes
, 8))
263 return per_put_few_bits(po
, n
, 8 * bytes
);
268 * Put a small number of bits (<= 31).
271 per_put_few_bits(asn_per_outp_t
*po
, uint32_t bits
, int obits
) {
272 size_t off
; /* Next after last bit offset */
273 size_t omsk
; /* Existing last byte meaningful bits mask */
276 if(obits
<= 0 || obits
>= 32) return obits
? -1 : 0;
278 ASN_DEBUG("[PER put %d bits %x to %p+%d bits]",
279 obits
, (int)bits
, po
->buffer
, (int)po
->nboff
);
282 * Normalize position indicator.
285 po
->buffer
+= (po
->nboff
>> 3);
286 po
->nbits
-= (po
->nboff
& ~0x07);
291 * Flush whole-bytes output, if necessary.
293 if(po
->nboff
+ obits
> po
->nbits
) {
294 int complete_bytes
= (po
->buffer
- po
->tmpspace
);
295 ASN_DEBUG("[PER output %ld complete + %ld]",
296 (long)complete_bytes
, (long)po
->flushed_bytes
);
297 if(po
->outper(po
->tmpspace
, complete_bytes
, po
->op_key
) < 0)
300 po
->tmpspace
[0] = po
->buffer
[0];
301 po
->buffer
= po
->tmpspace
;
302 po
->nbits
= 8 * sizeof(po
->tmpspace
);
303 po
->flushed_bytes
+= complete_bytes
;
307 * Now, due to sizeof(tmpspace), we are guaranteed large enough space.
310 omsk
= ~((1 << (8 - po
->nboff
)) - 1);
311 off
= (po
->nboff
+ obits
);
313 /* Clear data of debris before meaningful bits */
314 bits
&= (((uint32_t)1 << obits
) - 1);
316 ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits
,
317 (int)bits
, (int)bits
,
318 (int)po
->nboff
, (int)off
,
319 buf
[0], (int)(omsk
&0xff),
320 (int)(buf
[0] & omsk
));
322 if(off
<= 8) /* Completely within 1 byte */
325 buf
[0] = (buf
[0] & omsk
) | bits
;
329 buf
[0] = (buf
[0] & omsk
) | (bits
>> 8),
334 buf
[0] = (buf
[0] & omsk
) | (bits
>> 16),
340 buf
[0] = (buf
[0] & omsk
) | (bits
>> 24),
345 per_put_few_bits(po
, bits
>> (obits
- 24), 24);
346 per_put_few_bits(po
, bits
, obits
- 24);
349 ASN_DEBUG("[PER out %u/%x => %02x buf+%ld]",
350 (int)bits
, (int)bits
, buf
[0],
351 (long)(po
->buffer
- po
->tmpspace
));
358 * Output a large number of bits.
361 per_put_many_bits(asn_per_outp_t
*po
, const uint8_t *src
, int nbits
) {
367 value
= (src
[0] << 16) | (src
[1] << 8) | src
[2];
370 if(per_put_few_bits(po
, value
, 24))
375 value
= (value
<< 8) | src
[1];
377 value
= (value
<< 8) | src
[2];
379 value
>>= (8 - (nbits
& 0x07));
380 if(per_put_few_bits(po
, value
, nbits
))
390 * Put the length "n" (or part of it) into the stream.
393 uper_put_length(asn_per_outp_t
*po
, size_t length
) {
395 if(length
<= 127) /* #10.9.3.6 */
396 return per_put_few_bits(po
, length
, 8)
397 ? -1 : (ssize_t
)length
;
398 else if(length
< 16384) /* #10.9.3.7 */
399 return per_put_few_bits(po
, length
|0x8000, 16)
400 ? -1 : (ssize_t
)length
;
403 if(length
> 4) length
= 4;
405 return per_put_few_bits(po
, 0xC0 | length
, 8)
406 ? -1 : (ssize_t
)(length
<< 14);
411 * Put the normally small length "n" into the stream.
412 * This procedure used to encode length of extensions bit-maps
413 * for SET and SEQUENCE types.
416 uper_put_nslength(asn_per_outp_t
*po
, size_t length
) {
420 if(length
== 0) return -1;
421 return per_put_few_bits(po
, length
-1, 7) ? -1 : 0;
423 if(uper_put_length(po
, length
) != (ssize_t
)length
) {
424 /* This might happen in case of >16K extensions */