From 0b11db7e9f6f15262443d5f31206bec92ea05d8f Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 1 Aug 2016 18:13:40 +0200 Subject: [PATCH] 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 --- openbsc/include/openbsc/v42bis.h | 140 +++++ openbsc/include/openbsc/v42bis_private.h | 127 ++++ openbsc/src/gprs/v42bis.c | 764 +++++++++++++++++++++++ 3 files changed, 1031 insertions(+) create mode 100644 openbsc/include/openbsc/v42bis.h create mode 100644 openbsc/include/openbsc/v42bis_private.h create mode 100644 openbsc/src/gprs/v42bis.c diff --git a/openbsc/include/openbsc/v42bis.h b/openbsc/include/openbsc/v42bis.h new file mode 100644 index 000000000..b947a61cd --- /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 + * + * 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 000000000..2c801ebe2 --- /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 + * + * 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 000000000..d025ea9f7 --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 ------------------------------------------------------------*/