--- /dev/null
+#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/per/common.hpp"
+#include "asn/per/visitor.hpp"
+
+namespace asn {
+namespace per {
+
+/******************************************************************
+ * Adapter
+ *****************************************************************/
+template <class Container, typename IE, class Enabler = void>
+struct Adapter
+{
+ static void inline run(IE const& ie, EncoderCtx& ctx, Container const& cont)
+ {
+ Element<IE, IE::ie_type>::run(ie, ctx);
+ }
+
+ static void inline run(IE& ie, DecoderCtx& ctx, Container const& cont)
+ {
+ Element<IE, IE::ie_type>::run(ie, ctx);
+ }
+};
+template <class Container, typename IE>
+struct Adapter<Container, IE, std::enable_if_t< (IE::ie_type == element_type::T_OBJFIELD_FTV) >>
+{
+ static void inline run(IE const& ie, EncoderCtx& ctx, Container const& cont)
+ {
+ VisitorEncoder v(ctx);
+ bool rv = ie.encode(v, cont);
+ if(!rv && static_cast<bool>(ctx))
+ {
+ ctx.refErrorCtx().errorNoObject(Container::name());
+ }
+ }
+
+ static void inline run(IE& ie, DecoderCtx& ctx, Container const& cont)
+ {
+ VisitorDecoder v(ctx);
+ bool rv = ie.decode(v, cont);
+ if(!rv && static_cast<bool>(ctx))
+ {
+ ctx.refErrorCtx().errorNoObject(Container::name());
+ }
+ }
+};
+template <class Container, typename IE>
+struct Adapter<Container, IE, std::enable_if_t< (IE::ie_type == element_type::T_OBJFIELD_TF)> >
+{
+ static void inline run(IE const& ie, EncoderCtx& ctx, Container const& cont)
+ {
+ OpenTypeVisitorEncoder v(ctx);
+ bool rv = ie.encode(v, cont);
+ if(!rv && static_cast<bool>(ctx))
+ {
+ ctx.refErrorCtx().errorNoObject(Container::name());
+ }
+ }
+
+ static void inline run(IE& ie, DecoderCtx& ctx, Container const& cont)
+ {
+ OpenTypeVisitorDecoder v(ctx);
+ bool rv = ie.decode(v, cont);
+ if(!rv && static_cast<bool>(ctx))
+ {
+ ctx.refErrorCtx().errorNoObject(Container::name());
+ }
+ if(ie.is_unknown()) //skip the unknown Open Type
+ {
+ size_t size = LengthDeterminantDefault::run(ctx);
+ DecoderCtx::buf_type& buffer = ctx.refBuffer();
+ if (buffer.getBytesLeft() < size)
+ ctx.refErrorCtx().lengthErrorBytes(buffer.getBytesLeft(), size);
+ else
+ buffer.advance(size);
+ }
+ }
+};
+
+/******************************************************************
+ * RootEncoder
+ *****************************************************************/
+template <class Container>
+struct RootEncoder
+{
+ RootEncoder(EncoderCtx& ctx, Container const& cont) : m_ctx(ctx), m_cont(cont) {}
+
+ template <typename IE>
+ bool operator() (IE const& ie) const
+ {
+ if(!IE::extension)
+ {
+ if(IE::optional && !ie.is_valid())
+ return static_cast<bool>(m_ctx);
+ Adapter<Container, IE>::run(ie, m_ctx, m_cont);
+ }
+ return static_cast<bool>(m_ctx);
+ }
+ EncoderCtx& m_ctx;
+ Container const& m_cont;
+};
+
+/******************************************************************
+ * RootDecoder
+ *****************************************************************/
+template <class Container>
+struct RootDecoder
+{
+ RootDecoder(DecoderCtx& ctx, Container const& cont) : m_ctx(ctx), m_cont(cont) {}
+
+ template <typename IE>
+ bool operator() (IE& ie) const
+ {
+ if(!IE::extension)
+ {
+ if(IE::optional)
+ {
+ if(m_ctx.map_elm())
+ {
+ ie.setpresent(true);
+ Adapter<Container, IE>::run(ie, m_ctx, m_cont);
+ }
+ else
+ ie.setpresent(false);
+ }
+ else
+ {
+ Adapter<Container, IE>::run(ie, m_ctx, m_cont);
+ }
+ }
+ return static_cast<bool>(m_ctx);
+ }
+ DecoderCtx& m_ctx;
+ Container const& m_cont;
+};
+
+/******************************************************************
+ * ExtEncoder
+ *****************************************************************/
+template <class Container>
+struct ExtEncoder
+{
+ explicit ExtEncoder(EncoderCtx& ctx, Container const& cont) : m_ctx(ctx), m_cont(cont) {}
+
+ template <typename IE>
+ bool operator() (IE const& ie) const
+ {
+ if(!IE::extension)
+ return static_cast<bool>(m_ctx);
+
+ if(IE::optional && !ie.is_valid())
+ return static_cast<bool>(m_ctx);
+
+ size_t reserved_size = 1; //1 byte length is most likely
+ Tools::bit_accessor::padByte(m_ctx.refBuffer());
+
+ if (m_ctx.refBuffer().getBytesLeft())
+ {
+ EncoderCtx::buf_type::pointer p = m_ctx.refBuffer().advance(reserved_size);
+ EncoderCtx::buf_type::pointer start = p + reserved_size;
+
+ Adapter<Container, IE>::run(ie, m_ctx, m_cont);
+
+ if (m_ctx)
+ {
+ Tools::bit_accessor::padByte(m_ctx.refBuffer());
+ EncoderCtx::buf_type::pointer p_new = m_ctx.refBuffer().begin();
+ size_t size = p_new - start;
+ size_t needed = LengthDeterminantDefault::bytes_needed(size) - reserved_size;
+ if (needed) //1 byte is not enough for the length determinant. it is hardly possible
+ {
+ if (m_ctx.refBuffer().getBytesLeft() < needed) {
+ m_ctx.refErrorCtx().lengthErrorBytes(m_ctx.refBuffer().getBytesLeft(), needed);
+ return false;
+ }
+ memmove(start + needed, start, size);
+ p_new += needed;
+ }
+ m_ctx.refBuffer().set_begin(p, 0);
+ LengthDeterminantDefault::run(m_ctx, size);
+ if (m_ctx) {
+ m_ctx.refBuffer().set_begin(p_new, 0);
+ Tools::bit_accessor::padByte(m_ctx.refBuffer());
+ }
+ }
+ }
+ else
+ {
+ m_ctx.refErrorCtx().lengthErrorBytes(m_ctx.refBuffer().getBytesLeft(), reserved_size);
+ return false;
+ }
+ return static_cast<bool>(m_ctx);
+ }
+ EncoderCtx& m_ctx;
+ Container const& m_cont;
+};
+
+/******************************************************************
+ * ExtDecoder
+ *****************************************************************/
+template <class Container>
+struct ExtDecoder
+{
+ explicit ExtDecoder(DecoderCtx& ctx, Container const& cont) : m_ctx(ctx), m_cont(cont) {}
+
+ template <typename IE>
+ bool operator() (IE& ie) const
+ {
+ if(!IE::extension)
+ return static_cast<bool>(m_ctx);
+
+ if (m_ctx.map_elm())
+ {
+ ie.setpresent(true);
+ size_t size = LengthDeterminantDefault::run(m_ctx);
+ DecoderCtx::buf_type& buffer = m_ctx.refBuffer();
+ if (buffer.getBytesLeft() < size)
+ {
+ m_ctx.refErrorCtx().lengthErrorBytes(buffer.getBytesLeft(), size);
+ return false;
+ }
+ DecoderCtx::buf_type::pointer end = buffer.end();
+ buffer.set_end(buffer.begin() + size);
+ Adapter<Container, IE>::run(ie, m_ctx, m_cont);
+ Tools::bit_accessor::padByte(m_ctx.refBuffer());
+ buffer.set_end(end);
+ }
+ else
+ {
+ ie.setpresent(false);
+ }
+
+ return static_cast<bool>(m_ctx);
+ }
+ DecoderCtx& m_ctx;
+ Container const& m_cont;
+};
+
+/******************************************************************
+ * SeqMap
+ *****************************************************************/
+template<class IE, class Enabler = void>
+struct SeqMap;
+
+/*
+template <class IE>
+struct SeqMap<IE, std::enable_if_t< (IE::num_opt_entries > 8) > >
+{
+ static void inline run(EncoderCtx& ctx, size_t val)
+ {
+ u8 k = IE::num_opt_entries / 8; // num of full bytes in a seq map mask
+ u8 fb_bits = IE::num_opt_entries % 8; // num of first byte bits (without padding)
+
+ if (fb_bits) {
+ // Put a part of a first byte (without padding, only seq map own bits)
+ Tools::bit_accessor::put((u8)(val >> 8*k), fb_bits, ctx.refBuffer());
+ }
+
+ // Put other bytes (full bytes)
+ for (int i = (k-1); i >= 0; i--) {
+ Tools::bit_accessor::put((u8)(val >> 8*i), 8, ctx.refBuffer());
+ }
+ }
+
+ static DecoderCtx::map_type inline run(DecoderCtx& ctx)
+ {
+ uint num_opts = IE::num_opt_entries;
+ u64 res = 0;
+ u8 k = 1;
+ u8 lb_bits = IE::num_opt_entries % 8; // num of last byte bits (without padding)
+
+ while (num_opts >= 8) {
+ size_t shift = sizeof(DecoderCtx::map_type) * CHAR_BIT - 8*k;
+
+ DecoderCtx::map_type rval = Tools::bit_accessor::get(8, ctx.refBuffer());
+
+ rval = rval << shift;
+ res |= rval;
+
+ k++;
+ num_opts -= 8;
+ }
+
+ if (lb_bits > 0) {
+ // (8 - lb_bits) - padding in a last byte
+ size_t shift = sizeof(DecoderCtx::map_type) * CHAR_BIT - 8*k + 8 - lb_bits;
+
+ DecoderCtx::map_type rval = Tools::bit_accessor::get(lb_bits, ctx.refBuffer());
+
+ rval = rval << shift;
+ res |= rval;
+ }
+
+ return res;
+ }
+}
+;
+*/
+
+template<class IE>
+struct SeqMap<IE, std::enable_if_t< (IE::num_opt_entries <= 8)> > //todo: to add code for more than 8 optional elements
+{
+ static void inline run(EncoderCtx& ctx, size_t val)
+ {
+ Tools::bit_accessor::put((u8)val, IE::num_opt_entries, ctx.refBuffer());
+ }
+ static DecoderCtx::map_type inline run(DecoderCtx& ctx)
+ {
+ size_t shift = sizeof(DecoderCtx::map_type) * CHAR_BIT - IE::num_opt_entries;
+ DecoderCtx::map_type rval = Tools::bit_accessor::get(IE::num_opt_entries, ctx.refBuffer());
+ rval = rval << shift;
+ return rval;
+ }
+};
+
+/******************************************************************
+ * SeqExtMap
+ *****************************************************************/
+template<class IE, bound_t NumExtEntries, class Enabler = void>
+struct SeqExtMap
+{
+ static void inline run(EncoderCtx& ctx, size_t val) {}
+ static DecoderCtx::map_type inline run(DecoderCtx& ctx)
+ {
+ size_t len = NormallySmallLength<1>::run(ctx);
+ size_t shift = sizeof(DecoderCtx::map_type) * CHAR_BIT - len;
+ u8 tmp;
+ DecoderCtx::map_type rval{ 0 };
+ u8 val;
+
+ while (len) {
+ tmp = len > 8 ? 8 : (u8)len;
+ val = Tools::bit_accessor::get(tmp, ctx.refBuffer());
+ rval = rval << tmp;
+ rval |= val;
+ len -= tmp;
+ }
+ rval = rval << shift;
+ return rval;
+ }
+};
+
+template<class IE, bound_t NumExtEntries>
+struct SeqExtMap<IE, NumExtEntries, std::enable_if_t< ((NumExtEntries <= 8) && (NumExtEntries > 0)) > >
+{
+ static void inline run(EncoderCtx& ctx, size_t val)
+ {
+ NormallySmallLength<NumExtEntries>::run(ctx);
+ Tools::bit_accessor::put((u8)val, NumExtEntries, ctx.refBuffer());
+ }
+};
+
+template<class IE, bound_t NumExtEntries>
+struct SeqExtMap<IE, NumExtEntries, std::enable_if_t< (NumExtEntries > 8) > >
+{
+ static void inline run(EncoderCtx& ctx, size_t val)
+ {
+ NormallySmallLength<NumExtEntries>::run(ctx);
+
+ size_t len = NumExtEntries;
+ size_t tmp;
+
+ while (len) {
+ tmp = len > 8 ? 8 : len;
+ Tools::bit_accessor::put((u8)val, tmp, ctx.refBuffer());
+ val = val >> tmp;
+ len -= tmp;
+ }
+ }
+};
+
+/******************************************************************
+ * SeqMapBuilder
+ *****************************************************************/
+struct SeqMapBuilder
+{
+ SeqMapBuilder(uint64_t& map) : m_map(map) {}
+
+ template<typename IE>
+ bool operator()(IE const& ie)
+ {
+ if(!IE::extension && IE::optional)
+ {
+ m_map = m_map << 1;
+ if(ie.is_valid())
+ m_map |= 1;
+ }
+ return true;
+ }
+ uint64_t& m_map;
+};
+
+/******************************************************************
+ * SeqExtMapBuilder
+ *****************************************************************/
+struct SeqExtMapBuilder
+{
+ SeqExtMapBuilder(uint64_t& map) : m_map(map) {}
+
+ template<typename IE>
+ bool operator()(IE const& ie)
+ {
+ if(IE::extension)
+ {
+ m_map = m_map << 1;
+ if(!IE::optional || (IE::optional && ie.is_valid()))
+ m_map |= 1;
+ }
+ return true;
+ }
+ uint64_t& m_map;
+};
+
+/******************************************************************
+ * SeqNoExtDefault
+ *****************************************************************/
+// Default (No OPT/DEF present within the extension root)
+template <class IE>
+struct SeqNoExtDefault
+{
+ static void inline run(IE const& ie, EncoderCtx& ctx)
+ {
+ RootEncoder<IE> ve(ctx, ie);
+ ie.encode(ve);
+ }
+ static void inline run(IE& ie, DecoderCtx& ctx, DecoderCtx::map_type map = 0)
+ {
+ RootDecoder<IE> ve(ctx, ie);
+
+ DecoderCtx::map_type old_map = ctx.set_map(map);
+ ie.decode(ve);
+ ctx.set_map(old_map);
+ }
+};
+
+/******************************************************************
+ * SeqNoExt
+ *****************************************************************/
+// Default (No OPT/DEF present within the extension root)
+template <class IE, class Enabler = void>
+struct SeqNoExt
+{
+ static void inline run(IE const& ie, EncoderCtx& ctx)
+ {
+ SeqNoExtDefault<IE>::run(ie, ctx);
+ }
+ static void inline run(IE& ie, DecoderCtx& ctx)
+ {
+ SeqNoExtDefault<IE>::run(ie, ctx);
+ }
+};
+
+//18.2 OPT/DEF present within the extension root
+template <class IE>
+struct SeqNoExt<IE, std::enable_if_t< (IE::num_opt_entries > 0) > >
+{
+ static void inline run(IE const& ie, EncoderCtx& ctx)
+ {
+ uint64_t map = 0;
+ SeqMapBuilder mb(map);
+ ie.encode(mb);
+ SeqMap<IE>::run(ctx, map);
+ SeqNoExtDefault<IE>::run(ie, ctx);
+ }
+ static void inline run(IE& ie, DecoderCtx& ctx)
+ {
+ SeqNoExtDefault<IE>::run(ie, ctx, SeqMap<IE>::run(ctx));
+ }
+};
+
+/******************************************************************
+ * Seq
+ *****************************************************************/
+template <class IE, class Enable = void>
+struct Seq;
+
+template <class IE>
+struct Seq<IE, std::enable_if_t< (IE::constraint_t::extended == true)> >
+{
+ static void inline run(IE const& ie, EncoderCtx& ctx)
+ {
+ uint64_t map = 0;
+ SeqExtMapBuilder emb(map);
+ ie.encode(emb);
+
+ if (map) {
+ Tools::bit_accessor::put(1, 1, ctx.refBuffer());
+ SeqNoExt<IE>::run(ie, ctx);
+ SeqExtMap<IE, IE::num_ext_entries>::run(ctx, map);
+ ExtEncoder<IE> ove(ctx, ie);
+ ie.encode(ove);
+ }
+ else {
+ Tools::bit_accessor::put(0, 1, ctx.refBuffer());
+ SeqNoExt<IE>::run(ie, ctx);
+ }
+ }
+ static void inline run(IE& ie, DecoderCtx& ctx)
+ {
+ u8 ext = Tools::bit_accessor::get(1, ctx.refBuffer());
+ if (ext)
+ {
+ SeqNoExt<IE>::run(ie, ctx);
+
+ auto old_map = ctx.set_map(SeqExtMap<IE, 0>::run(ctx)); // we do not know how many exts can be received
+ {
+ ExtDecoder<IE> ove(ctx, ie);
+ ie.decode(ove);
+
+ auto& buffer = ctx.refBuffer();
+ while(ctx.get_map())
+ {
+ if(ctx.map_elm()) //skip unknown extensions
+ {
+ size_t size = LengthDeterminantDefault::run(ctx);
+ if (buffer.getBytesLeft() < size)
+ {
+ ctx.refErrorCtx().lengthErrorBytes(buffer.getBytesLeft(), size);
+ break;
+ }
+ buffer.advance(size);
+ }
+ }
+ }
+ ctx.set_map(old_map);
+ }
+ else
+ SeqNoExt<IE>::run(ie, ctx);
+ }
+};
+
+template <class IE>
+struct Seq<IE, std::enable_if_t< (IE::constraint_t::extended == false)> >
+{
+ static void inline run(IE const& ie, EncoderCtx& ctx)
+ {
+ SeqNoExt<IE>::run(ie, ctx);
+ }
+ static void inline run(IE& ie, DecoderCtx& ctx)
+ {
+ SeqNoExt<IE>::run(ie, ctx);
+ }
+};
+
+} //namespace per
+} //namespace asn