2 /******************************************************************************
4 * Copyright (c) 2019 AT&T Intellectual Property.
5 * Copyright (c) 2018-2019 Nokia.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 *******************************************************************************/
21 // Standard Includes: ANSI C/C++, MSA, and Third-Party Libraries
24 // Local Includes: Application specific classes, functions, and libraries
25 #include "asn/error_context.hpp"
26 #include "value_traits.hpp"
28 //#define CODEC_BIT_TRACE_ENABLE
29 #ifdef CODEC_BIT_TRACE_ENABLE
30 #define CODEC_BIT_TRACE(FMT, args...) printf("%s[%u]:" FMT "\n", std::strrchr(__FILE__, '/') + 1, __LINE__, ##args)
32 #define CODEC_BIT_TRACE(...)
37 template <class IE, class VALUE, bool LSB>
41 struct bit_accessor_cross_byte;
43 template <typename PTR>
49 explicit buffer(error_context& err)
53 void reset(pointer data, u32 size)
56 m_end = m_start + size;
61 u32 getOffset() const { return begin() - m_start; }
63 u32 getBytesLeft() const { return end() - begin(); }
64 u32 getBytesUsed() const { return getOffset() + (get_shift() ? 1 : 0); }
66 u8 get_shift() const { return m_shift; }
67 void reset_shift() { m_shift = 0; }
68 void byte_align() { if (get_shift()) { m_shift = 0; ++m_current; } }
70 pointer advance(u32 delta) { pointer p = begin(); m_current += delta; return p; }
72 pointer begin() const { return m_current; }
74 void set_begin(pointer ptr, u8 bit_shift)
76 if (m_start <= ptr && ptr <= end())
83 pointer end() const { return m_end; }
84 void set_end(pointer v) { m_end = v; }
86 bool checkAlignment(char const* name, u8 const mask = 0xFF)
88 if (0 == (get_shift() & mask)) return true;
89 m_errCtx.alignmentError(name, get_shift());
93 bool checkBytesLeft(char const* name, u32 const num_bytes)
95 if (getBytesLeft() >= num_bytes) return true;
96 m_errCtx.lengthErrorBytes(name, getBytesLeft(), num_bytes);
100 bool checkBitsLeft(char const* name, u32 const num_bits)
102 if (getBitsLeft() >= num_bits) return true;
103 m_errCtx.lengthErrorBits(name, getBitsLeft(), num_bits);
107 bool checkBytesAndAlignment(char const* name, u32 const num_bytes, u8 const mask = 0xFF)
109 if (getBytesLeft() >= num_bytes)
111 if (0 == (get_shift() & mask)) return true;
112 m_errCtx.alignmentError(name, get_shift());
116 m_errCtx.lengthErrorBytes(name, getBytesLeft(), num_bytes);
121 pointer getBytes(char const* name, u32 num_bytes)
123 if (checkBytesAndAlignment(name, num_bytes))
125 return advance(num_bytes);
130 //NOTE! num_bytes should be set to minimal number of bytes expected
131 pointer getBytes(char const* name, u32 max_requested, u32& num_bytes)
133 if (checkAlignment(name))
135 u32 const left = getBytesLeft();
136 if (left >= num_bytes)
138 num_bytes = (left > max_requested) ? max_requested : left;
139 return advance(num_bytes);
142 m_errCtx.lengthErrorBytes(name, getBytesLeft(), num_bytes);
147 bool putByte(char const* name, u8 byte)
149 if (checkBytesAndAlignment(name, 1))
158 bool putBytes(char const* name, void const* in, u32 num_bytes)
160 if (checkBytesAndAlignment(name, num_bytes))
162 std::memcpy(begin(), in, num_bytes);
163 m_current += num_bytes;
169 bool checkAlignment(u8 const mask = 0xFF)
171 if (0 == (get_shift() & mask)) return true;
172 m_errCtx.alignmentError(get_shift());
176 bool checkBytesLeft(u32 const num_bytes)
178 if (getBytesLeft() >= num_bytes) return true;
179 m_errCtx.lengthErrorBytes(getBytesLeft(), num_bytes);
183 bool checkBitsLeft(u32 const num_bits)
185 if (getBitsLeft() >= num_bits) return true;
186 m_errCtx.lengthErrorBits(getBitsLeft(), num_bits);
190 bool checkBytesAndAlignment(u32 const num_bytes, u8 const mask = 0xFF)
192 if (getBytesLeft() >= num_bytes)
194 if (0 == (get_shift() & mask)) return true;
195 m_errCtx.alignmentError(get_shift());
199 m_errCtx.lengthErrorBytes(getBytesLeft(), num_bytes);
204 pointer getBytes(u32 num_bytes)
206 if (checkBytesAndAlignment(num_bytes))
208 return advance(num_bytes);
213 //NOTE! num_bytes should be set to minimal number of bytes expected
214 pointer getBytes(u32 max_requested, u32& num_bytes)
216 if (checkAlignment())
218 u32 const left = getBytesLeft();
219 if (left >= num_bytes)
221 num_bytes = (left > max_requested) ? max_requested : left;
222 return advance(num_bytes);
225 m_errCtx.lengthErrorBytes(getBytesLeft(), num_bytes);
230 bool putByte(u8 byte)
232 if (checkBytesAndAlignment(1))
241 bool putBytes(void const* in, u32 num_bytes)
245 if(checkBytesAndAlignment(num_bytes))
247 std::memcpy(begin(), in, num_bytes);
248 m_current += num_bytes;
256 char const* toString() const
259 u8 const* p = begin();
260 snprintf(sz, sizeof(sz), "%02X %02X %02X %02X [%02X]=%p@%u..%p -%u bits: +%u bytes", p[-4], p[-3], p[-2], p[-1], p[0], p
261 , getOffset(), end(), get_shift(), getBytesLeft());
266 template <class IE, class VALUE, bool LSB>
267 friend class bit_accessor;
270 friend struct bit_accessor_cross_byte;
272 template <typename U>
273 void push_u8(U value) { *m_current++ = static_cast<u8>(value); }
274 template <typename U>
275 void put_u8(U value) { m_current[0] = static_cast<u8>(value); }
277 //khxm68: TODO: won't compile without it. WTF?!
278 #pragma GCC diagnostic push
279 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
280 u8 get_u8() const { return m_current[0]; }
281 #pragma GCC diagnostic pop
282 u8 extract_u8(u8 value, u8 mask, u8 shift) const
283 { return (get_u8() & ~(mask << shift)) | ((value & mask) << shift); }
285 u16 get_u16() const { return (static_cast<u16>(m_current[0]) << 8) | m_current[1]; }
286 u16 extract_u16(u16 value, u16 mask, u16 shift) const
287 { return (get_u16() & ~(mask << shift)) | ((value & mask) << shift); }
289 u32 get_u24() const { return (static_cast<u32>(m_current[0]) << 16) | (static_cast<u32>(m_current[1]) << 8) | m_current[2]; }
290 u32 extract_u24(u32 value, u32 mask, u32 shift) const
291 { return (get_u24() & ~(mask << shift)) | ((value & mask) << shift); }
293 u32 get_u32() const { return (static_cast<u32>(m_current[0]) << 24) | (static_cast<u32>(m_current[1]) << 16) | (static_cast<u32>(m_current[2]) << 8) | m_current[3]; }
294 u32 extract_u32(u32 value, u32 mask, u32 shift) const
295 { return (get_u32() & ~(mask << shift)) | ((value & mask) << shift); }
297 u32 getBitsLeft() const { return begin() == end() ? 0 : (getBytesLeft()*8 - get_shift()); }
299 void bit_advance(u8 num_bits)
313 error_context& m_errCtx;
316 template <class IE> //LSB 1..7 bits
317 struct bit_accessor<IE, stdex::value::_8, true>
319 static_assert(IE::length::value > 0 && IE::length::value < 8, "something wrong with traits!");
321 static void put(u8 value, buffer<u8*>& buf)
323 u8 const mask = static_cast<u8>((1u << IE::length::value) - 1);
324 u8 const shift = buf.get_shift();
325 u8 const data = buf.extract_u8(value, mask, shift);
328 CODEC_BIT_TRACE("put shift=%u(%u) data[%u]=%02X(%02X) mask=%04X: %s", shift, buf.get_shift()
329 , IE::length::value, data, value, mask, buf.toString());
331 buf.bit_advance(IE::length::value);
334 static u8 get(buffer<u8 const*>& buf)
336 u8 const mask = static_cast<u8>((1u << IE::length::value) - 1);
337 u8 const shift = buf.get_shift();
338 u8 const data = (buf.get_u8() >> shift) & mask;
340 CODEC_BIT_TRACE("get shift=%u(%u) data[%u]=%02X mask=%02X: %s", shift, buf.get_shift()
341 , IE::length::value, data, mask, buf.toString());
343 buf.bit_advance(IE::length::value);
348 template <class IE> //LSB 9..15 bits
349 struct bit_accessor<IE, stdex::value::_16, true>
351 static_assert(IE::length::value > 8 && IE::length::value < 16, "something wrong with traits!");
353 static void put(u16 value, buffer<u8*>& buf)
355 u16 const mask = static_cast<u16>((1u << IE::length::value) - 1);
356 u16 const shift = buf.get_shift();
357 u16 const data = buf.extract_u16(value, mask, shift);
358 buf.push_u8(data >> 8);
361 CODEC_BIT_TRACE("put shift=%u(%u) data[%u]=%04X(%04X) mask=%04X: %s", shift, buf.get_shift()
362 , IE::length::value, data, value, mask, buf.toString());
364 buf.bit_advance(IE::length::value - 8);
367 static u16 get(buffer<u8 const*>& buf)
369 u16 const mask = static_cast<u16>((1u << IE::length::value) - 1);
370 u16 const shift = buf.get_shift();
371 u16 const data = (buf.get_u16() >> shift) & mask;
374 CODEC_BIT_TRACE("get shift=%u(%u) data[%u]=%04X mask=%04X: %s", shift, buf.get_shift()
375 , IE::length::value, data, mask, buf.toString());
377 buf.bit_advance(IE::length::value - 8);
382 template <class IE> //LSB 17..24 bits
383 struct bit_accessor<IE, stdex::value::_24, true>
385 static_assert(IE::length::value > 16 && IE::length::value <= 24, "something wrong with traits!");
387 static void put(u32 value, buffer<u8*>& buf)
389 u32 const mask = static_cast<u32>((1u << IE::length::value) - 1);
390 u32 const shift = buf.get_shift();
391 u32 const data = buf.extract_u24(value, mask, shift);
393 buf.push_u8(data >> 16);
394 buf.push_u8(data >> 8);
397 CODEC_BIT_TRACE("put shift=%u(%u) data[%u]=%06X(%06X) mask=%06X: %s", shift, buf.get_shift()
398 , IE::length::value, data, value, mask, buf.toString());
400 buf.bit_advance(IE::length::value - 16);
403 static u32 get(buffer<u8 const*>& buf)
405 u32 const mask = static_cast<u32>((1u << IE::length::value) - 1);
406 u32 const shift = buf.get_shift();
407 u32 const data = (buf.get_u24() >> shift) & mask;
410 CODEC_BIT_TRACE("get shift=%u(%u) data[%u]=%06X mask=%06X: %s", shift, buf.get_shift()
411 , IE::length::value, data, mask, buf.toString());
413 buf.bit_advance(IE::length::value - 16);
418 template <class IE> //LSB 25..31 bits
419 struct bit_accessor<IE, stdex::value::_32, true>
421 static_assert(IE::length::value > 24 && IE::length::value < 32, "something wrong with traits!");
423 static void put(u32 value, buffer<u8*>& buf)
425 u32 const mask = static_cast<u32>((1u << IE::length::value) - 1);
426 u32 const shift = buf.get_shift();
427 u32 const data = buf.extract_u32(value, mask, shift);
429 buf.push_u8(data >> 24);
430 buf.push_u8(data >> 16);
431 buf.push_u8(data >> 8);
434 CODEC_BIT_TRACE("put shift=%u(%u) data[%u]=%08X(%08X) mask=%08X: %s", shift, buf.get_shift()
435 , IE::length::value, data, value, mask, buf.toString());
437 buf.bit_advance(IE::length::value - 24);
440 static u32 get(buffer<u8 const*>& buf)
442 u32 const mask = static_cast<u32>((1u << IE::length::value) - 1);
443 u32 const shift = buf.get_shift();
444 u32 const data = (buf.get_u32() >> shift) & mask;
447 CODEC_BIT_TRACE("get shift=%u(%u) data[%u]=%08X mask=%08X: %s", shift, buf.get_shift()
448 , IE::length::value, data, mask, buf.toString());
450 buf.bit_advance(IE::length::value - 24);
456 template <class IE> //MSB 1..7 bits
457 struct bit_accessor<IE, stdex::value::_8, false>
459 static_assert(IE::length::value > 0 && IE::length::value < 8, "something wrong with traits!");
461 static void put(u8 value, buffer<u8*>& buf)
463 u8 const mask = static_cast<u8>((1u << IE::length::value) - 1);
464 u8 const shift = 8 - buf.get_shift() - IE::length::value;
465 u8 const data = buf.extract_u8(value, mask, shift);
468 CODEC_BIT_TRACE("put shift=%u(%u) data[%u]=%02X(%02X) mask=%04X: %s", shift, buf.get_shift()
469 , IE::length::value, data, value, mask, buf.toString());
471 buf.bit_advance(IE::length::value);
474 static u8 get(buffer<u8 const*>& buf)
476 u8 const mask = static_cast<u8>((1u << IE::length::value) - 1);
477 u8 const shift = 8 - buf.get_shift() - IE::length::value;
478 u8 const data = (buf.get_u8() >> shift) & mask;
480 CODEC_BIT_TRACE("get shift=%u(%u) data[%u]=%02X mask=%02X: %s", shift, buf.get_shift()
481 , IE::length::value, data, mask, buf.toString());
483 buf.bit_advance(IE::length::value);
488 template <class IE> //MSB 9..15 bits
489 struct bit_accessor<IE, stdex::value::_16, false>
491 static_assert(IE::length::value > 8 && IE::length::value < 16, "something wrong with traits!");
493 static void put(u16 value, buffer<u8*>& buf)
495 u16 const mask = static_cast<u16>((1u << IE::length::value) - 1);
496 u16 const shift = 16 - buf.get_shift() - IE::length::value;
497 u16 const data = buf.extract_u16(value, mask, shift);
499 buf.push_u8(data >> 8);
502 CODEC_BIT_TRACE("put shift=%u(%u) data[%u]=%04X(%04X) mask=%04X: %s", shift, buf.get_shift()
503 , IE::length::value, data, value, mask, buf.toString());
505 buf.bit_advance(IE::length::value - 8);
508 static u16 get(buffer<u8 const*>& buf)
510 u16 const mask = static_cast<u16>((1u << IE::length::value) - 1);
511 u16 const shift = 16 - buf.get_shift() - IE::length::value;
512 u16 const data = (buf.get_u16() >> shift) & mask;
515 CODEC_BIT_TRACE("get shift=%u(%u) data[%u]=%04X mask=%04X: %s", shift, buf.get_shift()
516 , IE::length::value, data, mask, buf.toString());
518 buf.bit_advance(IE::length::value - 8);
524 template <class IE> //MSB 17..24 bits
525 struct bit_accessor<IE, stdex::value::_24, false>
527 static_assert(IE::length::value > 16 && IE::length::value <= 24, "something wrong with traits!");
529 static void put(u32 value, buffer<u8*>& buf)
531 u32 const mask = static_cast<u32>((1u << IE::length::value) - 1);
532 u32 const shift = 24 - buf.get_shift() - IE::length::value;
533 u32 const data = buf.extract_u24(value, mask, shift);
535 buf.push_u8(data >> 16);
536 buf.push_u8(data >> 8);
539 CODEC_BIT_TRACE("put shift=%u(%u) data[%u]=%06X(%06X) mask=%06X: %s", shift, buf.get_shift()
540 , IE::length::value, data, value, mask, buf.toString());
542 buf.bit_advance(IE::length::value - 16);
545 static u32 get(buffer<u8 const*>& buf)
547 u32 const mask = static_cast<u32>((1u << IE::length::value) - 1);
548 u32 const shift = 24 - buf.get_shift() - IE::length::value;
549 u32 const data = (buf.get_u24() >> shift) & mask;
552 CODEC_BIT_TRACE("get shift=%u(%u) data[%u]=%06X mask=%06X: %s", shift, buf.get_shift()
553 , IE::length::value, data, mask, buf.toString());
555 buf.bit_advance(IE::length::value - 16);
560 template <class IE> //MSB 25..31 bits
561 struct bit_accessor<IE, stdex::value::_32, false>
563 static_assert(IE::length::value > 24 && IE::length::value < 32, "something wrong with traits!");
565 static void put(u32 value, buffer<u8*>& buf)
567 u32 const mask = static_cast<u32>((1u << IE::length::value) - 1);
568 u32 const shift = 32 - buf.get_shift() - IE::length::value;
569 u32 const data = buf.extract_u32(value, mask, shift);
571 buf.push_u8(data >> 24);
572 buf.push_u8(data >> 16);
573 buf.push_u8(data >> 8);
576 CODEC_BIT_TRACE("put shift=%u(%u) data[%u]=%08X(%08X) mask=%08X: %s", shift, buf.get_shift()
577 , IE::length::value, data, value, mask, buf.toString());
579 buf.bit_advance(IE::length::value - 24);
582 static u32 get(buffer<u8 const*>& buf)
584 u32 const mask = static_cast<u32>((1u << IE::length::value) - 1);
585 u32 const shift = 32 - buf.get_shift() - IE::length::value;
586 u32 const data = (buf.get_u32() >> shift) & mask;
589 CODEC_BIT_TRACE("get shift=%u(%u) data[%u]=%08X mask=%08X: %s", shift, buf.get_shift()
590 , IE::length::value, data, mask, buf.toString());
592 buf.bit_advance(IE::length::value - 24);
597 /********************************************************************************
598 bit_accessor_cross_byte
599 *********************************************************************************/
601 struct bit_accessor_cross_byte<false> //MSB
603 static bool put(u8 value, u8 length, buffer<u8*>& buf)
605 u8 spare = 8 - buf.get_shift();
610 u8 chunk = value >> length;
611 u8 const mask = static_cast<u8>((1u << spare) - 1);
612 u8 data = buf.extract_u8(chunk, mask, 0);
615 buf.bit_advance(spare);
617 if (buf.checkBytesLeft(1))
619 u8 const mask = static_cast<u8>((1u << length) - 1);
620 u8 const shift = 8 - buf.get_shift() - length;
621 u8 data = buf.extract_u8(value, mask, shift);
624 buf.bit_advance(length);
631 static u8 get(u8 length, buffer<u8 const*>& buf)
634 u8 const mask = static_cast<u8>((1u << length) - 1);
635 u8 const spare = 8 - buf.get_shift();
639 rval = buf.get_u8() << length;
640 buf.bit_advance(spare);
642 if (length && buf.checkBytesLeft(1))
644 u8 val = buf.get_u8();
645 val = val >> (8 - length - buf.get_shift());
648 buf.bit_advance(length);
653 static void padByte(buffer<u8*>& buf)
657 u8 mask = static_cast<u8>((1u << buf.get_shift()) - 1);
658 mask = mask << (8 - buf.get_shift());
659 buf.begin()[0] &= mask;
660 buf.bit_advance(8 - buf.get_shift());
664 static void padByte(buffer<u8 const*>& buf)
667 buf.bit_advance(8 - buf.get_shift());
670 static bool put(const u8* in, size_t len, buffer<u8*>& buf, u8 trail_bitsqty)
676 buf.putBytes(in, len - 1);
677 u8 last_byte = in[len - 1];
678 last_byte = last_byte >> (8 - trail_bitsqty);
679 return put(last_byte, trail_bitsqty, buf);
681 buf.putBytes(in, len);
687 } //end: namespace asn