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=%d span %+d[%d..%d] (%d)}",
18
(((int)pd->buffer) & 0xf),
20
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)", nleft, nbits);
53
tailv = per_get_few_bits(pd, nleft);
54
if(tailv < 0) return -1;
55
/* Refill (replace pd contents with new data) */
59
vhead = per_get_few_bits(pd, nbits);
60
/* Combine the rest of previous pd with the head of new one */
61
tailv = (tailv << nbits) | vhead; /* Could == -1 */
66
* Normalize position indicator.
69
pd->buffer += (pd->nboff >> 3);
70
pd->nbits -= (pd->nboff & ~0x07);
79
* Extract specified number of bits.
82
accum = nbits ? (buf[0]) >> (8 - off) : 0;
84
accum = ((buf[0] << 8) + buf[1]) >> (16 - off);
86
accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off);
88
accum = ((buf[0] << 24) + (buf[1] << 16)
89
+ (buf[2] << 8) + (buf[3])) >> (32 - off);
90
else if(nbits <= 31) {
91
asn_per_data_t tpd = *pd;
92
/* Here are we with our 31-bits limit plus 1..7 bits offset. */
93
per_get_undo(&tpd, nbits);
94
/* The number of available bits in the stream allow
95
* for the following operations to take place without
96
* invoking the ->refill() function */
97
accum = per_get_few_bits(&tpd, nbits - 24) << 24;
98
accum |= per_get_few_bits(&tpd, 24);
100
per_get_undo(pd, nbits);
104
accum &= (((uint32_t)1 << nbits) - 1);
106
ASN_DEBUG(" [PER got %2d<=%2d bits => span %d %+d[%d..%d]:%02x (%d) => 0x%x]",
109
(((int)pd->buffer) & 0xf),
110
pd->nboff, pd->nbits,
112
pd->nbits - pd->nboff,
119
* Extract a large number of bits from the specified PER data pointer.
122
per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int alright, int nbits) {
125
if(alright && (nbits & 7)) {
126
/* Perform right alignment of a first few bits */
127
value = per_get_few_bits(pd, nbits & 0x07);
128
if(value < 0) return -1;
129
*dst++ = value; /* value is already right-aligned */
135
value = per_get_few_bits(pd, 24);
136
if(value < 0) return -1;
137
*(dst++) = value >> 16;
138
*(dst++) = value >> 8;
142
value = per_get_few_bits(pd, nbits);
143
if(value < 0) return -1;
144
if(nbits & 7) { /* implies left alignment */
145
value <<= 8 - (nbits & 7),
146
nbits += 8 - (nbits & 7);
148
*dst++ = value >> 24;
151
*dst++ = value >> 16;
163
* Get the length "n" from the stream.
166
uper_get_length(asn_per_data_t *pd, int ebits, int *repeat) {
171
if(ebits >= 0) return per_get_few_bits(pd, ebits);
173
value = per_get_few_bits(pd, 8);
174
if(value < 0) return -1;
175
if((value & 128) == 0) /* #10.9.3.6 */
176
return (value & 0x7F);
177
if((value & 64) == 0) { /* #10.9.3.7 */
178
value = ((value & 63) << 8) | per_get_few_bits(pd, 8);
179
if(value < 0) return -1;
182
value &= 63; /* this is "m" from X.691, #10.9.3.8 */
183
if(value < 1 || value > 4)
186
return (16384 * value);
190
* Get the normally small length "n".
191
* This procedure used to decode length of extensions bit-maps
192
* for SET and SEQUENCE types.
195
uper_get_nslength(asn_per_data_t *pd) {
198
ASN_DEBUG("Getting normally small length");
200
if(per_get_few_bits(pd, 1) == 0) {
201
length = per_get_few_bits(pd, 6) + 1;
202
if(length <= 0) return -1;
203
ASN_DEBUG("l=%d", length);
207
length = uper_get_length(pd, -1, &repeat);
208
if(length >= 0 && !repeat) return length;
209
return -1; /* Error, or do not support >16K extensions */
214
* Get the normally small non-negative whole number.
218
uper_get_nsnnwn(asn_per_data_t *pd) {
221
value = per_get_few_bits(pd, 7);
222
if(value & 64) { /* implicit (value < 0) */
225
value |= per_get_few_bits(pd, 2);
226
if(value & 128) /* implicit (value < 0) */
232
value = per_get_few_bits(pd, 8 * value);
240
* Put the normally small non-negative whole number.
244
uper_put_nsnnwn(asn_per_outp_t *po, int n) {
249
return per_put_few_bits(po, n, 7);
255
else if(n < 256 * 65536)
258
return -1; /* This is not a "normally small" value */
259
if(per_put_few_bits(po, bytes, 8))
262
return per_put_few_bits(po, n, 8 * bytes);
267
* Put a small number of bits (<= 31).
270
per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) {
271
size_t off; /* Next after last bit offset */
272
size_t omsk; /* Existing last byte meaningful bits mask */
275
if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
277
ASN_DEBUG("[PER put %d bits %x to %p+%d bits]",
278
obits, (int)bits, po->buffer, po->nboff);
281
* Normalize position indicator.
284
po->buffer += (po->nboff >> 3);
285
po->nbits -= (po->nboff & ~0x07);
290
* Flush whole-bytes output, if necessary.
292
if(po->nboff + obits > po->nbits) {
293
int complete_bytes = (po->buffer - po->tmpspace);
294
ASN_DEBUG("[PER output %d complete + %d]",
295
complete_bytes, po->flushed_bytes);
296
if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0)
299
po->tmpspace[0] = po->buffer[0];
300
po->buffer = po->tmpspace;
301
po->nbits = 8 * sizeof(po->tmpspace);
302
po->flushed_bytes += complete_bytes;
306
* Now, due to sizeof(tmpspace), we are guaranteed large enough space.
309
omsk = ~((1 << (8 - po->nboff)) - 1);
310
off = (po->nboff += obits);
312
/* Clear data of debris before meaningful bits */
313
bits &= (((uint32_t)1 << obits) - 1);
315
ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits,
316
(int)bits, (int)bits,
317
po->nboff - obits, off, buf[0], omsk&0xff, buf[0] & omsk);
319
if(off <= 8) /* Completely within 1 byte */
321
buf[0] = (buf[0] & omsk) | bits;
324
buf[0] = (buf[0] & omsk) | (bits >> 8),
328
buf[0] = (buf[0] & omsk) | (bits >> 16),
333
buf[0] = (buf[0] & omsk) | (bits >> 24),
338
ASN_DEBUG("->[PER out split %d]", obits);
339
per_put_few_bits(po, bits >> 8, 24);
340
per_put_few_bits(po, bits, obits - 24);
341
ASN_DEBUG("<-[PER out split %d]", obits);
344
ASN_DEBUG("[PER out %u/%x => %02x buf+%d]",
345
(int)bits, (int)bits, buf[0], po->buffer - po->tmpspace);
352
* Output a large number of bits.
355
per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) {
361
value = (src[0] << 16) | (src[1] << 8) | src[2];
364
if(per_put_few_bits(po, value, 24))
369
value = (value << 8) | src[1];
371
value = (value << 8) | src[2];
373
value >>= (8 - (nbits & 0x07));
374
if(per_put_few_bits(po, value, nbits))
384
* Put the length "n" (or part of it) into the stream.
387
uper_put_length(asn_per_outp_t *po, size_t length) {
389
if(length <= 127) /* #10.9.3.6 */
390
return per_put_few_bits(po, length, 8)
391
? -1 : (ssize_t)length;
392
else if(length < 16384) /* #10.9.3.7 */
393
return per_put_few_bits(po, length|0x8000, 16)
394
? -1 : (ssize_t)length;
397
if(length > 4) length = 4;
399
return per_put_few_bits(po, 0xC0 | length, 8)
400
? -1 : (ssize_t)(length << 14);
405
* Put the normally small length "n" into the stream.
406
* This procedure used to encode length of extensions bit-maps
407
* for SET and SEQUENCE types.
410
uper_put_nslength(asn_per_outp_t *po, size_t length) {
414
if(length == 0) return -1;
415
return per_put_few_bits(po, length-1, 7) ? -1 : 0;
417
if(uper_put_length(po, length) != (ssize_t)length) {
418
/* This might happen in case of >16K extensions */