Add hand-written encoder/decoder for RLC/MAC UL/DL data blocks

Their format is simply too complex to be used with the automatic RAW
encoder/decoder.  Let's implement it by hand, using the automatic
coder whenever possible.
diff --git a/library/RLCMAC_EncDec.cc b/library/RLCMAC_EncDec.cc
new file mode 100644
index 0000000..b2f0525
--- /dev/null
+++ b/library/RLCMAC_EncDec.cc
@@ -0,0 +1,265 @@
+#include "RLCMAC_Types.hh"
+#include "GSM_Types.hh"
+/* Decoding of TS 44.060 GPRS RLC/MAC blocks, portions requiring manual functions
+ * beyond what TITAN RAW coder can handle internally.
+ *
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ */
+
+namespace RLCMAC__Types {
+
+OCTETSTRING enc__RlcmacDlDataBlock(const RlcmacDlDataBlock& si)
+{
+	RlcmacDlDataBlock in = si;
+	OCTETSTRING ret_val;
+	TTCN_Buffer ttcn_buffer;
+	int i;
+
+	/* Fix 'e' bit of initial header based on following blocks */
+	if (!in.blocks().is_bound() || (in.blocks().size_of() == 1 && !in.blocks()[0].hdr().is_bound()))
+		in.mac__hdr().hdr__ext().e() = true;
+	else
+		in.mac__hdr().hdr__ext().e() = false;
+
+	/* use automatic/generated decoder for header */
+	in.mac__hdr().encode(DlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+
+	/* Add LI octets, if any */
+	if (in.blocks().is_bound() &&
+	    (in.blocks().size_of() != 1 || in.blocks()[0].hdr().is_bound())) {
+		/* first write LI octets */
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			/* fix the 'E' bit in case it is not clear */
+			if (i < in.blocks().size_of()-1)
+				in.blocks()[i].hdr().e() = false;
+			else
+				in.blocks()[i].hdr().e() = true;
+			in.blocks()[i].hdr().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+		}
+	}
+	if (in.blocks().is_bound()) {
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			if (!in.blocks()[i].is_bound())
+				continue;
+			ttcn_buffer.put_string(in.blocks()[i].payload());
+		}
+	}
+
+	ttcn_buffer.get_string(ret_val);
+	return ret_val;
+}
+
+RlcmacDlDataBlock dec__RlcmacDlDataBlock(const OCTETSTRING& stream)
+{
+	RlcmacDlDataBlock ret_val;
+	TTCN_Buffer ttcn_buffer(stream);
+	int num_llc_blocks = 0;
+
+	/* use automatic/generated decoder for header */
+	ret_val.mac__hdr().decode(DlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+
+	/* optional extension octets, containing LI+M+E of Llc blocks */
+	if (ret_val.mac__hdr().hdr__ext().e() == false) {
+		/* extension octet follows, i.e. optional Llc length octets */
+		while (1) {
+			/* decode one more extension octet with LlcBlocHdr inside */
+			LlcBlock lb;
+			lb.hdr().decode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+			ret_val.blocks()[num_llc_blocks++] = lb;
+
+			/* if E == '1'B, we can proceed further */
+			if (lb.hdr().e() == true)
+				break;
+		}
+	}
+
+	/* RLC blocks at end */
+	if (ret_val.mac__hdr().hdr__ext().e() == true) {
+		LlcBlock lb;
+		unsigned int length = ttcn_buffer.get_read_len();
+		/* LI not present: The Upper Layer PDU that starts with the current RLC data block either
+		 * fills the current RLC data block precisely or continues in the following in-sequence RLC
+		 * data block */
+		lb.payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
+		ttcn_buffer.increase_pos(length);
+		ret_val.blocks()[0] = lb;
+	} else {
+		if (ret_val.blocks().is_bound()) {
+			for (int i = 0; i < ret_val.blocks().size_of(); i++) {
+				unsigned int length = ret_val.blocks()[i].hdr().length__ind();
+				if (length > ttcn_buffer.get_read_len())
+					length = ttcn_buffer.get_read_len();
+				ret_val.blocks()[i].payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
+				ttcn_buffer.increase_pos(length);
+			}
+		}
+	}
+
+	return ret_val;
+}
+
+
+OCTETSTRING enc__RlcmacUlDataBlock(const RlcmacUlDataBlock& si)
+{
+	RlcmacUlDataBlock in = si;
+	OCTETSTRING ret_val;
+	TTCN_Buffer ttcn_buffer;
+	int i;
+
+	/* Fix 'e' bit of initial header based on following blocks */
+	if (!in.blocks().is_bound() || (in.blocks().size_of() == 1 && !in.blocks()[0].hdr().is_bound()))
+		in.mac__hdr().e() = true;
+	else
+		in.mac__hdr().e() = false;
+
+	/* Fix other presence indications */
+	in.mac__hdr().tlli__ind() = in.tlli().is_bound();
+	in.mac__hdr().pfi__ind() = in.pfi().is_bound();
+
+	/* use automatic/generated decoder for header */
+	in.mac__hdr().encode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+
+	/* Add LI octets, if any */
+	if (in.blocks().is_bound() &&
+	    (in.blocks().size_of() != 1 || in.blocks()[0].hdr().is_bound())) {
+		/* first write LI octets */
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			/* fix the 'E' bit in case it is not clear */
+			if (i < in.blocks().size_of()-1)
+				in.blocks()[i].hdr().e() = false;
+			else
+				in.blocks()[i].hdr().e() = true;
+			in.blocks()[i].hdr().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+		}
+	}
+
+	if (in.mac__hdr().tlli__ind()) {
+		/* FIXME */
+		//in.tlli().encode(GSM__Types::GprsTlli_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+		INTEGER t = in.tlli();
+		unsigned int tmp = t.get_long_long_val();
+		ttcn_buffer.put_c(tmp >> 24);
+		ttcn_buffer.put_c(tmp >> 16);
+		ttcn_buffer.put_c(tmp >> 8);
+		ttcn_buffer.put_c(tmp);
+	}
+
+	if (in.mac__hdr().pfi__ind()) {
+		in.pfi().encode(RlcmacUlDataBlock_pfi_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+	}
+
+	if (in.blocks().is_bound()) {
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			if (!in.blocks()[i].is_bound())
+				continue;
+			ttcn_buffer.put_string(in.blocks()[i].payload());
+		}
+	}
+
+	ttcn_buffer.get_string(ret_val);
+	return ret_val;
+}
+
+RlcmacUlDataBlock dec__RlcmacUlDataBlock(const OCTETSTRING& stream)
+{
+	RlcmacUlDataBlock ret_val;
+	TTCN_Buffer ttcn_buffer(stream);
+	int num_llc_blocks = 0;
+
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("==================================\n"
+				"dec_RlcmacUlDataBlock(): Stream before decoding: ");
+	stream.log();
+	TTCN_Logger::end_event();
+
+	/* use automatic/generated decoder for header */
+	ret_val.mac__hdr().decode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream after decoding hdr: ");
+	ttcn_buffer.log();
+	TTCN_Logger::end_event();
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val after decoding hdr: ");
+	ret_val.log();
+	TTCN_Logger::end_event();
+
+	/* Manually decoder remainder of ttcn_buffer, containing optional header octets, 
+	 * optional tlli, optional pfi and LLC Blocks */
+
+	/* optional extension octets, containing LI+M+E of Llc blocks */
+	if (ret_val.mac__hdr().e() == false) {
+		/* extension octet follows, i.e. optional Llc length octets */
+		while (1) {
+			/* decode one more extension octet with LlcBlocHdr inside */
+			LlcBlock lb;
+			lb.hdr().decode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+			ret_val.blocks()[num_llc_blocks++] = lb;
+
+			TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+			TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream after decoding ExtOct: ");
+			ttcn_buffer.log();
+			TTCN_Logger::end_event();
+			TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+			TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val after decoding ExtOct: ");
+			ret_val.log();
+			TTCN_Logger::end_event();
+
+			/* if E == '1'B, we can proceed further */
+			if (lb.hdr().e() == true)
+				break;
+		}
+	}
+
+	/* parse optional TLLI */
+	if (ret_val.mac__hdr().tlli__ind()) {
+		/* FIXME: Why is this not working ?!? */
+		//ret_val.tlli().decode(GSM__Types::GprsTlli_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+		const unsigned char *cur = ttcn_buffer.get_read_data();
+		unsigned int tmp = cur[0] << 24 | cur[1] << 16 | cur[2] << 8 | cur[3];
+		INTEGER t;
+		t.set_long_long_val(tmp);
+		ret_val.tlli() = t;
+		ttcn_buffer.increase_pos(4);
+	}
+	/* parse optional PFI */
+	if (ret_val.mac__hdr().pfi__ind()) {
+		ret_val.pfi().decode(RlcmacUlDataBlock_pfi_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+	}
+
+	/* RLC blocks at end */
+	if (ret_val.mac__hdr().e() == true) {
+		LlcBlock lb;
+		unsigned int length = ttcn_buffer.get_read_len();
+		/* LI not present: The Upper Layer PDU that starts with the current RLC data block either
+		 * fills the current RLC data block precisely or continues in the following in-sequence RLC
+		 * data block */
+		lb.payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
+		ttcn_buffer.increase_pos(length);
+		ret_val.blocks()[0] = lb;
+	} else {
+		if (ret_val.blocks().is_bound()) {
+			for (int i = 0; i < ret_val.blocks().size_of(); i++) {
+				unsigned int length = ret_val.blocks()[i].hdr().length__ind();
+				if (length > ttcn_buffer.get_read_len())
+					length = ttcn_buffer.get_read_len();
+				ret_val.blocks()[i].payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
+				ttcn_buffer.increase_pos(length);
+			}
+		}
+	}
+
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream before return: ");
+	ttcn_buffer.log();
+	TTCN_Logger::end_event();
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val before return: ");
+	ret_val.log();
+	TTCN_Logger::end_event();
+
+	return ret_val;
+}
+
+
+} // namespace