Moving in e2sim originally from it/test/simulators
[sim/e2-interface.git] / e2sim / ASN1c / asn_bit_data.c
1 /*****************************************************************************
2 #                                                                            *
3 # Copyright 2019 AT&T Intellectual Property                                  *
4 #                                                                            *
5 # Licensed under the Apache License, Version 2.0 (the "License");            *
6 # you may not use this file except in compliance with the License.           *
7 # You may obtain a copy of the License at                                    *
8 #                                                                            *
9 #      http://www.apache.org/licenses/LICENSE-2.0                            *
10 #                                                                            *
11 # Unless required by applicable law or agreed to in writing, software        *
12 # distributed under the License is distributed on an "AS IS" BASIS,          *
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
14 # See the License for the specific language governing permissions and        *
15 # limitations under the License.                                             *
16 #                                                                            *
17 ******************************************************************************/
18
19 /*
20  * Copyright (c) 2005-2017 Lev Walkin <vlm@lionet.info>.
21  * All rights reserved.
22  * Redistribution and modifications are permitted subject to BSD license.
23  */
24 #include <asn_system.h>
25 #include <asn_internal.h>
26 #include <asn_bit_data.h>
27
28 /*
29  * Create a contiguous non-refillable bit data structure.
30  * Can be freed by FREEMEM().
31  */
32 asn_bit_data_t *
33 asn_bit_data_new_contiguous(const void *data, size_t size_bits) {
34     size_t size_bytes = (size_bits + 7) / 8;
35     asn_bit_data_t *pd;
36     uint8_t *bytes;
37
38     /* Get the extensions map */
39     pd = CALLOC(1, sizeof(*pd) + size_bytes + 1);
40     if(!pd) {
41         return NULL;
42     }
43     bytes = (void *)(((char *)pd) + sizeof(*pd));
44     memcpy(bytes, data, size_bytes);
45     bytes[size_bytes] = 0;
46     pd->buffer = bytes;
47     pd->nboff = 0;
48     pd->nbits = size_bits;
49
50     return pd;
51 }
52
53
54 char *
55 asn_bit_data_string(asn_bit_data_t *pd) {
56         static char buf[2][32];
57         static int n;
58         n = (n+1) % 2;
59     snprintf(buf[n], sizeof(buf[n]),
60              "{m=%" ASN_PRI_SIZE " span %" ASN_PRI_SIZE "[%" ASN_PRI_SIZE
61              "..%" ASN_PRI_SIZE "] (%" ASN_PRI_SIZE ")}",
62              pd->moved, ((uintptr_t)(pd->buffer) & 0xf), pd->nboff, pd->nbits,
63              pd->nbits - pd->nboff);
64     return buf[n];
65 }
66
67 void
68 asn_get_undo(asn_bit_data_t *pd, int nbits) {
69         if((ssize_t)pd->nboff < nbits) {
70                 assert((ssize_t)pd->nboff < nbits);
71         } else {
72                 pd->nboff -= nbits;
73                 pd->moved -= nbits;
74         }
75 }
76
77 /*
78  * Extract a small number of bits (<= 31) from the specified PER data pointer.
79  */
80 int32_t
81 asn_get_few_bits(asn_bit_data_t *pd, int nbits) {
82         size_t off;     /* Next after last bit offset */
83         ssize_t nleft;  /* Number of bits left in this stream */
84         uint32_t accum;
85         const uint8_t *buf;
86
87         if(nbits < 0)
88                 return -1;
89
90         nleft = pd->nbits - pd->nboff;
91         if(nbits > nleft) {
92                 int32_t tailv, vhead;
93                 if(!pd->refill || nbits > 31) return -1;
94                 /* Accumulate unused bytes before refill */
95                 ASN_DEBUG("Obtain the rest %d bits (want %d)",
96                         (int)nleft, (int)nbits);
97                 tailv = asn_get_few_bits(pd, nleft);
98                 if(tailv < 0) return -1;
99                 /* Refill (replace pd contents with new data) */
100                 if(pd->refill(pd))
101                         return -1;
102                 nbits -= nleft;
103                 vhead = asn_get_few_bits(pd, nbits);
104                 /* Combine the rest of previous pd with the head of new one */
105                 tailv = (tailv << nbits) | vhead;  /* Could == -1 */
106                 return tailv;
107         }
108
109         /*
110          * Normalize position indicator.
111          */
112         if(pd->nboff >= 8) {
113                 pd->buffer += (pd->nboff >> 3);
114                 pd->nbits  -= (pd->nboff & ~0x07);
115                 pd->nboff  &= 0x07;
116         }
117         pd->moved += nbits;
118         pd->nboff += nbits;
119         off = pd->nboff;
120         buf = pd->buffer;
121
122         /*
123          * Extract specified number of bits.
124          */
125         if(off <= 8)
126                 accum = nbits ? (buf[0]) >> (8 - off) : 0;
127         else if(off <= 16)
128                 accum = ((buf[0] << 8) + buf[1]) >> (16 - off);
129         else if(off <= 24)
130                 accum = ((buf[0] << 16) + (buf[1] << 8) + buf[2]) >> (24 - off);
131         else if(off <= 31)
132                 accum = (((uint32_t)buf[0] << 24) + (buf[1] << 16)
133                         + (buf[2] << 8) + (buf[3])) >> (32 - off);
134         else if(nbits <= 31) {
135                 asn_bit_data_t tpd = *pd;
136                 /* Here are we with our 31-bits limit plus 1..7 bits offset. */
137                 asn_get_undo(&tpd, nbits);
138                 /* The number of available bits in the stream allow
139                  * for the following operations to take place without
140                  * invoking the ->refill() function */
141                 accum  = asn_get_few_bits(&tpd, nbits - 24) << 24;
142                 accum |= asn_get_few_bits(&tpd, 24);
143         } else {
144                 asn_get_undo(pd, nbits);
145                 return -1;
146         }
147
148         accum &= (((uint32_t)1 << nbits) - 1);
149
150         ASN_DEBUG("  [PER got %2d<=%2d bits => span %d %+ld[%d..%d]:%02x (%d) => 0x%x]",
151                 (int)nbits, (int)nleft,
152                 (int)pd->moved,
153                 (((long)pd->buffer) & 0xf),
154                 (int)pd->nboff, (int)pd->nbits,
155                 ((pd->buffer != NULL)?pd->buffer[0]:0),
156                 (int)(pd->nbits - pd->nboff),
157                 (int)accum);
158
159         return accum;
160 }
161
162 /*
163  * Extract a large number of bits from the specified PER data pointer.
164  */
165 int
166 asn_get_many_bits(asn_bit_data_t *pd, uint8_t *dst, int alright, int nbits) {
167         int32_t value;
168
169         if(alright && (nbits & 7)) {
170                 /* Perform right alignment of a first few bits */
171                 value = asn_get_few_bits(pd, nbits & 0x07);
172                 if(value < 0) return -1;
173                 *dst++ = value; /* value is already right-aligned */
174                 nbits &= ~7;
175         }
176
177         while(nbits) {
178                 if(nbits >= 24) {
179                         value = asn_get_few_bits(pd, 24);
180                         if(value < 0) return -1;
181                         *(dst++) = value >> 16;
182                         *(dst++) = value >> 8;
183                         *(dst++) = value;
184                         nbits -= 24;
185                 } else {
186                         value = asn_get_few_bits(pd, nbits);
187                         if(value < 0) return -1;
188                         if(nbits & 7) { /* implies left alignment */
189                                 value <<= 8 - (nbits & 7),
190                                 nbits += 8 - (nbits & 7);
191                                 if(nbits > 24)
192                                         *dst++ = value >> 24;
193                         }
194                         if(nbits > 16)
195                                 *dst++ = value >> 16;
196                         if(nbits > 8)
197                                 *dst++ = value >> 8;
198                         *dst++ = value;
199                         break;
200                 }
201         }
202
203         return 0;
204 }
205
206 /*
207  * Put a small number of bits (<= 31).
208  */
209 int
210 asn_put_few_bits(asn_bit_outp_t *po, uint32_t bits, int obits) {
211         size_t off;     /* Next after last bit offset */
212         size_t omsk;    /* Existing last byte meaningful bits mask */
213         uint8_t *buf;
214
215         if(obits <= 0 || obits >= 32) return obits ? -1 : 0;
216
217         ASN_DEBUG("[PER put %d bits %x to %p+%d bits]",
218                         obits, (int)bits, (void *)po->buffer, (int)po->nboff);
219
220         /*
221          * Normalize position indicator.
222          */
223         if(po->nboff >= 8) {
224                 po->buffer += (po->nboff >> 3);
225                 po->nbits  -= (po->nboff & ~0x07);
226                 po->nboff  &= 0x07;
227         }
228
229         /*
230          * Flush whole-bytes output, if necessary.
231          */
232         if(po->nboff + obits > po->nbits) {
233                 size_t complete_bytes;
234                 if(!po->buffer) po->buffer = po->tmpspace;
235                 complete_bytes = (po->buffer - po->tmpspace);
236                 ASN_DEBUG("[PER output %ld complete + %ld]",
237                         (long)complete_bytes, (long)po->flushed_bytes);
238                 if(po->output(po->tmpspace, complete_bytes, po->op_key) < 0)
239                         return -1;
240                 if(po->nboff)
241                         po->tmpspace[0] = po->buffer[0];
242                 po->buffer = po->tmpspace;
243                 po->nbits = 8 * sizeof(po->tmpspace);
244                 po->flushed_bytes += complete_bytes;
245         }
246
247         /*
248          * Now, due to sizeof(tmpspace), we are guaranteed large enough space.
249          */
250         buf = po->buffer;
251         omsk = ~((1 << (8 - po->nboff)) - 1);
252         off = (po->nboff + obits);
253
254         /* Clear data of debris before meaningful bits */
255         bits &= (((uint32_t)1 << obits) - 1);
256
257         ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits,
258                 (int)bits, (int)bits,
259                 (int)po->nboff, (int)off,
260                 buf[0], (int)(omsk&0xff),
261                 (int)(buf[0] & omsk));
262
263         if(off <= 8)    /* Completely within 1 byte */
264                 po->nboff = off,
265                 bits <<= (8 - off),
266                 buf[0] = (buf[0] & omsk) | bits;
267         else if(off <= 16)
268                 po->nboff = off,
269                 bits <<= (16 - off),
270                 buf[0] = (buf[0] & omsk) | (bits >> 8),
271                 buf[1] = bits;
272         else if(off <= 24)
273                 po->nboff = off,
274                 bits <<= (24 - off),
275                 buf[0] = (buf[0] & omsk) | (bits >> 16),
276                 buf[1] = bits >> 8,
277                 buf[2] = bits;
278         else if(off <= 31)
279                 po->nboff = off,
280                 bits <<= (32 - off),
281                 buf[0] = (buf[0] & omsk) | (bits >> 24),
282                 buf[1] = bits >> 16,
283                 buf[2] = bits >> 8,
284                 buf[3] = bits;
285         else {
286                 if(asn_put_few_bits(po, bits >> (obits - 24), 24)) return -1;
287                 if(asn_put_few_bits(po, bits, obits - 24)) return -1;
288         }
289
290         ASN_DEBUG("[PER out %u/%x => %02x buf+%ld]",
291                 (int)bits, (int)bits, buf[0],
292                 (long)(po->buffer - po->tmpspace));
293
294         return 0;
295 }
296
297
298 /*
299  * Output a large number of bits.
300  */
301 int
302 asn_put_many_bits(asn_bit_outp_t *po, const uint8_t *src, int nbits) {
303
304         while(nbits) {
305                 uint32_t value;
306
307                 if(nbits >= 24) {
308                         value = (src[0] << 16) | (src[1] << 8) | src[2];
309                         src += 3;
310                         nbits -= 24;
311                         if(asn_put_few_bits(po, value, 24))
312                                 return -1;
313                 } else {
314                         value = src[0];
315                         if(nbits > 8)
316                                 value = (value << 8) | src[1];
317                         if(nbits > 16)
318                                 value = (value << 8) | src[2];
319                         if(nbits & 0x07)
320                                 value >>= (8 - (nbits & 0x07));
321                         if(asn_put_few_bits(po, value, nbits))
322                                 return -1;
323                         break;
324                 }
325         }
326
327         return 0;
328 }
329
330
331 int
332 asn_put_aligned_flush(asn_bit_outp_t *po) {
333     uint32_t unused_bits = (0x7 & (8 - (po->nboff & 0x07)));
334     size_t complete_bytes =
335         (po->buffer ? po->buffer - po->tmpspace : 0) + ((po->nboff + 7) >> 3);
336
337     if(unused_bits) {
338         po->buffer[po->nboff >> 3] &= ~0u << unused_bits;
339     }
340
341     if(po->output(po->tmpspace, complete_bytes, po->op_key) < 0) {
342         return -1;
343     } else {
344         po->buffer = po->tmpspace;
345         po->nboff = 0;
346         po->nbits = 8 * sizeof(po->tmpspace);
347         po->flushed_bytes += complete_bytes;
348         return 0;
349     }
350 }
351