Enhanced SIM for E2AP v1 for TS UC
[sim/e2-interface.git] / e2sim / e2apv1sim / src / ASN1 / asn / ber / codec.hpp
diff --git a/e2sim/e2apv1sim/src/ASN1/asn/ber/codec.hpp b/e2sim/e2apv1sim/src/ASN1/asn/ber/codec.hpp
new file mode 100755 (executable)
index 0000000..3a42ad5
--- /dev/null
@@ -0,0 +1,931 @@
+#pragma once
+/******************************************************************************
+*
+*   Copyright (c) 2019 AT&T Intellectual Property.
+*   Copyright (c) 2018-2019 Nokia.
+*
+*   Licensed under the Apache License, Version 2.0 (the "License");
+*   you may not use this file except in compliance with the License.
+*   You may obtain a copy of the License at
+*
+*       http://www.apache.org/licenses/LICENSE-2.0
+*
+*   Unless required by applicable law or agreed to in writing, software
+*   distributed under the License is distributed on an "AS IS" BASIS,
+*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*   See the License for the specific language governing permissions and
+*   limitations under the License.
+*
+******************************************************************************/
+
+// Standard Includes: ANSI C/C++, MSA, and Third-Party Libraries
+
+// Local Includes: Application specific classes, functions, and libraries
+#include "asn/elements.hpp"
+#include "asn/ber/common.hpp"
+#include "asn/ber/context.hpp"
+#include "asn/ber/tag.hpp"
+#include "asn/ber/length.hpp"
+#include "asn/ber/visitor.hpp"
+#include "asn/ber/opentype.hpp"
+
+namespace asn {
+namespace ber {
+
+/********************************************************************************
+pack (X.690)
+*********************************************************************************/
+template <class IE>
+bool pack(IE const& ie, EncoderCtx& ctx)
+{
+       ctx.refErrorCtx().reset();
+       Element<IE>::run(ie, ctx);
+
+       if (ctx)
+               Tools::bit_accessor::padByte(ctx.refBuffer());
+
+       return static_cast<bool>(ctx);
+}
+/********************************************************************************
+unpack (X.690)
+*********************************************************************************/
+template <class IE>
+bool unpack(IE& ie, DecoderCtx& ctx)
+{
+       Element<IE>::run(ie, ctx);
+
+       if (ctx)
+               Tools::bit_accessor::padByte(ctx.refBuffer());
+
+       if(ctx && ctx.refBuffer().getBytesLeft())
+       {
+               ctx.ie_name(IE::name());
+               ctx.refErrorCtx().lengthErrorBytes(ctx.refBuffer().getBytesLeft(), 0);
+       }
+
+       return static_cast<bool>(ctx);
+}
+
+/***************************************************************************************
+* ElementType
+***************************************************************************************/
+template <class IE, class Enabler = void>
+struct ElementType;
+
+/***************************************************************************************
+* ExplicitCodec: Codec for elements with EXPLICIT tag
+***************************************************************************************/
+template <class IE>
+struct ExplicitCodec
+{
+       using tag_t = Tag<IE, true>;
+       static bool inline is_matched(tag_value_t _tag) {return _tag == tag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               auto & buffer = ctx.refBuffer();
+
+               tag_t::encode(ctx);
+    u8* len_ptr = buffer.advance(1); //reserve for the length
+
+               Element<typename IE::parent_t>::run(static_cast<typename IE::parent_t const&>(ie), ctx);
+
+    size_t len = buffer.begin() - len_ptr - 1;
+    if(len > 127)
+    {
+      len_ptr[0] = 0x80; //undefinite length form per X.690 8.1.3.6
+                 uint8_t buff[2] = {0}; // end-of-contents octets (X.690 8.1.5)
+                 buffer.putBytes(buff, sizeof(buff));
+    }
+    else
+    {
+      len_ptr[0] = static_cast<u8>(len); //one byte form per X.690 8.1.3.4
+    }
+       }
+
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               if(tag_t::value() != tag)
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               else
+               {
+                       auto & buffer = ctx.refBuffer();
+                       size_t length = Length::decode(ctx);
+
+      ASN_DECODER_TRACE("EX<tag=0x%x length=%zu> buffer: %s", static_cast<int>(tag), length, ctx.refBuffer().toString());
+
+                       if(length == indefinite_length)
+                       {
+                               Element<typename IE::parent_t>::run(static_cast<typename IE::parent_t&>(ie), ctx);
+
+                               if(ctx)
+                               {
+                                       uint8_t const* data_in = ctx.refBuffer().getBytes(2);
+                                       if(data_in && (data_in[0] || data_in[1]))
+                                       {
+                                               ctx.refErrorCtx().errorWrongEndOfContent();
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               if (buffer.getBytesLeft() < length)
+                               {
+                                       ctx.refErrorCtx().lengthErrorBytes(buffer.getBytesLeft(), length);
+                               }
+                               else
+                               {
+                                       DecoderCtx::buf_type::pointer end = buffer.end();
+                                       buffer.set_end(buffer.begin() + length);
+
+                                       Element<typename IE::parent_t>::run(static_cast<typename IE::parent_t&>(ie), ctx);
+
+                                       buffer.set_end(end);
+                               }
+                       }
+
+               }
+       }
+};
+
+/***************************************************************************************
+* BOOLEAN: Encoding the boolean type  (X.690 8.2)
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_BOOLEAN> >
+{
+       using tag_t = Tag<IE, false>;
+
+       static bool inline is_matched(tag_value_t tag) {return tag == tag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               tag_t::encode(ctx);
+               Length::encode(1, ctx);
+               if(ie.get())
+                       Tools::put_bytes(0xFF, 1, ctx);
+               else
+                       Tools::put_bytes(0, 1, ctx);
+       }
+
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               if(tag_t::value() != tag)
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               else
+               {
+                       size_t length = Length::decode(ctx);
+                       if(length != 1)
+                       {
+                               ctx.refErrorCtx().sizeRangeError(length, 1, 1);
+                       }
+                       else
+                       {
+        ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+                               uint8_t value;
+                               Tools::get_bytes(value, 1, ctx);
+                               ie.set(value > 0);
+                       }
+               }
+       }
+};
+/***************************************************************************************
+* INTEGER: Encoding the integer type  (X.690 8.3)
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_INTEGER> >
+{
+       using tag_t = Tag<IE, false>;
+
+       static bool inline is_matched(tag_value_t tag) {return tag == tag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               tag_t::encode(ctx);
+               size_t length = Length::get(ie.get());
+               Length::encode(length, ctx);
+               Tools::put_bytes(ie.get(), length, ctx);
+       }
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               if(tag_t::value() != tag)
+    {
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+    }
+               else
+               {
+                       size_t length = Length::decode(ctx);
+                       if(!length || length == indefinite_length)
+                               ctx.refErrorCtx().sizeRangeError(length);
+                       else
+                       {
+        ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+                               typename IE::value_type value;
+                               Tools::get_bytes(value, length, ctx);
+                               ie.set(value);
+                       }
+               }
+       }
+};
+/***************************************************************************************
+* ENUMERATED: Encoding the enumerated type  (X.690 8.4)
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_ENUMERATED> >
+{
+       using tag_t = Tag<IE, false>;
+
+       static bool inline is_matched(tag_value_t tag) {return tag == tag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               tag_t::encode(ctx);
+               size_t length = Length::get(ie.get());
+               Length::encode(length, ctx);
+               Tools::put_bytes(ie.get(), length, ctx);
+       }
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               if(tag_t::value() != tag)
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               else
+               {
+                       size_t length = Length::decode(ctx);
+                       if(!length || length == indefinite_length)
+                               ctx.refErrorCtx().sizeRangeError(length);
+                       else
+                       {
+        ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+                               typename IE::value_type value;
+                               Tools::get_bytes(value, length, ctx);
+                               ie.set(value);
+                       }
+               }
+       }
+};
+/***************************************************************************************
+* REAL: Encoding the real type  (X.690 8.5)
+***************************************************************************************/
+//missing...
+/***************************************************************************************
+* BIT STRING: Encoding the bitstring type (X.690 8.6)
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<(IE::ie_type == element_type::T_BITSTRING)> >
+{
+       using tag_t = Tag<IE, false>;
+       using ctag_t = Tag<IE, true>;
+
+       static bool inline is_matched(tag_value_t tag) {return tag == tag_t::value() || tag == ctag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               tag_t::encode(ctx);
+
+               uint8_t tail = ie.get_bitqty() % 8;
+
+               size_t length = ie.get_buffer().size();
+               Length::encode(length + 1, ctx);
+
+               auto & buffer = ctx.refBuffer();
+               buffer.putByte((8 - tail) & 0x7F);
+
+               if (tail)
+               {
+                       buffer.putBytes(ie.get_buffer().data(), length - 1);
+                       u8 last_byte = *(ie.get_buffer().data() + length - 1);
+
+                       last_byte <<= 8 - tail;
+                       buffer.putBytes(&last_byte, 1);
+               }
+               else
+               {
+                       buffer.putBytes(ie.get_buffer().data(), length);
+               }
+       }
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               ie.clear();
+               if(tag_t::value() == tag)
+               {
+                       size_t length = Length::decode(ctx);
+                       if(!length || length == indefinite_length)
+                       {
+                               ctx.refErrorCtx().sizeRangeError(length);
+                       }
+                       else
+                       {
+                               ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+                               uint8_t const* data_in = ctx.refBuffer().getBytes(length);
+                               if(data_in)
+                               {
+                                       size_t len_bytes = length - 1;
+                                       size_t bitqty    = len_bytes << 3;
+
+                                       if((data_in[0] & 0x80) || (bitqty < data_in[0]))
+                                       {
+                                               ctx.refErrorCtx().valueRangeError(data_in[0]);
+                                       }
+                                       else
+                                       {
+                                               bitqty = bitqty - data_in[0];
+                                               uint8_t* data_out = ctx.refAllocator().alloc_bytes(len_bytes);
+                                               if (data_out)
+                                               {
+                                                       memcpy(data_out, &data_in[1], len_bytes);
+                                                       const u8 shift = bitqty % 8;
+                                                       if (shift)
+                                                       {
+                                                               data_out[len_bytes - 1] >>= 8 - shift;
+                                                       }
+                                                       ie.set_buffer(bitqty, data_out);
+                                               }
+                                               else
+                                               {
+                                                       ctx.refErrorCtx().allocatorNoMem(0, len_bytes);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               else if(ctag_t::value() == tag)
+               {
+                       //todo: implement the segmented data
+                       ctx.refErrorCtx().errorUnsupported();
+               }
+               else
+               {
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               }
+       }
+};
+/***************************************************************************************
+* OCTET STRING: Encoding the octetstring type (X.690 8.7)
+* Restricted Character string types (X.690 8.23)
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_OCTETSTRING> >
+{
+       using tag_t = Tag<IE, false>;
+       using ctag_t = Tag<IE, true>;
+
+       static bool inline is_matched(tag_value_t tag) {return tag == tag_t::value() || tag == ctag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               tag_t::encode(ctx);
+               size_t length = ie.get().size();
+               Length::encode(length, ctx);
+               ctx.refBuffer().putBytes(ie.get().data(), length);
+       }
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               ie.clear();
+               if(tag_t::value() == tag)
+               {
+                       size_t length = Length::decode(ctx);
+                       if(length == indefinite_length)
+                       {
+                               ctx.refErrorCtx().sizeRangeError(length);
+                       }
+                       else
+                       {
+        ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+                               uint8_t const* data_in = ctx.refBuffer().getBytes(length);
+                               if(data_in)
+                               {
+                                       ie.set(length, data_in);
+                               }
+                       }
+               }
+               else if(ctag_t::value() == tag)
+               {
+                       //todo: implement the segmented data
+                       ctx.refErrorCtx().errorUnsupported();
+               }
+               else
+               {
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               }
+       }
+};
+/***************************************************************************************
+* NULL: Encoding the null type (X.690 8.8)
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_NULL> >
+{
+       using tag_t = Tag<IE, false>;
+
+       static bool inline is_matched(tag_value_t tag) {return tag == tag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               tag_t::encode(ctx);
+               Length::encode(0, ctx);
+       }
+
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               if(tag_t::value() != tag)
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               else
+               {
+                       size_t length = Length::decode(ctx);
+                       if(length)
+                               ctx.refErrorCtx().sizeRangeError(length);
+      ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+               }
+       }
+};
+/***************************************************************************************
+* SEQUENCE: Encoding the sequence type (X.690 8.9)
+* SET:                 Encoding the set type (X.690 8.11)
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<(IE::ie_type == element_type::T_SEQUENCE) || (IE::ie_type == element_type::T_SET)> >
+{
+       using tag_t = Tag<IE, true>;
+
+       static bool inline is_matched(tag_value_t tag) {return tag == tag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+    auto & buffer = ctx.refBuffer();
+
+               tag_t::encode(ctx);
+    u8* len_ptr = buffer.advance(1); //reserve for the length
+
+               VisitorEncoderSeq<IE> ve(ctx, ie);
+               ie.encode(ve);
+
+    size_t len = buffer.begin() - len_ptr - 1;
+    if(len > 127)
+    {
+      len_ptr[0] = 0x80; //undefinite length form per X.690 8.1.3.6
+                 uint8_t buff[2] = {0}; // end-of-contents octets (X.690 8.1.5)
+                 buffer.putBytes(buff, sizeof(buff));
+    }
+    else
+      len_ptr[0] = static_cast<u8>(len); //one byte form per X.690 8.1.3.4
+       }
+
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag) //todo: support arbitrary order of IEs in SET
+       {
+               if(tag_t::value() != tag)
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               else
+               {
+                       VisitorDecoderSeq<IE> vd(ctx, ie);
+                       auto & buffer = ctx.refBuffer();
+
+                       size_t length = Length::decode(ctx);
+
+      ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+
+                       if(length == indefinite_length)
+                       {
+                               ie.decode(vd);
+                               if(ctx)
+                               {
+                                       if(invalid_tag != vd.get_unhandled_tag())
+                                       {
+                                               tag_value_t _tag = vd.get_unhandled_tag();
+                                               if(IE::constraint_t::extended) //skip the unknown extension now
+                                               {
+                                                       tag_value_t const* tag_ptr = &_tag;
+                                                       size_t _length;
+                                                       do
+                                                       {
+                                                               _tag = OpenType::decode(ctx, _length, tag_ptr);
+                                                               tag_ptr = nullptr;
+
+                                                       } while(ctx && !(_tag == 0 && _length == 0));
+                                               }
+                                               else // it should be the end-of-contents octets (8.1.5)
+                                               {
+                                                       uint8_t const* data_in = ctx.refBuffer().getBytes(1);
+                                                       if(data_in && (_tag || data_in[0]))
+                                                       {
+                                                               ctx.refErrorCtx().errorWrongEndOfContent();
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               uint8_t const* data_in = ctx.refBuffer().getBytes(2);
+                                               if(data_in && (data_in[0] || data_in[1]))
+                                               {
+                                                       ctx.refErrorCtx().errorWrongEndOfContent();
+                                               }
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               if (buffer.getBytesLeft() < length)
+                               {
+                                       ctx.refErrorCtx().lengthErrorBytes(buffer.getBytesLeft(), length);
+                               }
+                               else
+                               {
+                                       DecoderCtx::buf_type::pointer end = buffer.end();
+                                       buffer.set_end(buffer.begin() + length);
+                                       ie.decode(vd);
+                                       tag_value_t _tag = vd.get_unhandled_tag();
+                                       if(invalid_tag != _tag)
+                                       {
+                                               if(IE::constraint_t::extended) //skip the unknown extension now
+                                               {
+                                                       tag_value_t const* tag_ptr = &_tag;
+                                                       size_t _length;
+                                                       do
+                                                       {
+                                                               _tag = OpenType::decode(ctx, _length, tag_ptr);
+                                                               tag_ptr = nullptr;
+
+                                                       } while(ctx && buffer.getBytesLeft() > 0);
+                                               }
+                                               else
+                                                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag)); // unexpected tag
+                                       }
+                                       buffer.set_end(end);
+                               }
+                       }
+
+               }
+       }
+};
+/***************************************************************************************
+* SEQUENCE OF:         Encoding the sequence-of type (X.690 8.10)
+* SET OF:              Encoding the set-of type (X.690 8.12)
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<(IE::ie_type == element_type::T_SEQUENCE_OF) || (IE::ie_type == element_type::T_SET_OF)> >
+{
+       using tag_t = Tag<IE, true>;
+
+       static bool inline is_matched(tag_value_t tag) {return tag == tag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+    auto & buffer = ctx.refBuffer();
+
+               tag_t::encode(ctx);
+    u8* len_ptr = buffer.advance(1); //reserve for the length
+
+               for (auto& elem : ie)
+               {
+                       Element<typename IE::element_t>::run(elem, ctx);
+               }
+
+    size_t len = buffer.begin() - len_ptr - 1;
+    if(len > 127)
+    {
+      len_ptr[0] = 0x80; //undefinite length form per X.690 8.1.3.6
+                 uint8_t buff[2] = {0}; // end-of-contents octets (X.690 8.1.5)
+                 buffer.putBytes(buff, sizeof(buff));
+    }
+    else
+      len_ptr[0] = static_cast<u8>(len); //one byte form per X.690 8.1.3.4
+       }
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               ie.clear();
+
+               if(tag_t::value() != tag)
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               else
+               {
+                       auto & buffer = ctx.refBuffer();
+
+                       size_t length = Length::decode(ctx);
+
+      ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+
+                       if(length == indefinite_length)
+                       {
+                               tag_value_t elm_tag = get_tag(ctx);
+
+                               while(ctx && Element<typename IE::element_t>::is_matched(elm_tag))
+                               {
+                                       add_element(ie, ctx, &elm_tag);
+                                       elm_tag = get_tag(ctx);
+                               }
+
+                               if(ctx)
+                               {
+                                       uint8_t const* data_in = ctx.refBuffer().getBytes(1);
+                                       if(data_in && (elm_tag || data_in[0]))
+                                       {
+                                               ctx.refErrorCtx().errorWrongEndOfContent();
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               if (buffer.getBytesLeft() < length)
+                               {
+                                       ctx.refErrorCtx().lengthErrorBytes(buffer.getBytesLeft(), length);
+                               }
+                               else
+                               {
+                                       DecoderCtx::buf_type::pointer end = buffer.end();
+                                       buffer.set_end(buffer.begin() + length);
+
+                                       while(ctx && buffer.getBytesLeft() > 0)
+                                               add_element(ie, ctx);
+
+                                       buffer.set_end(end);
+                               }
+                       }
+               }
+       }
+
+private:
+       static void inline add_element(IE& ie, DecoderCtx& ctx, tag_value_t const* elm_tag = nullptr)
+       {
+               uint8_t* data = ctx.refAllocator().alloc_bytes(sizeof(typename IE::value_type));
+               if (data)
+               {
+                       typename IE::value_type * v = new (data) typename IE::value_type;
+                       v->clear();
+                       ie.push_back(*v);
+                       Element<typename IE::element_t>::run(*v, ctx, elm_tag);
+               }
+               else
+               {
+                       ctx.refErrorCtx().allocatorNoMem(0, sizeof(typename IE::value_type));
+               }
+       }
+};
+
+/***************************************************************************************
+* CHOICE: Encoding the choice type
+***************************************************************************************/
+struct ChoiceVisitorEncoder
+{
+       ChoiceVisitorEncoder(EncoderCtx& ctx) : m_ctx(ctx) {}
+
+       template<typename IE>
+       bool operator()(IE const& ie)
+       {
+               Element<IE>::run(ie, m_ctx);
+               return static_cast<bool>(m_ctx);
+       }
+
+       EncoderCtx&             m_ctx;
+};
+
+struct ChoiceVisitorDecoder
+{
+       ChoiceVisitorDecoder(DecoderCtx& ctx, tag_value_t tag) : m_ctx(ctx), m_tag(tag) {}
+
+       template<typename IE> bool operator()(IE& ie)
+       {
+               Element<IE>::run(ie, m_ctx, &m_tag);
+               return static_cast<bool>(m_ctx);
+       }
+
+       DecoderCtx&             m_ctx;
+       tag_value_t     m_tag;
+};
+
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_CHOICE && IE::asn_identifier_t::class_type == class_type_t::UNSPECIFIED> >
+{
+       struct Selector
+       {
+               Selector(tag_value_t tag) : m_tag(tag) {}
+
+               template<typename ELM> void operator()(size_t idx)
+               {
+                       if(!m_valid && Element<ELM>::is_matched(m_tag))
+                       {
+                               m_index = idx;
+                               m_valid = true;
+                       }
+               }
+
+               size_t get_idx() const {return m_index;}
+               bool is_valid() const {return m_valid;}
+
+       private:
+               tag_value_t m_tag;
+               size_t          m_index {0};
+               bool            m_valid {false};
+       };
+
+  static bool inline is_matched(tag_value_t tag)
+       {
+    Selector selector {tag};
+       IE::enumerate(selector);
+       return selector.is_valid();
+       }
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               ChoiceVisitorEncoder ve(ctx);
+
+               if(ctx && !ie.encode(ve))
+                       ctx.refErrorCtx().tagError(ie.get_index());
+       }
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               ie.clear();
+
+               Selector selector {tag};
+               IE::enumerate(selector);
+
+               if(!selector.is_valid())
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               else
+               {
+                       ChoiceVisitorDecoder vd {ctx, tag};
+                       if(ctx && !ie.decode(selector.get_idx(), vd))
+                               ctx.refErrorCtx().tagError(ie.get_index());
+               }
+       }
+};
+
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_CHOICE && IE::asn_identifier_t::class_type != class_type_t::UNSPECIFIED> >
+{
+       static bool inline is_matched(tag_value_t _tag) {return ExplicitCodec<IE>::is_matched(_tag);}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+    ExplicitCodec<IE>::run(ie, ctx);
+       }
+
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+    ExplicitCodec<IE>::run(ie, ctx, tag);
+       }
+};
+
+/***************************************************************************************
+* IE_OBJECT_IDENTIFIER: Encoding the object identifier type
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_OBJECTIDENTIFIER> >
+{
+       using tag_t = Tag<IE, false>;
+
+       static bool inline is_matched(tag_value_t _tag) {return _tag == tag_t::value();}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               tag_t::encode(ctx);
+               size_t length = ie.get().size();
+               Length::encode(length, ctx);
+               ctx.refBuffer().putBytes(ie.get().data(), length);
+       }
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               ie.clear();
+               if(tag_t::value() == tag)
+               {
+                       size_t length = Length::decode(ctx);
+                       if(!length || length == indefinite_length)
+                       {
+                               ctx.refErrorCtx().sizeRangeError(length);
+                       }
+                       else
+                       {
+        ASN_DECODER_TRACE("IE<type=%d name=%s tag=0x%x length=%zu> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), static_cast<int>(tag), length, ctx.refBuffer().toString());
+                               uint8_t const* data_in = ctx.refBuffer().getBytes(length);
+                               if(data_in)
+                               {
+                                       ie.set(length, data_in);
+                               }
+                       }
+               }
+               else
+               {
+                       ctx.refErrorCtx().tagError(static_cast<uint32_t>(tag));
+               }
+       }
+};
+
+/***************************************************************************************
+* T_OBJFIELD_FTV
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_OBJFIELD_FTV> >
+{
+       using tag_t = Tag<IE, false>;
+
+       static bool inline is_matched(tag_value_t _tag) {return _tag == tag_t::value();}
+};
+
+/***************************************************************************************
+* T_OBJFIELD_TF
+***************************************************************************************/
+template <class IE>
+struct ElementType<IE, std::enable_if_t<IE::ie_type == element_type::T_OBJFIELD_TF> >
+{
+       struct Selector
+       {
+               Selector(tag_value_t tag) : m_tag(tag) {}
+
+               template<typename ELM> void operator()(size_t idx)
+               {
+                       if(Element<ELM>::is_matched(m_tag))
+                       {
+                               m_index = idx;
+                               m_valid = true;
+                       }
+               }
+
+               size_t get_idx() const {return m_index;}
+               bool is_valid() const {return m_valid;}
+
+       private:
+               tag_value_t m_tag;
+               size_t          m_index {0};
+               bool            m_valid {false};
+       };
+
+       static bool inline is_matched(tag_value_t tag)
+       {
+               Selector selector(tag);
+               IE::enumerate(selector);
+
+               return selector.is_valid();
+       }
+};
+
+/***************************************************************************************
+* Identifier
+***************************************************************************************/
+template <class IE, class Enabler = void>
+struct Identifier
+{
+       static bool inline is_matched(tag_value_t _tag)
+       {
+               return ElementType<IE>::is_matched(_tag);
+       }
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               ElementType<IE>::run(ie, ctx);
+       }
+
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+               ElementType<IE>::run(ie, ctx, tag);
+       }
+};
+
+template <class IE>
+struct Identifier<IE, std::enable_if_t<IE::asn_identifier_t::tag_type == tag_type_t::EXPLICIT> >
+{
+       static bool inline is_matched(tag_value_t _tag) {return ExplicitCodec<IE>::is_matched(_tag);}
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+    ExplicitCodec<IE>::run(ie, ctx);
+       }
+
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t tag)
+       {
+    ExplicitCodec<IE>::run(ie, ctx, tag);
+       }
+};
+
+/***************************************************************************************
+* COMMON: Element
+***************************************************************************************/
+template <class IE>
+struct Element
+{
+       static bool inline is_matched(tag_value_t _tag)
+       {
+               return Identifier<IE>::is_matched(_tag);
+       }
+
+       static void inline run(IE const& ie, EncoderCtx& ctx)
+       {
+               if (ctx)
+               {
+                       ASN_ENCODER_TRACE("IE<type=%d name=%s> buffer: %s", static_cast<int>(IE::ie_type), IE::name(), ctx.refBuffer().toString());
+                       ctx.ie_name(IE::name());
+                       Identifier<IE>::run(ie, ctx);
+               }
+       }
+       static void inline run(IE& ie, DecoderCtx& ctx, tag_value_t const* tag_ptr = nullptr)
+       {
+               if (ctx)
+               {
+                       ctx.ie_name(IE::name());
+
+                       tag_value_t tag = tag_ptr ? *tag_ptr : get_tag(ctx);
+                       if(ctx)
+                               Identifier<IE>::run(ie, ctx, tag);
+               }
+       }
+};
+
+} //namespace ber
+} //namespace asn