[PATCH] openbsc[master]: V.42bis: add sourcecode from IAXmodem (SPANDSP)

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

dexter gerrit-no-reply at lists.osmocom.org
Fri Sep 2 11:54:57 UTC 2016


Hello Harald Welte, Jenkins Builder,

I'd like you to reexamine a change.  Please visit

    https://gerrit.osmocom.org/643

to look at the new patch set (#29).

V.42bis: add sourcecode from IAXmodem (SPANDSP)

V.42bis is a data compression method found in modems. It has also
been specified for GPRS as data compression algorithm.

The implementation has been taken from IAXmodem:

https://sourceforge.net/p/iaxmodem/code/HEAD/tree/
svn checkout svn://svn.code.sf.net/p/iaxmodem/code/ iaxmodem-code
Revision: r36

Change-Id: Iabedece9f97ca944a1e3f747bb073e532c4e9dca
---
A openbsc/include/openbsc/v42bis.h
A openbsc/include/openbsc/v42bis_private.h
A openbsc/src/gprs/v42bis.c
3 files changed, 1,031 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/openbsc refs/changes/43/643/29

diff --git a/openbsc/include/openbsc/v42bis.h b/openbsc/include/openbsc/v42bis.h
new file mode 100644
index 0000000..b947a61
--- /dev/null
+++ b/openbsc/include/openbsc/v42bis.h
@@ -0,0 +1,140 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * v42bis.h
+ *
+ * Written by Steve Underwood <steveu at coppice.org>
+ *
+ * Copyright (C) 2005, 2011 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \page v42bis_page V.42bis modem data compression
+\section v42bis_page_sec_1 What does it do?
+The v.42bis specification defines a data compression scheme, to work in
+conjunction with the error correction scheme defined in V.42.
+
+\section v42bis_page_sec_2 How does it work?
+*/
+
+#if !defined(_SPANDSP_V42BIS_H_)
+#define _SPANDSP_V42BIS_H_
+
+#define V42BIS_MIN_STRING_SIZE      6
+#define V42BIS_MAX_STRING_SIZE      250
+#define V42BIS_MIN_DICTIONARY_SIZE  512
+#define V42BIS_MAX_BITS             12
+#define V42BIS_MAX_CODEWORDS        4096    /* 2^V42BIS_MAX_BITS */
+#define V42BIS_MAX_OUTPUT_LENGTH    1024
+
+enum
+{
+    V42BIS_P0_NEITHER_DIRECTION = 0,
+    V42BIS_P0_INITIATOR_RESPONDER,
+    V42BIS_P0_RESPONDER_INITIATOR,
+    V42BIS_P0_BOTH_DIRECTIONS
+};
+
+enum
+{
+    V42BIS_COMPRESSION_MODE_DYNAMIC = 0,
+    V42BIS_COMPRESSION_MODE_ALWAYS,
+    V42BIS_COMPRESSION_MODE_NEVER
+};
+
+/*!
+    V.42bis compression/decompression descriptor. This defines the working state for a
+    single instance of V.42bis compress/decompression.
+*/
+typedef struct v42bis_state_s v42bis_state_t;
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/*! Compress a block of octets.
+    \param s The V.42bis context.
+    \param buf The data to be compressed.
+    \param len The length of the data buffer.
+    \return 0 */
+SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t buf[], int len);
+
+/*! Flush out any data remaining in a compression buffer.
+    \param s The V.42bis context.
+    \return 0 */
+SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s);
+
+/*! Decompress a block of octets.
+    \param s The V.42bis context.
+    \param buf The data to be decompressed.
+    \param len The length of the data buffer.
+    \return 0 */
+SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t buf[], int len);
+    
+/*! Flush out any data remaining in the decompression buffer.
+    \param s The V.42bis context.
+    \return 0 */
+SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s);
+
+/*! Set the compression mode.
+    \param s The V.42bis context.
+    \param mode One of the V.42bis compression modes -
+            V42BIS_COMPRESSION_MODE_DYNAMIC,
+            V42BIS_COMPRESSION_MODE_ALWAYS,
+            V42BIS_COMPRESSION_MODE_NEVER */
+SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode);
+
+/*! Initialise a V.42bis context.
+    \param s The V.42bis context.
+    \param negotiated_p0 The negotiated P0 parameter, from the V.42bis spec.
+    \param negotiated_p1 The negotiated P1 parameter, from the V.42bis spec.
+    \param negotiated_p2 The negotiated P2 parameter, from the V.42bis spec.
+    \param encode_handler Encode callback handler.
+    \param encode_user_data An opaque pointer passed to the encode callback handler.
+    \param max_encode_len The maximum length that should be passed to the encode handler.
+    \param decode_handler Decode callback handler.
+    \param decode_user_data An opaque pointer passed to the decode callback handler.
+    \param max_decode_len The maximum length that should be passed to the decode handler.
+    \return The V.42bis context. */
+SPAN_DECLARE(v42bis_state_t *) v42bis_init(v42bis_state_t *s,
+                                           int negotiated_p0,
+                                           int negotiated_p1,
+                                           int negotiated_p2,
+                                           put_msg_func_t encode_handler,
+                                           void *encode_user_data,
+                                           int max_encode_len,
+                                           put_msg_func_t decode_handler,
+                                           void *decode_user_data,
+                                           int max_decode_len);
+
+/*! Release a V.42bis context.
+    \param s The V.42bis context.
+    \return 0 if OK */
+SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s);
+
+/*! Free a V.42bis context.
+    \param s The V.42bis context.
+    \return 0 if OK */
+SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/openbsc/include/openbsc/v42bis_private.h b/openbsc/include/openbsc/v42bis_private.h
new file mode 100644
index 0000000..2c801eb
--- /dev/null
+++ b/openbsc/include/openbsc/v42bis_private.h
@@ -0,0 +1,127 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/v42bis.h
+ *
+ * Written by Steve Underwood <steveu at coppice.org>
+ *
+ * Copyright (C) 2005 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(_SPANDSP_PRIVATE_V42BIS_H_)
+#define _SPANDSP_PRIVATE_V42BIS_H_
+
+/*!
+    V.42bis dictionary node.
+    Note that 0 is not a valid node to point to (0 is always a control code), so 0 is used
+    as a "no such value" marker in this structure.
+*/
+typedef struct
+{
+    /*! \brief The value of the octet represented by the current dictionary node */
+    uint8_t node_octet;
+    /*! \brief The parent of this node */
+    uint16_t parent;
+    /*! \brief The first child of this node */
+    uint16_t child;
+    /*! \brief The next node at the same depth */
+    uint16_t next;
+} v42bis_dict_node_t;
+
+/*!
+    V.42bis compression or decompression. This defines the working state for a single instance
+    of V.42bis compression or decompression.
+*/
+typedef struct
+{
+    /*! \brief Compression enabled. */
+    int v42bis_parm_p0;
+    /*! \brief Compression mode. */
+    int compression_mode;
+    /*! \brief Callback function to handle output data. */
+    put_msg_func_t handler;
+    /*! \brief An opaque pointer passed in calls to the data handler. */
+    void *user_data;
+    /*! \brief The maximum amount to be passed to the data handler. */
+    int max_output_len;
+
+    /*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */
+    int transparent;
+    /*! \brief Next empty dictionary entry */
+    uint16_t v42bis_parm_c1;
+    /*! \brief Current codeword size */
+    uint16_t v42bis_parm_c2;
+    /*! \brief Threshold for codeword size change */
+    uint16_t v42bis_parm_c3;
+    /*! \brief The current update point in the dictionary */
+    uint16_t update_at;
+    /*! \brief The last entry matched in the dictionary */
+    uint16_t last_matched;
+    /*! \brief The last entry added to the dictionary */
+    uint16_t last_added;
+    /*! \brief Total number of codewords in the dictionary */
+    int v42bis_parm_n2;
+    /*! \brief Maximum permitted string length */
+    int v42bis_parm_n7;
+    /*! \brief The dictionary */
+    v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS];
+
+    /*! \brief The octet string in progress */
+    uint8_t string[V42BIS_MAX_STRING_SIZE];
+    /*! \brief The current length of the octet string in progress */
+    int string_length;
+    /*! \brief The amount of the octet string in progress which has already
+        been flushed out of the buffer */
+    int flushed_length;
+
+    /*! \brief Compression performance metric */
+    uint16_t compression_performance;
+
+    /*! \brief Outgoing bit buffer (compression), or incoming bit buffer (decompression) */ 
+    uint32_t bit_buffer;
+    /*! \brief Outgoing bit count (compression), or incoming bit count (decompression) */ 
+    int bit_count;
+
+    /*! \brief The output composition buffer */
+    uint8_t output_buf[V42BIS_MAX_OUTPUT_LENGTH];
+    /*! \brief The length of the contents of the output composition buffer */    
+    int output_octet_count;
+
+    /*! \brief The current value of the escape code */
+    uint8_t escape_code;
+    /*! \brief TRUE if we just hit an escape code, and are waiting for the following octet */
+    int escaped;
+} v42bis_comp_state_t;
+
+/*!
+    V.42bis compression/decompression descriptor. This defines the working state for a
+    single instance of V.42bis compress/decompression.
+*/
+struct v42bis_state_s
+{
+    /*! \brief Compression state. */
+    v42bis_comp_state_t compress;
+    /*! \brief Decompression state. */
+    v42bis_comp_state_t decompress;
+
+    /*! \brief Error and flow logging control */
+    logging_state_t logging;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/openbsc/src/gprs/v42bis.c b/openbsc/src/gprs/v42bis.c
new file mode 100644
index 0000000..d025ea9
--- /dev/null
+++ b/openbsc/src/gprs/v42bis.c
@@ -0,0 +1,764 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * v42bis.c
+ *
+ * Written by Steve Underwood <steveu at coppice.org>
+ *
+ * Copyright (C) 2005, 2011 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. 
+   Currently it performs the core compression and decompression functions OK.
+   However, a number of the bells and whistles in V.42bis are incomplete. */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/logging.h"
+#include "spandsp/bit_operations.h"
+#include "spandsp/async.h"
+#include "spandsp/v42bis.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/v42bis.h"
+
+/* Fixed parameters from the spec. */
+/* Character size (bits) */
+#define V42BIS_N3                           8
+/* Number of characters in the alphabet */
+#define V42BIS_N4                           256
+/* Index number of first dictionary entry used to store a string */
+#define V42BIS_N5                           (V42BIS_N4 + V42BIS_N6)
+/* Number of control codewords */
+#define V42BIS_N6                           3 
+/* V.42bis/9.2 */
+#define V42BIS_ESC_STEP                     51
+
+/* Compreeibility monitoring parameters for assessing automated switches between
+   transparent and compressed mode */
+#define COMPRESSIBILITY_MONITOR             (256*V42BIS_N3)
+#define COMPRESSIBILITY_MONITOR_HYSTERESIS  11
+
+/* Control code words in compressed mode */
+enum
+{
+    V42BIS_ETM = 0,         /* Enter transparent mode */
+    V42BIS_FLUSH = 1,       /* Flush data */
+    V42BIS_STEPUP = 2       /* Step up codeword size */
+};
+
+/* Command codes in transparent mode */
+enum
+{
+    V42BIS_ECM = 0,         /* Enter compression mode */
+    V42BIS_EID = 1,         /* Escape character in data */
+    V42BIS_RESET = 2        /* Force reinitialisation */
+};
+
+static __inline__ void push_octet(v42bis_comp_state_t *s, int octet)
+{
+    s->output_buf[s->output_octet_count++] = (uint8_t) octet;
+    if (s->output_octet_count >= s->max_output_len)
+    {
+        s->handler(s->user_data, s->output_buf, s->output_octet_count);
+        s->output_octet_count = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len)
+{
+    int i;
+    int chunk;
+
+    i = 0;
+    while ((s->output_octet_count + len - i) >= s->max_output_len)
+    {
+        chunk = s->max_output_len - s->output_octet_count;
+        memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
+        s->handler(s->user_data, s->output_buf, s->max_output_len);
+        s->output_octet_count = 0;
+        i += chunk;
+    }
+    chunk = len - i;
+    if (chunk > 0)
+    {
+        memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
+        s->output_octet_count += chunk;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code)
+{
+    s->bit_buffer |= code << s->bit_count;
+    s->bit_count += s->v42bis_parm_c2;
+    while (s->bit_count >= 8)
+    {
+        push_octet(s, s->bit_buffer & 0xFF);
+        s->bit_buffer >>= 8;
+        s->bit_count -= 8;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void push_octet_alignment(v42bis_comp_state_t *s)
+{
+    if ((s->bit_count & 7))
+    {
+        s->bit_count += (8 - (s->bit_count & 7));
+        while (s->bit_count >= 8)
+        {
+            push_octet(s, s->bit_buffer & 0xFF);
+            s->bit_buffer >>= 8;
+            s->bit_count -= 8;
+        }
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void flush_octets(v42bis_comp_state_t *s)
+{
+    if (s->output_octet_count > 0)
+    {
+        s->handler(s->user_data, s->output_buf, s->output_octet_count);
+        s->output_octet_count = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dictionary_init(v42bis_comp_state_t *s)
+{
+    int i;
+
+    memset(s->dict, 0, sizeof(s->dict));
+    for (i = 0;  i < V42BIS_N4;  i++)
+        s->dict[i + V42BIS_N6].node_octet = i;
+    s->v42bis_parm_c1 = V42BIS_N5;
+    s->v42bis_parm_c2 = V42BIS_N3 + 1;
+    s->v42bis_parm_c3 = V42BIS_N4 << 1;
+    s->last_matched = 0;
+    s->update_at = 0;
+    s->last_added = 0;
+    s->bit_buffer = 0;
+    s->bit_count = 0;
+    s->flushed_length = 0;
+    s->string_length = 0;
+    s->escape_code = 0;
+    s->transparent = TRUE;
+    s->escaped = FALSE;
+    s->compression_performance = COMPRESSIBILITY_MONITOR;
+}
+/*- End of function --------------------------------------------------------*/
+
+static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
+{
+    uint16_t e;
+
+    if (at == 0)
+        return octet + V42BIS_N6;
+    e = s->dict[at].child;
+    while (e)
+    {
+        if (s->dict[e].node_octet == octet)
+            return e;
+        e = s->dict[e].next;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
+{
+    uint16_t newx;
+    uint16_t next;
+    uint16_t e;
+
+    newx = s->v42bis_parm_c1;
+    s->dict[newx].node_octet = octet;
+    s->dict[newx].parent = at;
+    s->dict[newx].child = 0;
+    s->dict[newx].next = s->dict[at].child;
+    s->dict[at].child = newx;
+    next = newx;
+    /* 6.5 Recovering a dictionary entry to use next */
+    do
+    {
+        /* 6.5(a) and (b) */
+        if (++next == s->v42bis_parm_n2)
+            next = V42BIS_N5;
+    }
+    while (s->dict[next].child);
+    /* 6.5(c) We need to reuse a leaf node */
+    if (s->dict[next].parent)
+    {
+        /* 6.5(d) Detach the leaf node from its parent, and re-use it */
+        e = s->dict[next].parent;
+        if (s->dict[e].child == next)
+        {
+            s->dict[e].child = s->dict[next].next;
+        }
+        else
+        {
+            e = s->dict[e].child;
+            while (s->dict[e].next != next)
+                e = s->dict[e].next;
+            s->dict[e].next = s->dict[next].next;
+        }
+    }
+    s->v42bis_parm_c1 = next;
+    return newx;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void send_string(v42bis_comp_state_t *s)
+{
+    push_octets(s, s->string, s->string_length);
+    s->string_length = 0;
+    s->flushed_length = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code)
+{
+    int i;
+    uint16_t p;
+
+    /* Work out the length */
+    for (i = 0, p = code;  p;  i++)
+        p = s->dict[p].parent;
+    s->string_length += i;
+    /* Now expand the known length of string */
+    i = s->string_length - 1;
+    for (p = code;  p;  )
+    {
+        s->string[i--] = s->dict[p].node_octet;
+        p = s->dict[p].parent;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code)
+{
+    int i;
+
+    /* Update compressibility metric */
+    /* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */
+    s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR);
+    if (s->transparent)
+    {
+        for (i = 0;  i < s->string_length;  i++)
+        {
+            push_octet(s, s->string[i]);
+            if (s->string[i] == s->escape_code)
+            {
+                push_octet(s, V42BIS_EID);
+                s->escape_code += V42BIS_ESC_STEP;
+            }
+        }
+    }
+    else
+    {
+        /* Allow for any escape octets in the string */
+        for (i = 0;  i < s->string_length;  i++)
+        {
+            if (s->string[i] == s->escape_code)
+                s->escape_code += V42BIS_ESC_STEP;
+        }
+        /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */
+        while (code >= s->v42bis_parm_c3)
+        {
+            /* We need to increase the codeword size */
+            /* 7.4(a) */
+            push_compressed_code(s, V42BIS_STEPUP);
+            /* 7.4(b) */
+            s->v42bis_parm_c2++;
+            /* 7.4(c) */
+            s->v42bis_parm_c3 <<= 1;
+            /* 7.4(d) this might need to be repeated, so we loop */
+        }
+        /* 7.5 Transfer - output the last state of the string */
+        push_compressed_code(s, code);
+    }
+    s->string_length = 0;
+    s->flushed_length = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void go_compressed(v42bis_state_t *ss)
+{
+    v42bis_comp_state_t *s;
+
+    s = &ss->compress;
+    if (!s->transparent)
+        return;
+    span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n");
+    /* Switch out of transparent now, between codes. We need to send the octet which did not
+       match, just before switching. */
+    if (s->last_matched)
+    {
+        s->update_at = s->last_matched;
+        send_encoded_data(s, s->last_matched);
+        s->last_matched = 0;
+    }
+    push_octet(s, s->escape_code);
+    push_octet(s, V42BIS_ECM);
+    s->bit_buffer = 0;
+    s->transparent = FALSE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void go_transparent(v42bis_state_t *ss)
+{
+    v42bis_comp_state_t *s;
+
+    s = &ss->compress;
+    if (s->transparent)
+        return;
+    span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n");
+    /* Switch into transparent now, between codes, and the unmatched octet should
+       go out in transparent mode, just below */
+    if (s->last_matched)
+    {
+        s->update_at = s->last_matched;
+        send_encoded_data(s, s->last_matched);
+        s->last_matched = 0;
+    }
+    s->last_added = 0;
+    push_compressed_code(s, V42BIS_ETM);
+    push_octet_alignment(s);
+    s->transparent = TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void monitor_for_mode_change(v42bis_state_t *ss)
+{
+    v42bis_comp_state_t *s;
+
+    s = &ss->compress;
+    switch (s->compression_mode)
+    {
+    case V42BIS_COMPRESSION_MODE_DYNAMIC:
+        /* 7.8 Data compressibility test */
+        if (s->transparent)
+        {
+            if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS)
+            {
+                /* 7.8.1 Transition to compressed mode */
+                go_compressed(ss);
+            }
+        }
+        else
+        {
+            if (s->compression_performance > COMPRESSIBILITY_MONITOR)
+            {
+                /* 7.8.2 Transition to transparent mode */
+                go_transparent(ss);
+            }
+        }
+        /* 7.8.3 Reset function - TODO */
+        break;
+    case V42BIS_COMPRESSION_MODE_ALWAYS:
+        if (s->transparent)
+            go_compressed(ss);
+        break;
+    case V42BIS_COMPRESSION_MODE_NEVER:
+        if (!s->transparent)
+            go_transparent(ss);
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v42bis_comp_init(v42bis_comp_state_t *s,
+                            int p1,
+                            int p2,
+                            put_msg_func_t handler,
+                            void *user_data,
+                            int max_output_len)
+{
+    memset(s, 0, sizeof(*s));
+    s->v42bis_parm_n2 = p1;
+    s->v42bis_parm_n7 = p2;
+    s->handler = handler;
+    s->user_data = user_data;
+    s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH)  ?  max_output_len  :  V42BIS_MAX_OUTPUT_LENGTH;
+    s->output_octet_count = 0;
+    dictionary_init(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int comp_exit(v42bis_comp_state_t *s)
+{
+    s->v42bis_parm_n2 = 0;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len)
+{
+    v42bis_comp_state_t *s;
+    int i;
+    uint16_t code;
+
+    s = &ss->compress;
+    if (!s->v42bis_parm_p0)
+    {
+        /* Compression is off - just push the incoming data out */
+        push_octets(s, buf, len);
+        return 0;
+    }
+    for (i = 0;  i < len;  )
+    {
+        /* 6.4 Add the string to the dictionary */
+        if (s->update_at)
+        {
+            if (match_octet(s, s->update_at, buf[i]) == 0)
+                s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]);
+            s->update_at = 0;
+        }
+        /* Match string */
+        while (i < len)
+        {
+            code = match_octet(s, s->last_matched, buf[i]);
+            if (code == 0)
+            {
+                s->update_at = s->last_matched;
+                send_encoded_data(s, s->last_matched);
+                s->last_matched = 0;
+                break;
+            }
+            if (code == s->last_added)
+            {
+                s->last_added = 0;
+                send_encoded_data(s, s->last_matched);
+                s->last_matched = 0;
+                break;
+            }
+            s->last_matched = code;
+            /* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry
+                      created by the last invocation of the string matching procedure, then the
+                      next character shall be read and appended to the string and this step
+                      repeated. */
+            s->string[s->string_length++] = buf[i++];
+            /* 6.4(a) The string must not exceed N7 in length */
+            if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
+            {
+                send_encoded_data(s, s->last_matched);
+                s->last_matched = 0;
+                break;
+            }
+        }
+        monitor_for_mode_change(ss);
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss)
+{
+    v42bis_comp_state_t *s;
+    int len;
+    
+    s = &ss->compress;
+    if (s->update_at)
+        return 0;
+    if (s->last_matched)
+    {
+        len = s->string_length;
+        send_encoded_data(s, s->last_matched);
+        s->flushed_length += len;
+    }
+    if (!s->transparent)
+    {
+        s->update_at = s->last_matched;
+        s->last_matched = 0;
+        s->flushed_length = 0;
+        push_compressed_code(s, V42BIS_FLUSH);
+        push_octet_alignment(s);
+    }
+    flush_octets(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len)
+{
+    v42bis_comp_state_t *s;
+    int i;
+    int j;
+    int yyy;
+    uint16_t code;
+    uint16_t p;
+    uint8_t ch;
+    uint8_t in;
+
+    s = &ss->decompress;
+    if (!s->v42bis_parm_p0)
+    {
+        /* Compression is off - just push the incoming data out */
+        push_octets(s, buf, len);
+        return 0;
+    }
+    for (i = 0;  i < len;  )
+    {
+        if (s->transparent)
+        {
+            in = buf[i];
+            if (s->escaped)
+            {
+                /* Command */
+                s->escaped = FALSE;
+                switch (in)
+                {
+                case V42BIS_ECM:
+                    /* Enter compressed mode */
+                    span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n");
+                    send_string(s);
+                    s->transparent = FALSE;
+                    s->update_at = s->last_matched;
+                    s->last_matched = 0;
+                    i++;
+                    continue;
+                case V42BIS_EID:
+                    /* Escape symbol */
+                    span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n");
+                    in = s->escape_code;
+                    s->escape_code += V42BIS_ESC_STEP;
+                    break;
+                case V42BIS_RESET:
+                    /* Reset dictionary */
+                    span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n");
+                    /* TODO: */
+                    send_string(s);
+                    dictionary_init(s);
+                    i++;
+                    continue;
+                default:
+                    span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in);
+                    return -1;
+                }
+            }
+            else if (in == s->escape_code)
+            {
+                s->escaped = TRUE;
+                i++;
+                continue;
+            }
+
+            yyy = TRUE;
+            for (j = 0;  j < 2  &&  yyy;  j++)
+            {
+                if (s->update_at)
+                {
+                    if (match_octet(s, s->update_at, in) == 0)
+                        s->last_added = add_octet_to_dictionary(s, s->update_at, in);
+                    s->update_at = 0;
+                }
+
+                code = match_octet(s, s->last_matched, in);
+                if (code == 0)
+                {
+                    s->update_at = s->last_matched;
+                    send_string(s);
+                    s->last_matched = 0;
+                }
+                else if (code == s->last_added)
+                {
+                    s->last_added = 0;
+                    send_string(s);
+                    s->last_matched = 0;
+                }
+                else
+                {
+                    s->last_matched = code;
+                    s->string[s->string_length++] = in;
+                    if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
+                    {
+                        send_string(s);
+                        s->last_matched = 0;
+                    }
+                    i++;
+                    yyy = FALSE;
+                }
+            }
+        }
+        else
+        {
+            /* Get code from input */
+            while (s->bit_count < s->v42bis_parm_c2  &&  i < len)
+            {
+                s->bit_buffer |= buf[i++] << s->bit_count;
+                s->bit_count += 8;
+            }
+            if (s->bit_count < s->v42bis_parm_c2)
+                continue;
+            code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1);
+            s->bit_buffer >>= s->v42bis_parm_c2;
+            s->bit_count -= s->v42bis_parm_c2;
+
+            if (code < V42BIS_N6)
+            {
+                /* We have a control code. */
+                switch (code)
+                {
+                case V42BIS_ETM:
+                    /* Enter transparent mode */
+                    span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n");
+                    s->bit_count = 0;
+                    s->transparent = TRUE;
+                    s->last_matched = 0;
+                    s->last_added = 0;
+                    break;
+                case V42BIS_FLUSH:
+                    /* Flush signal */
+                    span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n");
+                    s->bit_count = 0;
+                    break;
+                case V42BIS_STEPUP:
+                    /* Increase code word size */
+                    span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n");
+                    s->v42bis_parm_c2++;
+                    s->v42bis_parm_c3 <<= 1;
+                    if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3))
+                        return -1;
+                    break;
+                }
+                continue;
+            }
+            /* Regular codeword */
+            if (code == s->v42bis_parm_c1)
+                return -1;
+            expand_codeword_to_string(s, code);
+            if (s->update_at)
+            {
+                ch = s->string[0];
+                if ((p = match_octet(s, s->update_at, ch)) == 0)
+                {
+                    s->last_added = add_octet_to_dictionary(s, s->update_at, ch);
+                    if (code == s->v42bis_parm_c1)
+                        return -1;
+                }
+                else if (p == s->last_added)
+                {
+                    s->last_added = 0;
+                }
+            }
+            s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7)  ?  0  :  code;
+            /* Allow for any escapes which may be in this string */
+            for (j = 0;  j < s->string_length;  j++)
+            {
+                if (s->string[j] == s->escape_code)
+                    s->escape_code += V42BIS_ESC_STEP;
+            }
+            send_string(s);
+        }
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss)
+{
+    v42bis_comp_state_t *s;
+    int len;
+    
+    s = &ss->decompress;
+    len = s->string_length;
+    send_string(s);
+    s->flushed_length += len;
+    flush_octets(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode)
+{
+    s->compress.compression_mode = mode;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(v42bis_state_t *) v42bis_init(v42bis_state_t *s,
+                                           int negotiated_p0,
+                                           int negotiated_p1,
+                                           int negotiated_p2,
+                                           put_msg_func_t encode_handler,
+                                           void *encode_user_data,
+                                           int max_encode_len,
+                                           put_msg_func_t decode_handler,
+                                           void *decode_user_data,
+                                           int max_decode_len)
+{
+    int ret;
+
+    if (negotiated_p1 < V42BIS_MIN_DICTIONARY_SIZE  ||  negotiated_p1 > 65535)
+        return NULL;
+    if (negotiated_p2 < V42BIS_MIN_STRING_SIZE  ||  negotiated_p2 > V42BIS_MAX_STRING_SIZE)
+        return NULL;
+    if (s == NULL)
+    {
+        if ((s = (v42bis_state_t *) malloc(sizeof(*s))) == NULL)
+            return NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "V.42bis");
+
+    if ((ret = v42bis_comp_init(&s->compress, negotiated_p1, negotiated_p2, encode_handler, encode_user_data, max_encode_len)))
+        return NULL;
+    if ((ret = v42bis_comp_init(&s->decompress, negotiated_p1, negotiated_p2, decode_handler, decode_user_data, max_decode_len)))
+    {
+        comp_exit(&s->compress);
+        return NULL;
+    }
+    s->compress.v42bis_parm_p0 = negotiated_p0 & 2;
+    s->decompress.v42bis_parm_p0 = negotiated_p0 & 1;
+
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s)
+{
+    comp_exit(&s->compress);
+    comp_exit(&s->decompress);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

-- 
To view, visit https://gerrit.osmocom.org/643
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Iabedece9f97ca944a1e3f747bb073e532c4e9dca
Gerrit-PatchSet: 29
Gerrit-Project: openbsc
Gerrit-Branch: master
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list