774 lines
24 KiB
C
774 lines
24 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*! \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>
|
|
#if defined(HAVE_STDBOOL_H)
|
|
#include <stdbool.h>
|
|
#else
|
|
#include "spandsp/stdbool.h"
|
|
#endif
|
|
|
|
#include "spandsp/telephony.h"
|
|
#include "spandsp/alloc.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(logging_state_t *) v42bis_get_logging_state(v42bis_state_t *s)
|
|
{
|
|
return &s->logging;
|
|
}
|
|
/*- 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 *) span_alloc(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);
|
|
span_free(s);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
/*- End of file ------------------------------------------------------------*/
|