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
This commit is contained in:
parent
f1f34360fb
commit
0b11db7e9f
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* v42bis.h
|
||||
*
|
||||
* Written by Steve Underwood <steveu@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 ------------------------------------------------------------*/
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* private/v42bis.h
|
||||
*
|
||||
* Written by Steve Underwood <steveu@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 ------------------------------------------------------------*/
|
|
@ -0,0 +1,764 @@
|
|||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* v42bis.c
|
||||
*
|
||||
* Written by Steve Underwood <steveu@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 ------------------------------------------------------------*/
|
Loading…
Reference in New Issue