2013 lines
46 KiB
C
2013 lines
46 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
arap.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the v42bis compression/decompression routines
|
|
used by ARAP (adapted from fcr's code)
|
|
|
|
Author:
|
|
|
|
Shirish Koti
|
|
|
|
Revision History:
|
|
15 Nov 1996 Initial Version
|
|
|
|
--*/
|
|
|
|
#include "atalk.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_init_dictionary)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_init)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_decode_codeword)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_decode_codeword_flush)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_encode_codeword)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_encode_codeword_flush)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_encode_value)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_apply_compression_test)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_encode_buffer)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_encode_flush)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_transition_to_compressed)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_transition_to_transparent)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_signal_reset)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_decode_match)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_decode_buffer)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_decode_flush)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_init_buffer)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_connection_init)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_connection_init_buffers)
|
|
#pragma alloc_text(PAGE_ARAP, v42bis_connection_init_push)
|
|
#pragma alloc_text(PAGE_ARAP, v42bisInit)
|
|
#pragma alloc_text(PAGE_ARAP, v42bisCompress)
|
|
#pragma alloc_text(PAGE_ARAP, v42bisDecompress)
|
|
#endif
|
|
|
|
//
|
|
// bitmaps[# of bits]
|
|
//
|
|
USHORT bit_masks[16] =
|
|
{
|
|
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,
|
|
0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff
|
|
};
|
|
|
|
|
|
static PCHAR
|
|
show_char(
|
|
IN UCHAR ch
|
|
)
|
|
{
|
|
static char dec[20];
|
|
|
|
if (' ' <= ch && ch <= '~')
|
|
{
|
|
dec[0] = ch;
|
|
dec[1] = 0;
|
|
return dec;
|
|
}
|
|
|
|
// BUGV42BUG: do we need this?
|
|
//sprintf(dec, "0x%02x", ch);
|
|
return dec;
|
|
}
|
|
|
|
/*
|
|
decode_xid_params()
|
|
|
|
decode compression negotiation packet as per V42bis spec.
|
|
|
|
note: this is not used (there is code in the mnp LR routines to do
|
|
this), but is included for completeness.
|
|
*/
|
|
|
|
|
|
/*
|
|
v42bis_push()
|
|
|
|
perform PUSH on output stream. accumlated bytes are pushed
|
|
out.
|
|
*/
|
|
|
|
|
|
/*
|
|
v42bis_init_dictionary()
|
|
|
|
init dictionary in accordance with section 6.2 and 7.2
|
|
|
|
this is used at init time and in response to a CCW_RESET
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_init_dictionary(state)
|
|
v42bis_t *state;
|
|
{
|
|
int i;
|
|
node_t *n;
|
|
|
|
/* initialize dictionary tree */
|
|
for (i = 0, n = state->dictionary; i < state->n2; i++, n++)
|
|
{
|
|
n->byte = 0;
|
|
n->parent = 0;
|
|
n->node = 0;
|
|
n->leaf = 0;
|
|
}
|
|
|
|
/* section 6.2 */
|
|
state->c1 = N5; /* next codeword */
|
|
state->c2 = N3 + 1; /* codeword size (bits) */
|
|
state->c3 = N4 * 2; /* threshold */
|
|
|
|
state->transparent = TRUE;
|
|
state->escape = 0;
|
|
state->escaped = FALSE;
|
|
state->exception_next = FALSE;
|
|
|
|
/* initialize searching */
|
|
state->last_match = 0;
|
|
state->last_new = 0;
|
|
state->string_size = 0;
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
v42bis_init()
|
|
|
|
implements C-INIT semantics
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_init(state)
|
|
v42bis_t *state;
|
|
{
|
|
|
|
DWORD dwRetCode;
|
|
|
|
V_FLOW(("v42bis_init()"));
|
|
|
|
/* our defaults */
|
|
state->n1 = LOG2_CODES; /* max codeword size (bits) */
|
|
state->n2 = CONN(state)->neg_p1; /* total # of codewords */
|
|
state->n7 = CONN(state)->neg_p2; /* max string length */
|
|
|
|
/* init dictionary */
|
|
v42bis_init_dictionary(state);
|
|
|
|
/* initialize encode/decode */
|
|
state->bits_acc = 0;
|
|
state->bits_used = 0;
|
|
state->word_size = 8*sizeof(unsigned short);
|
|
|
|
state->bits_waiting = 0;
|
|
state->bits_remaining = 0;
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
#ifdef DEBUG_ENABLED
|
|
/*
|
|
itobits()
|
|
|
|
turn an integer bitfield into an ascii representation (i.e. "01010101")
|
|
*/
|
|
|
|
char *
|
|
itobits(word, bits)
|
|
USHORT word;
|
|
int bits;
|
|
{
|
|
static char buf[33];
|
|
int i;
|
|
|
|
if (bits > 32) bits = 32;
|
|
|
|
for (i = bits-1; i >= 0; i--)
|
|
buf[(bits-1)-i] = word & (1 << i) ? '1' : '0';
|
|
|
|
buf[bits] = 0;
|
|
return buf;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
v42bis_decode_codeword()
|
|
|
|
decode n-bit codewords from a bytesteam.
|
|
*/
|
|
|
|
USHORT
|
|
v42bis_decode_codeword(state, value)
|
|
v42bis_t *state;
|
|
UCHAR value;
|
|
{
|
|
register UCHAR bits_free, bits_new, bits_residual;
|
|
register USHORT codeword;
|
|
|
|
V_FLOW(("v42bis_decode_codeword(%x) c2=%d", value, state->c2));
|
|
|
|
/* simple case */
|
|
if (state->c2 == 8 || state->transparent)
|
|
return value;
|
|
|
|
/* not-so-simple case */
|
|
D_DEBUG(("before: waiting %06x, bits_remaining %d",
|
|
state->bits_waiting, state->bits_remaining));
|
|
|
|
/* add in these 8 bits */
|
|
state->bits_waiting |= ((DWORD)value) << state->bits_remaining;
|
|
state->bits_remaining += 8;
|
|
|
|
/* do we have a codeword ? */
|
|
if (state->bits_remaining >= state->c2) {
|
|
D_DEBUG(("input %04x %s",
|
|
state->bits_waiting & bit_masks[state->c2],
|
|
itobits(state->bits_waiting & bit_masks[state->c2],
|
|
state->c2)));
|
|
|
|
codeword = (USHORT)(state->bits_waiting & bit_masks[state->c2]);
|
|
|
|
state->bits_waiting >>= state->c2;
|
|
state->bits_remaining -= state->c2;
|
|
|
|
D_DEBUG(("after: waiting %04x, bits_remaining %d (data)",
|
|
state->bits_waiting, state->bits_remaining));
|
|
|
|
return codeword;
|
|
}
|
|
|
|
D_DEBUG(("after: waiting %04x, bits_remaining %d (no data)",
|
|
state->bits_waiting, state->bits_remaining));
|
|
|
|
return ((USHORT)-1);
|
|
}
|
|
|
|
/*
|
|
v42bis_decode_codeword_flush()
|
|
|
|
"flush" decoding of codewords, returning the last codeword
|
|
*/
|
|
|
|
USHORT
|
|
v42bis_decode_codeword_flush(state)
|
|
v42bis_t *state;
|
|
{
|
|
USHORT codeword = (USHORT)-1;
|
|
|
|
if (state->bits_remaining)
|
|
codeword = (USHORT)(state->bits_waiting & bit_masks[state->c2]);
|
|
|
|
state->bits_waiting = 0;
|
|
state->bits_remaining = 0;
|
|
|
|
return codeword;
|
|
}
|
|
|
|
/*
|
|
v42bis_encode_codeword()
|
|
|
|
encode n-bit codewords into a bytesteam.
|
|
|
|
This routine makes use of the fact that the code words will be always
|
|
be smaller than 16 bits. An "accumulator" is used with several state
|
|
variables to keep track of how much of the accumulator is in use at
|
|
any given time.
|
|
|
|
The code works for wordsizes of 8 and 16 bits. It is assumed that the
|
|
output is a byte stream. No assumptions are made about alignment of
|
|
data.
|
|
|
|
note: this routine needs to be "flushed" to get out the value left
|
|
in the accumulator.
|
|
|
|
jbp@fcr.com 09/13/92, 19:52
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_encode_codeword(state, value)
|
|
v42bis_t *state;
|
|
USHORT value;
|
|
{
|
|
register UCHAR bits_free, bits_new, bits_residual;
|
|
|
|
EN_DEBUG(("v42bis_encode_codeword(%d 0x%x) c2=%d",
|
|
value, value, state->c2));
|
|
|
|
/* simple case */
|
|
if (state->c2 == 8 || state->transparent)
|
|
{
|
|
E_DEBUG(("put acc %02x %s", value & 0xff, itobits(value & 0xff, 8)));
|
|
|
|
PUT((UCHAR)value);
|
|
|
|
if (state->transparent)
|
|
{
|
|
state->bits_out_while_transparent += N3;
|
|
}
|
|
else
|
|
{
|
|
state->bits_out_while_compressed += N3;
|
|
}
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
state->bits_out_while_compressed += state->c2;
|
|
|
|
/* not-so-simple case */
|
|
E_DEBUG(("before: acc %04x, bits_used %d",
|
|
state->bits_acc, state->bits_used));
|
|
|
|
/* place new value in appropriate bit positions */
|
|
state->bits_acc |= ((DWORD)value) << state->bits_used;
|
|
|
|
/* housekeeping */
|
|
bits_free = state->word_size - state->bits_used;
|
|
bits_new = bits_free < state->c2 ? bits_free : state->c2;
|
|
bits_residual = state->c2 - bits_new;
|
|
|
|
E_DEBUG(("bits_free %d, bits_new %d, bits_residual %d",
|
|
bits_free, bits_new, bits_residual));
|
|
|
|
#ifdef DEBUG
|
|
if (state->bits_used + bits_new >= 31)
|
|
logf("acc oflo, size %d", state->bits_used + bits_new);
|
|
#endif
|
|
|
|
/* do we have a full codeword in the accumulator? */
|
|
if (state->bits_used + bits_new == state->word_size)
|
|
{
|
|
|
|
if (state->word_size == 16)
|
|
{
|
|
E_DEBUG(("put acc %06x %s",
|
|
state->bits_acc, itobits(state->bits_acc, 24)));
|
|
|
|
PUT((UCHAR)(state->bits_acc));
|
|
|
|
PUT((UCHAR)(state->bits_acc >> 8));
|
|
|
|
}
|
|
else
|
|
{
|
|
E_DEBUG(("put acc %02x %s",
|
|
state->bits_acc & 0xff,
|
|
itobits(state->bits_acc & 0xff, 8)));
|
|
|
|
PUT((UCHAR)(state->bits_acc));
|
|
}
|
|
|
|
E_DEBUG(("value 0x%x, bits_used %d, acc 0x%x",
|
|
value, state->bits_used, value >> state->bits_used));
|
|
|
|
/* account for left over bits */
|
|
state->bits_acc = value >> (state->c2 - bits_residual);
|
|
|
|
state->bits_used = bits_residual;
|
|
}
|
|
else
|
|
{
|
|
state->bits_used += bits_new;
|
|
}
|
|
|
|
E_DEBUG(("after: acc %06x, bits_used %d",
|
|
state->bits_acc, state->bits_used));
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
v42bis_encode_codeword_flush()
|
|
|
|
flush partial assembly of codeword into 16 bit accumulator
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_encode_codeword_flush(state)
|
|
v42bis_t *state;
|
|
{
|
|
V_FLOW(("v42bis_encode_codeword_flush() bits_used %d", state->bits_used));
|
|
|
|
if (state->bits_used) {
|
|
E_DEBUG(("put acc (flush) %02x %s",
|
|
state->bits_acc & 0xff,
|
|
itobits(state->bits_acc & 0xff, 8)));
|
|
|
|
PUT((UCHAR)(state->bits_acc));
|
|
}
|
|
|
|
if (state->bits_used > 8) {
|
|
E_DEBUG(("put acc (flush2) %02x %s",
|
|
(state->bits_acc>>8) & 0xff,
|
|
itobits((state->bits_acc>>8) & 0xff, 8)));
|
|
|
|
PUT((UCHAR)(state->bits_acc >> 8));
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (state->bits_used > 16)
|
|
logf("flush: bits_used %d", state->bits_used);
|
|
#endif
|
|
|
|
state->bits_used = 0;
|
|
state->bits_acc = 0;
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
v42bis_encode_value()
|
|
|
|
encode a codeword value, noting if it's size exceeds C3, and
|
|
doing any required STEPUPs
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_encode_value(state, value)
|
|
v42bis_t *state;
|
|
USHORT value;
|
|
{
|
|
DWORD dwRetCode;
|
|
|
|
V_FLOW(("v42bis_encode_value(%lx, 0x%x)", state, value));
|
|
|
|
#ifdef DEBUG
|
|
/* sanity check */
|
|
if (value >= 8192) {
|
|
logf("encode_value() value too big, %d", value);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
/* section 7.4 */
|
|
|
|
/* check codeword size */
|
|
while (value >= state->c3)
|
|
{
|
|
EN_DEBUG(("stepup: value %d, max %d", value, state->c3));
|
|
|
|
dwRetCode = v42bis_encode_codeword(state, CCW_STEPUP);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
state->c2++;
|
|
state->c3 *= 2;
|
|
}
|
|
|
|
dwRetCode = v42bis_encode_codeword(state, value);
|
|
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/*
|
|
decide if we should transition from tranparent to compressed or
|
|
visa versa.
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_apply_compression_test(state)
|
|
v42bis_t *state;
|
|
{
|
|
|
|
DWORD dwRetCode;
|
|
|
|
if (state->just_flushed || state->exception_next)
|
|
{
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
#ifdef UNIT_TEST_PROGRESS
|
|
{
|
|
static int times = 0;
|
|
if (++times == 1000)
|
|
{
|
|
times = 0;
|
|
dwRetCode = v42bis_comp_test_report(state);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef UNIT_TEST_FORCE
|
|
/* force consistant behavior across all input */
|
|
if (!state->transparent)
|
|
{
|
|
state->bits_out_while_transparent = 0;
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
state->bits_out_if_transparent = 0;
|
|
#undef WINDOW_CHECK_BYTES
|
|
#define WINDOW_CHECK_BYTES 0
|
|
if (state->bits_out_while_transparent > 64*N3)
|
|
{
|
|
dwRetCode = v42bis_transition_to_compressed(state);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
#endif
|
|
|
|
/* bound check to recent history */
|
|
if (WINDOW_FULL(state->bits_out_this_mode))
|
|
{
|
|
state->bits_out_this_mode = 0;
|
|
state->bits_out_other_mode = 0;
|
|
}
|
|
|
|
if (!state->transparent)
|
|
{
|
|
/* compressing */
|
|
if ((state->bits_out_while_compressed -
|
|
state->bits_out_if_transparent) > WINDOW_MIN_BITS)
|
|
{
|
|
dwRetCode = v42bis_transition_to_transparent(state);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* transparent */
|
|
#ifdef NEVER_SEND_COMPRESSED
|
|
return ARAPERR_NO_ERROR;
|
|
#endif
|
|
/* transparent */
|
|
if ((state->bits_out_while_transparent -
|
|
state->bits_out_if_compressed) > WINDOW_MIN_BITS)
|
|
{
|
|
dwRetCode = v42bis_transition_to_compressed(state);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
v42bis_encode_buffer()
|
|
|
|
implements C-DATA semantics on encode side
|
|
|
|
encode a buffer full of data...
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_encode_buffer(state, string, insize)
|
|
v42bis_t *state;
|
|
PUCHAR string;
|
|
int insize;
|
|
{
|
|
UCHAR ch;
|
|
BOOLEAN hit, duplicate;
|
|
USHORT root_value, hit_node;
|
|
node_t *n, *dead, *p;
|
|
DWORD dwRetCode;
|
|
|
|
|
|
V_FLOW(("v42bis_encode_buffer(%lx, %lx, %d)", state, string, insize));
|
|
|
|
if (insize == 0)
|
|
{
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
V_FLOW(("v42bis_encode: input %*s", insize, string));
|
|
|
|
state->bytes_in += insize;
|
|
|
|
/* section 6.3 */
|
|
|
|
while (insize > 0)
|
|
{
|
|
/* "search" dictionary for string + character */
|
|
ch = string[0];
|
|
|
|
hit = FALSE;
|
|
duplicate = FALSE;
|
|
hit_node = state->last_match;
|
|
p = DICT(state->last_match);
|
|
|
|
EN_S_DEBUG(("last_match %d, string_size %d, insize %d, ch %d '%s'",
|
|
state->last_match, state->string_size, insize,
|
|
ch, show_char(ch)));
|
|
|
|
if (state->last_match == 0)
|
|
{
|
|
/*
|
|
* "the code word associated with each root node shall be N6 (the
|
|
* number of control codewords) plus the ordinal value of the
|
|
* character represented by the node"
|
|
*/
|
|
|
|
state->last_match = ch + N6;
|
|
state->string_size = 1;
|
|
|
|
EN_S_DEBUG(("codeword for root %d, '%s' = %d",
|
|
ch + N6, show_char(ch), CODE(DICT(ch + N6))));
|
|
|
|
p = DICT(ch + N6);
|
|
p->byte = ch;
|
|
|
|
hit = TRUE;
|
|
hit_node = state->last_match;
|
|
|
|
/* consume input */
|
|
goto consume_input;
|
|
}
|
|
|
|
/* we're at a node; search it's leaves */
|
|
for (n = DICT(DICT(state->last_match)->leaf);
|
|
CODE(n) && insize > 0;)
|
|
{
|
|
EN_S_DEBUG((" checking leaf node %d", CODE(n)));
|
|
|
|
if (n->byte == *string)
|
|
{
|
|
/* hit - check leafs */
|
|
EN_S_DEBUG((" hit: "));
|
|
|
|
hit_node = (USHORT)CODE(n);
|
|
p = n;
|
|
state->last_match = (USHORT)CODE(n);
|
|
|
|
if (state->just_flushed || hit_node == state->last_new)
|
|
{
|
|
EN_S_DEBUG(("leaving search, node == last created"));
|
|
hit = FALSE;
|
|
duplicate = TRUE;
|
|
|
|
/* backup to previous node */
|
|
hit_node = n->parent;
|
|
state->last_match = n->parent;
|
|
break;
|
|
}
|
|
|
|
hit = TRUE;
|
|
state->string_size++;
|
|
|
|
#ifdef never
|
|
string++;
|
|
insize--;
|
|
|
|
/* if no leafs, exit now - we're at the end */
|
|
if (n->leaf == 0)
|
|
{
|
|
EN_S_DEBUG(("leaving search, no leaf"));
|
|
break;
|
|
}
|
|
|
|
n = DICT(n->leaf);
|
|
EN_S_DEBUG(("continuing search, leaf %d", CODE(n)));
|
|
continue;
|
|
#else
|
|
EN_S_DEBUG(("exiting search, leaf %d", CODE(n)));
|
|
goto consume_input;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
EN_S_DEBUG((" miss: "));
|
|
hit = FALSE;
|
|
}
|
|
|
|
if (n->node == 0)
|
|
{
|
|
EN_S_DEBUG(("leaving search, no node"));
|
|
break;
|
|
}
|
|
|
|
n = DICT(n->node);
|
|
EN_S_DEBUG(("continuing search, node %d", CODE(n)));
|
|
}
|
|
|
|
EN_S_DEBUG(("search done, n %d, insize %d, next %d '%s' %s %s",
|
|
CODE(n), insize, string[0], show_char(string[0]),
|
|
hit ? "hit" : "miss", duplicate ? "duplicate" : ""));
|
|
|
|
#ifdef never
|
|
/* we're matching but we ran out of characters */
|
|
if (hit && insize == 0)
|
|
{
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
#endif
|
|
|
|
if (!hit && duplicate)
|
|
{
|
|
BOOLEAN ok_to_output;
|
|
|
|
EN_S_DEBUG(("duplicate"));
|
|
|
|
ok_to_output =
|
|
!state->just_flushed &&
|
|
!state->exception_next &&
|
|
!state->decode_only;
|
|
|
|
state->exception_next = FALSE;
|
|
|
|
if (ok_to_output)
|
|
{
|
|
if (!state->transparent)
|
|
{
|
|
dwRetCode = v42bis_encode_value(state, hit_node);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state->bits_out_if_compressed += state->c2;
|
|
|
|
/* check if we should go compressed */
|
|
if (state->bytes_since_last_check > WINDOW_CHECK_BYTES)
|
|
{
|
|
state->bytes_since_last_check = 0;
|
|
|
|
dwRetCode = v42bis_apply_compression_test(state);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* string = string + character */
|
|
state->string_size++;
|
|
|
|
/* reset match to unmatched character */
|
|
state->last_match = 0;
|
|
state->string_size = 0;
|
|
state->last_new = 0;
|
|
|
|
state->just_flushed = 0;
|
|
|
|
/* don't advance, "string = unmatched character" */
|
|
continue;
|
|
}
|
|
|
|
/* last char did not match or already in dictionary */
|
|
if (!hit && !duplicate)
|
|
{
|
|
BOOLEAN ok_to_output;
|
|
|
|
EN_S_DEBUG(("update dictionary"));
|
|
|
|
ok_to_output =
|
|
!state->just_flushed &&
|
|
!state->exception_next &&
|
|
!state->decode_only;
|
|
|
|
state->exception_next = FALSE;
|
|
|
|
if (ok_to_output)
|
|
{
|
|
if (!state->transparent)
|
|
{
|
|
dwRetCode = v42bis_encode_value(state, hit_node);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state->bits_out_if_compressed += state->c2;
|
|
|
|
/* check if we should go compressed */
|
|
if (state->bytes_since_last_check > WINDOW_CHECK_BYTES)
|
|
{
|
|
state->bytes_since_last_check = 0;
|
|
dwRetCode = v42bis_apply_compression_test(state);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
state->just_flushed = 0;
|
|
|
|
/* "add string + character to dictionary" */
|
|
|
|
/* section 6.4a */
|
|
|
|
/* string too big? */
|
|
if (state->string_size >= state->n7)
|
|
{
|
|
EN_DEBUG(("string size (%d) > n7 (%d)",
|
|
state->string_size, state->n7));
|
|
|
|
/* reset match */
|
|
state->last_match = 0;
|
|
state->string_size = 0;
|
|
|
|
/* we were in the match routine, reset last new */
|
|
state->last_new = 0;
|
|
|
|
continue;
|
|
}
|
|
|
|
/* pick a new code word */
|
|
n = DICT(state->c1);
|
|
state->last_new = (USHORT)CODE(n);
|
|
|
|
EN_DEBUG(("adding new node %d = %d '%s', parent %d",
|
|
CODE(n), string[0], show_char(string[0]), CODE(p)));
|
|
|
|
/* attach "string + character" */
|
|
n->byte = string[0];
|
|
n->parent = hit_node;
|
|
#ifdef DEBUG
|
|
if (CODE(n) == hit_node)
|
|
{
|
|
logf("creating loop! node %d", CODE(n));
|
|
}
|
|
#endif
|
|
n->node = 0;
|
|
|
|
/* XXX should be in ord(ch) order to allow faster search */
|
|
n->node = p->leaf;
|
|
p->leaf = (USHORT)CODE(n);
|
|
|
|
/* section 6.5 */
|
|
|
|
/* recover dictionary entries */
|
|
do
|
|
{
|
|
state->c1++;
|
|
|
|
if (state->c1 > (state->n2 - 1))
|
|
{
|
|
state->c1 = N5;
|
|
state->dict_full = TRUE;
|
|
}
|
|
|
|
dead = DICT(state->c1);
|
|
|
|
/* find terminal nodes (i.e. leaf == 0) */
|
|
} while (/*dead->parent != 0 &&*/ dead->leaf != 0);
|
|
|
|
/* terminal nodes with parents are eligible */
|
|
if (CODE(dead) && /* <- I think this is not needed */
|
|
/*dead->parent && */dead->leaf == 0 &&
|
|
state->dict_full)
|
|
{
|
|
/* go to parent, disconnect from chain */
|
|
node_t *parent = DICT(dead->parent);
|
|
|
|
EN_DEBUG(("recovering dead node %d", CODE(dead)));
|
|
|
|
/* if first on parents list, fix parent */
|
|
if (DICT(parent->leaf) == dead)
|
|
{
|
|
parent->leaf = dead->node;
|
|
}
|
|
else
|
|
{
|
|
/* else search parents list, fix sibling */
|
|
for (parent = DICT(DICT(dead->parent)->leaf);
|
|
CODE(parent); parent = DICT(parent->node))
|
|
{
|
|
if (DICT(parent->node) == dead)
|
|
{
|
|
parent->node = dead->node;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* mark node free */
|
|
dead->parent = 0;
|
|
dead->leaf = 0;
|
|
} /* dead node */
|
|
|
|
/* if we added a node, reset "string" */
|
|
//reset_match:
|
|
state->last_match = 0;
|
|
state->string_size = 0;
|
|
state->just_flushed = 0;
|
|
|
|
/*
|
|
* this is a "safe time" to do compression test, as we've just
|
|
* done an update...
|
|
*/
|
|
if (!state->decode_only)
|
|
{
|
|
if (state->bytes_since_last_check > WINDOW_CHECK_BYTES)
|
|
{
|
|
state->bytes_since_last_check = 0;
|
|
dwRetCode = v42bis_apply_compression_test(state);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* don't advance, "string = unmatched character" */
|
|
continue;
|
|
} /* (!hit && !duplicate) */
|
|
|
|
consume_input:
|
|
string++;
|
|
insize--;
|
|
state->bytes_since_last_check++;
|
|
|
|
/* section 9.2 */
|
|
//check_escape:
|
|
/* escape processing */
|
|
if (state->transparent)
|
|
{
|
|
if (!state->decode_only)
|
|
{
|
|
dwRetCode = v42bis_encode_value(state, ch);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
|
|
if (ch == state->escape)
|
|
{
|
|
if (!state->decode_only)
|
|
{
|
|
dwRetCode = v42bis_encode_value(state, CCW_EID);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
state->escape += ESCAPE_CYCLE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* compressed, cycle escape character */
|
|
if (ch == state->escape && !state->decode_only)
|
|
{
|
|
state->escape += ESCAPE_CYCLE;
|
|
}
|
|
|
|
state->bits_out_if_transparent += N3;
|
|
}
|
|
|
|
state->just_flushed = 0;
|
|
}
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
implements C-FLUSH semantics
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_encode_flush(state)
|
|
v42bis_t *state;
|
|
{
|
|
|
|
DWORD dwRetCode=ARAPERR_NO_ERROR;
|
|
|
|
|
|
V_FLOW(("v42bis_encode_flush() string_size %d, last_match %d",
|
|
state->string_size, state->last_match));
|
|
|
|
if (state->just_flushed)
|
|
{
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
if (state->transparent)
|
|
{
|
|
/* transparent, send any buffered characters */
|
|
}
|
|
else
|
|
{
|
|
/* compressed */
|
|
|
|
/* section 7.9a */
|
|
/* output partial match, if any */
|
|
if (state->string_size)
|
|
{
|
|
/* section 7.8.2 */
|
|
dwRetCode = v42bis_encode_value(state, state->last_match);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
|
|
state->just_flushed = 1;
|
|
|
|
dwRetCode = v42bis_encode_value(state, CCW_FLUSH);
|
|
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
dwRetCode = v42bis_encode_codeword_flush(state);
|
|
}
|
|
|
|
return dwRetCode;
|
|
}
|
|
|
|
DWORD
|
|
v42bis_transition_to_compressed(state)
|
|
v42bis_t *state;
|
|
{
|
|
|
|
DWORD dwRetCode=ARAPERR_NO_ERROR;
|
|
|
|
|
|
V_FLOW(("v42bis_transition_to_compressed()"));
|
|
|
|
#ifdef UNIT_TEST_VERBOSE
|
|
logf("v42bis_transition_to_compressed()");
|
|
v42bis_comp_test_report(state);
|
|
#endif
|
|
|
|
if (state->transparent)
|
|
{
|
|
/* section 7.8.1a */
|
|
dwRetCode = v42bis_encode_value(state, state->escape);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/* section 7.8.1b */
|
|
if ((dwRetCode = v42bis_encode_value(state, CCW_ECM)) != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
if ((dwRetCode = v42bis_encode_codeword_flush(state)) != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/* enter compressed mode */
|
|
state->transparent = FALSE;
|
|
|
|
state->bits_out_if_transparent = 0;
|
|
state->bits_out_while_compressed = 0;
|
|
}
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
v42bis_transition_to_transparent(state)
|
|
v42bis_t *state;
|
|
{
|
|
|
|
DWORD dwRetCode;
|
|
|
|
V_FLOW(("v42bis_transition_to_transparent()"));
|
|
|
|
#ifdef UNIT_TEST_VERBOSE
|
|
logf("v42bis_transition_to_transparent()");
|
|
v42bis_comp_test_report(state);
|
|
#endif
|
|
|
|
/* check counters for overflow */
|
|
|
|
if (!state->transparent)
|
|
{
|
|
/* output partial match, if any */
|
|
if (state->string_size)
|
|
{
|
|
/* section 7.8.2 */
|
|
dwRetCode = v42bis_encode_value(state, state->last_match);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
|
|
/* section 7.8.2c */
|
|
if ((dwRetCode = v42bis_encode_value(state, CCW_ETM)) != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/* section 7.8.2d */
|
|
if ((dwRetCode = v42bis_encode_codeword_flush(state)) != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/* section 7.8.2e */
|
|
/* enter transparent mode */
|
|
state->transparent = TRUE;
|
|
|
|
/* reset compressibility test */
|
|
state->bits_out_if_compressed = 0;
|
|
state->bits_out_while_transparent = 0;
|
|
}
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
v42bis_signal_reset(state)
|
|
v42bis_t *state;
|
|
{
|
|
|
|
DWORD dwRetCode;
|
|
|
|
if (!state->transparent)
|
|
{
|
|
/* change to transparent */
|
|
dwRetCode = v42bis_transition_to_transparent(state);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/* counteract side effect */
|
|
state->exception_next = FALSE;
|
|
}
|
|
|
|
dwRetCode = v42bis_encode_value(state, state->escape);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
dwRetCode = v42bis_encode_value(state, CCW_RESET);
|
|
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/*
|
|
expand a codeword into it's string
|
|
|
|
follow chain of "parent" to root and then expand the node characters
|
|
one by one.
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_decode_match(state, codeword, psize, pRetChar)
|
|
v42bis_t *state;
|
|
USHORT codeword;
|
|
UCHAR *pRetChar;
|
|
int *psize;
|
|
{
|
|
node_t *path[256];
|
|
int path_size = 0;
|
|
node_t *base;
|
|
int i;
|
|
|
|
V_FLOW(("v42bis_decode_match(%d)", codeword));
|
|
|
|
for (base = DICT(codeword); CODE(base); base = DICT(base->parent))
|
|
{
|
|
path[path_size++] = base;
|
|
if (path_size >= 256)
|
|
{
|
|
v42bis_c_error(state, "path_size exceeds 256!");
|
|
break;
|
|
}
|
|
#ifdef DEBUG
|
|
if (base == DICT(base->parent))
|
|
{
|
|
logf("loop! node %d", CODE(base));
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* XXX this should not be done here! */
|
|
if (codeword < N5 && DICT(codeword)->byte == 0)
|
|
{
|
|
DICT(codeword)->byte = codeword - N6;
|
|
}
|
|
|
|
D_DEBUG(("path_size %d", path_size));
|
|
|
|
for (i = path_size - 1; i >= 0; i--)
|
|
{
|
|
D_DEBUG(("put byte %02x '%s'",
|
|
path[i]->byte, show_char(path[i]->byte)));
|
|
|
|
if (path[i]->byte == state->escape)
|
|
{
|
|
state->escape += ESCAPE_CYCLE;
|
|
}
|
|
|
|
PUT(path[i]->byte);
|
|
}
|
|
|
|
*psize = path_size;
|
|
|
|
/* return first (prefix) char of string */
|
|
*pRetChar = path[path_size-1]->byte;
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
decode L-DATA semantics on the decode side
|
|
|
|
decode a buffer full of data...
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_decode_buffer(state, data, pDataSize)
|
|
v42bis_t *state;
|
|
PUCHAR data;
|
|
int *pDataSize;
|
|
{
|
|
USHORT codeword;
|
|
UCHAR ch;
|
|
DWORD dwRetCode;
|
|
|
|
|
|
V_FLOW(("v42bis_decode_buffer() %d bytes", *pDataSize));
|
|
|
|
while (*pDataSize)
|
|
{
|
|
//
|
|
// did we have an overflow? if so, stop right here
|
|
//
|
|
if (state->OverFlowBytes && data)
|
|
{
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO,
|
|
("Arap v42bis: %d bytes overflowed, suspending decomp\n",
|
|
state->OverFlowBytes));
|
|
|
|
return(ARAPERR_BUF_TOO_SMALL);
|
|
}
|
|
|
|
#if DBG
|
|
if (state->OverFlowBytes && !data)
|
|
{
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
|
|
("Arap v42bis: ALERT! ALERT: (%d)\n",state->OverFlowBytes));
|
|
|
|
ASSERT(0);
|
|
}
|
|
#endif
|
|
|
|
(*pDataSize)--;
|
|
|
|
if (data)
|
|
{
|
|
/* we have a buffer */
|
|
D_DEBUG(("pull 0x%x", *data & 0xff));
|
|
codeword = v42bis_decode_codeword(state, *data++);
|
|
}
|
|
else
|
|
{
|
|
/* no input buffer, flush */
|
|
codeword = v42bis_decode_codeword_flush(state);
|
|
*pDataSize = 0;
|
|
}
|
|
|
|
DE_DEBUG(("codeword %d (0x%x)", codeword, codeword));
|
|
|
|
/* if decode did not return a value, return */
|
|
if (codeword == 0xffff)
|
|
{
|
|
/* no data */
|
|
D_DEBUG(("no data"));
|
|
|
|
continue;
|
|
}
|
|
|
|
if (state->transparent)
|
|
{
|
|
/* transparent mode */
|
|
|
|
/* escaped - look at next codeword */
|
|
if (state->escaped)
|
|
{
|
|
state->escaped = FALSE;
|
|
|
|
DE_DEBUG(("escape codeword"));
|
|
|
|
/* section 5.8d */
|
|
if (codeword >= 3 && codeword <= 255)
|
|
{
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
|
|
("v42: received reserved code word (%d)", codeword));
|
|
v42bis_c_error(state, "received reserved code word");
|
|
break;
|
|
}
|
|
|
|
switch (codeword)
|
|
{
|
|
case CCW_ECM:
|
|
DE_DEBUG(("enter compression mode"));
|
|
state->transparent = FALSE;
|
|
|
|
/* set up for decode */
|
|
state->last_decode = state->last_match;
|
|
state->last_decode_size = state->string_size;
|
|
|
|
state->exception_next = TRUE;
|
|
break;
|
|
|
|
case CCW_EID:
|
|
DE_DEBUG(("escape id"));
|
|
|
|
codeword = state->escape;
|
|
state->escape += ESCAPE_CYCLE;
|
|
goto decode_encode;
|
|
break;
|
|
|
|
case CCW_RESET:
|
|
DE_DEBUG(("reset"));
|
|
|
|
v42bis_init_dictionary(state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
/* escape? */
|
|
if (codeword == state->escape)
|
|
{
|
|
DE_DEBUG(("escape prefix"));
|
|
state->escaped = TRUE;
|
|
continue;
|
|
}
|
|
|
|
decode_encode:
|
|
/* save data in output buffer */
|
|
PUT((UCHAR)codeword);
|
|
|
|
/* encode to build dictionary */
|
|
ch = (UCHAR)codeword;
|
|
|
|
dwRetCode = v42bis_encode_buffer(state, &ch, 1);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int size;
|
|
|
|
/* compression mode */
|
|
switch (codeword)
|
|
{
|
|
case CCW_ETM:
|
|
DE_DEBUG(("enter transparent mode"));
|
|
|
|
v42bis_decode_codeword_flush(state);
|
|
state->transparent = TRUE;
|
|
state->last_match = state->last_decode;
|
|
state->string_size = state->last_decode_size;
|
|
state->last_new = 0;
|
|
|
|
state->just_flushed = 1;
|
|
break;
|
|
|
|
case CCW_FLUSH:
|
|
DE_DEBUG(("flush"));
|
|
|
|
/* terminate search */
|
|
state->last_match = 0;
|
|
state->string_size = 0;
|
|
state->last_match = state->last_decode;
|
|
state->string_size = state->last_decode_size;
|
|
state->last_new = 0;
|
|
|
|
/* reset codeword decode machine */
|
|
state->bits_waiting = 0;
|
|
state->bits_remaining = 0;
|
|
break;
|
|
|
|
case CCW_STEPUP:
|
|
DE_DEBUG(("stepup"));
|
|
|
|
/* section 5.8a */;
|
|
if (state->c2 + 1 > state->n1)
|
|
{
|
|
v42bis_c_error(state, "received STEPUP; c2 exceeds max");
|
|
}
|
|
else
|
|
{
|
|
state->c2++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* regular codeword */
|
|
|
|
/* section 5.8b */
|
|
if (codeword == state->c1)
|
|
{
|
|
#ifdef DEBUG
|
|
logf(state, "received codeword equal to c1");
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
/* section 5.8c */
|
|
if (codeword >= N5 && state->dictionary[codeword].parent == 0)
|
|
{
|
|
#ifdef DEBUG
|
|
logf("received unused codeword %d, full %d, c1 %d",
|
|
codeword, state->dict_full, state->c1);
|
|
#endif
|
|
v42bis_c_error(state, "received unused codeword");
|
|
}
|
|
|
|
dwRetCode = v42bis_decode_match(state, codeword, &size, &ch);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/*
|
|
* umm... "New dictionary entries shall be created using
|
|
* the proceedure defined in section 6.4, with the first
|
|
* (prefix) character of the most recently decoded string
|
|
* being appended to the previously decoded string."
|
|
*
|
|
* what a pain this was to get right!
|
|
*/
|
|
|
|
/* section 8 */
|
|
state->last_match = state->last_decode;
|
|
state->string_size = state->last_decode_size;
|
|
|
|
dwRetCode = v42bis_encode_buffer(state, &ch, 1);
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
state->last_decode = codeword;
|
|
state->last_decode_size = (UCHAR)size;
|
|
}
|
|
}
|
|
}
|
|
|
|
dwRetCode = (state->OverFlowBytes) ?
|
|
ARAPERR_BUF_TOO_SMALL : ARAPERR_NO_ERROR;
|
|
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/*
|
|
v42bis_decode_flush()
|
|
|
|
flush codeword decoder and push out data
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_decode_flush(state)
|
|
v42bis_t *state;
|
|
{
|
|
DWORD dwRetCode;
|
|
int one;
|
|
|
|
V_FLOW(("v42bis_decode_flush()"));
|
|
|
|
one = 1;
|
|
|
|
dwRetCode = v42bis_decode_buffer(state, (PUCHAR )0, &one);
|
|
|
|
return(dwRetCode);
|
|
}
|
|
|
|
/*
|
|
v42bis_c_error()
|
|
|
|
implements C-ERROR semantics
|
|
*/
|
|
|
|
DWORD
|
|
v42bis_c_error(state, reason_string)
|
|
v42bis_t *state;
|
|
char *reason_string;
|
|
{
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
|
|
("v42bis: C-ERROR with '%s' on %s",reason_string,
|
|
state == &((v42bis_connection_t *)state->connection)->encode ?
|
|
"encode" : "decode"));
|
|
|
|
ASSERT(0);
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
v42bis_init_buffer(state, buf, size)
|
|
v42bis_t *state;
|
|
PUCHAR buf;
|
|
int size;
|
|
{
|
|
state->output_buffer = buf;
|
|
state->output_ptr = buf;
|
|
state->output_size = 0;
|
|
state->output_max = (USHORT)size;
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
DWORD
|
|
v42bis_connection_init(conn)
|
|
v42bis_connection_t *conn;
|
|
{
|
|
conn->default_p0 = 3;
|
|
conn->default_p1 = DEF_P1; /* total # of codewords */
|
|
conn->default_p2 = DEF_P2; /* max string length */
|
|
|
|
conn->neg_p0 = conn->default_p0; /* direction of compression */
|
|
conn->neg_p1 = conn->default_p1; /* total # of codewords */
|
|
conn->neg_p2 = (UCHAR)conn->default_p2; /* max string length */
|
|
|
|
/* encode side */
|
|
conn->encode.connection = (void *)conn;
|
|
conn->encode.decode_only = FALSE;
|
|
|
|
v42bis_init(&conn->encode);
|
|
|
|
/* decode side */
|
|
conn->decode.connection = (void *)conn;
|
|
conn->decode.decode_only = TRUE;
|
|
|
|
v42bis_init(&conn->decode);
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
v42bis_connection_init_buffers(conn, e_buf, e_size, d_buf, d_size)
|
|
v42bis_connection_t *conn;
|
|
PUCHAR e_buf;
|
|
int e_size;
|
|
PUCHAR d_buf;
|
|
int d_size;
|
|
{
|
|
v42bis_init_buffer(&conn->encode, e_buf, e_size);
|
|
v42bis_init_buffer(&conn->decode, d_buf, d_size);
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
v42bis_connection_init_push(conn, context, e_push, d_push)
|
|
v42bis_connection_t *conn;
|
|
void *context;
|
|
void (*e_push)();
|
|
void (*d_push)();
|
|
{
|
|
conn->encode.push_func = e_push;
|
|
conn->encode.push_context = context;
|
|
conn->decode.push_func = d_push;
|
|
conn->decode.push_context = context;
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
/* ------------- debug -------------- */
|
|
|
|
#ifdef DEBUG
|
|
|
|
DWORD
|
|
v42bis_dumptree_follownode(state, node)
|
|
v42bis_t *state;
|
|
USHORT node;
|
|
{
|
|
int i;
|
|
node_t *n = DICT(node);
|
|
|
|
for (i = 0; i < state->dump_indent; i++)
|
|
fprintf(stderr, " ");
|
|
|
|
fprintf(stderr, "code %d; char %d '%s' parent %d, node %d, leaf %d\n",
|
|
node, n->byte, show_char(n->byte), n->parent, n->node, n->leaf);
|
|
|
|
if (n->node)
|
|
v42bis_dumptree_follownode(state, n->node);
|
|
|
|
state->dump_indent++;
|
|
|
|
if (n->leaf)
|
|
v42bis_dumptree_follownode(state, n->leaf);
|
|
|
|
state->dump_indent--;
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
v42bis_dumptree(state, name)
|
|
v42bis_t *state;
|
|
char *name;
|
|
{
|
|
int i;
|
|
|
|
fprintf(stderr, "%s codewords:\n", name);
|
|
for (i = 0; i < CODES; i++)
|
|
if (state->dictionary[i].byte) {
|
|
node_t *n = &state->dictionary[i];
|
|
|
|
fprintf(stderr, "code %d; char %d '%s' parent %d, node %d, leaf %d\n",
|
|
i, n->byte, show_char(n->byte),
|
|
n->parent, n->node, n->leaf);
|
|
}
|
|
|
|
state->dump_indent = 0;
|
|
|
|
fprintf(stderr, "%s tree:\n", name);
|
|
for (i = 0; i < N5; i++)
|
|
if (state->dictionary[i].byte) {
|
|
node_t *n = &state->dictionary[i];
|
|
|
|
fprintf(stderr, "code %d; root node, %d '%s', leaf %d:\n",
|
|
i, n->byte, show_char(n->byte), n->leaf);
|
|
|
|
if (state->dictionary[i].leaf) {
|
|
state->dump_indent = 1;
|
|
v42bis_dumptree_follownode(state, n->leaf);
|
|
}
|
|
}
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
v42bis_connection_dumptree(conn)
|
|
v42bis_connection_t *conn;
|
|
{
|
|
int i;
|
|
|
|
fprintf(stderr, "\nv42bis_connection_dumptree()\n");
|
|
|
|
v42bis_dumptree(&conn->encode, "encode");
|
|
v42bis_dumptree(&conn->decode, "decode");
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
|
|
/* ------------- external interface -------------- */
|
|
|
|
DWORD
|
|
v42bis_mnp_set_debug(pArapConn)
|
|
PARAPCONN pArapConn;
|
|
{
|
|
#if DEBUG
|
|
pArapConn->v42bis.decode.debug_decode = 0;
|
|
pArapConn->v42bis.decode.debug_encode = 0;
|
|
|
|
switch (pArapConn->debug_v42) {
|
|
case 3:
|
|
pArapConn->v42bis.decode.debug_flow = TRUE;
|
|
pArapConn->v42bis.encode.debug_flow = TRUE;
|
|
/* fall through */
|
|
|
|
case 2:
|
|
pArapConn->v42bis.decode.debug_decode_bytes = TRUE;
|
|
pArapConn->v42bis.decode.debug_encode_bytes = TRUE;
|
|
|
|
pArapConn->v42bis.encode.debug_encode_bytes = TRUE;
|
|
|
|
pArapConn->v42bis.decode.debug_decode++;
|
|
pArapConn->v42bis.decode.debug_encode++;
|
|
|
|
/* fall through */
|
|
|
|
case 1:
|
|
pArapConn->v42bis.decode.debug_decode++;
|
|
pArapConn->v42bis.decode.debug_encode++;
|
|
|
|
pArapConn->v42bis.encode.debug_encode = TRUE;
|
|
break;
|
|
|
|
case 0:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return ARAPERR_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
v42bis_send()
|
|
|
|
send data to V.42bis connection
|
|
|
|
input: unsigned char *buffer; pointer to user data buffer
|
|
int buflen; length of user data buffer
|
|
|
|
output: int retcode - if positive, the number of data bytes
|
|
copied from the user data buffer;
|
|
if negative, link error code
|
|
*/
|
|
|
|
|
|
/*
|
|
v42bis_receive()
|
|
|
|
receive data from V.42bis connection
|
|
|
|
input: unsigned char *buffer; pointer to user buffer
|
|
int buflen; length of user buffer
|
|
|
|
output: int retcode; if positive, the number of data bytes
|
|
copied into the user data buffer;
|
|
if negative, link error code
|
|
*/
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Interface functions
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
v42bisInit(
|
|
IN PARAPCONN pArapConn,
|
|
IN PBYTE pReq,
|
|
OUT DWORD *dwReqToSkip,
|
|
OUT PBYTE pFrame,
|
|
OUT DWORD *dwFrameToSkip
|
|
)
|
|
{
|
|
|
|
BYTE VarLen;
|
|
BOOLEAN fV42Bis=TRUE;
|
|
|
|
|
|
DBG_ARAP_CHECK_PAGED_CODE();
|
|
|
|
if (ArapGlobs.V42bisEnabled)
|
|
{
|
|
*pFrame++ = MNP_LR_V42BIS;
|
|
VarLen = *pReq;
|
|
*pFrame++ = *pReq++;
|
|
|
|
RtlCopyMemory(pFrame, pReq, VarLen);
|
|
|
|
fV42Bis = TRUE;
|
|
|
|
*dwReqToSkip = (VarLen+1);
|
|
*dwFrameToSkip = (VarLen+2);
|
|
|
|
/* init the connection (both encode and decode */
|
|
v42bis_connection_init(pArapConn->pV42bis);
|
|
|
|
}
|
|
else
|
|
{
|
|
// send the v42bis type, but 0 for all parms
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
|
|
("v42bisInit: no v42bis (type 1: i.e. 0 for all parms) on %lx\n",pArapConn));
|
|
|
|
*pFrame++ = MNP_LR_V42BIS;
|
|
VarLen = *pReq;
|
|
*pFrame++ = *pReq++;
|
|
|
|
*pFrame++ = 0;
|
|
*pFrame++ = 0;
|
|
*pFrame++ = 0;
|
|
*pFrame++ = 0;
|
|
|
|
fV42Bis = FALSE;
|
|
|
|
*dwReqToSkip = (VarLen+1);
|
|
*dwFrameToSkip = (VarLen+2);
|
|
|
|
//
|
|
// the other two possibilities to indicate no compression: the one above works,
|
|
// the following two retained just in case we need later
|
|
//
|
|
#if 0
|
|
// send the v42bis type, but 0 for the direction flags: other parms valid
|
|
{
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
|
|
("v42bisInit: no v42bis (type 2: i.e. 0 only for direction) on %lx\n",pArapConn));
|
|
|
|
*pFrame++ = MNP_LR_V42BIS;
|
|
VarLen = *pReq;
|
|
*pFrame++ = *pReq++;
|
|
|
|
*pFrame++ = 0;
|
|
*pFrame++ = 0;
|
|
*pFrame++ = 0x8;
|
|
*pFrame++ = 0xfa;
|
|
|
|
fV42Bis = FALSE;
|
|
|
|
*dwReqToSkip = (VarLen+1);
|
|
*dwFrameToSkip = (VarLen+2);
|
|
}
|
|
|
|
// skip the v42bis type altogether
|
|
{
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_ERR,
|
|
("v42bisInit: no v42bis (type 3: i.e. not sending v42bis type) on %lx\n",pArapConn));
|
|
|
|
VarLen = *pReq;
|
|
|
|
fV42Bis = FALSE;
|
|
|
|
*dwReqToSkip = (VarLen+1);
|
|
*dwFrameToSkip = 0;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return(fV42Bis);
|
|
}
|
|
|
|
DWORD
|
|
v42bisCompress(
|
|
IN PARAPCONN pArapConn,
|
|
IN PUCHAR pUncompressedData,
|
|
IN DWORD UnCompressedDataLen,
|
|
OUT PUCHAR pCompressedData,
|
|
OUT DWORD CompressedDataBufSize,
|
|
OUT DWORD *pCompressedDataLen
|
|
)
|
|
{
|
|
DWORD dwRetCode;
|
|
|
|
|
|
DBG_ARAP_CHECK_PAGED_CODE();
|
|
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO,("v42bisCompress (%lx, %lx, %ld)\n",
|
|
pArapConn, pUncompressedData, UnCompressedDataLen));
|
|
|
|
#ifdef V42_DUMP_ENABLED
|
|
if (pArapConn->v42_dump) {
|
|
pArapConn->v42_size = buflen;
|
|
pArapConn->v42_type = 2;
|
|
write(pArapConn->v42_dump, &pArapConn->v42_esize, 4);
|
|
write(pArapConn->v42_dump, bufptr, buflen);
|
|
}
|
|
#endif
|
|
|
|
v42bis_init_buffer(&pArapConn->pV42bis->encode,
|
|
pCompressedData,
|
|
CompressedDataBufSize);
|
|
|
|
dwRetCode = v42bis_encode_buffer(&pArapConn->pV42bis->encode,
|
|
pUncompressedData,
|
|
UnCompressedDataLen);
|
|
|
|
if (dwRetCode != ARAPERR_NO_ERROR)
|
|
{
|
|
return(dwRetCode);
|
|
}
|
|
|
|
dwRetCode = v42bis_encode_flush(&pArapConn->pV42bis->encode);
|
|
|
|
// set the length of compressed data
|
|
*pCompressedDataLen = pArapConn->pV42bis->encode.output_size;
|
|
|
|
return(dwRetCode);
|
|
}
|
|
|
|
DWORD
|
|
v42bisDecompress(
|
|
IN PARAPCONN pArapConn,
|
|
IN PUCHAR pCompressedData,
|
|
IN DWORD CompressedDataLen,
|
|
OUT PUCHAR pDecompressedData,
|
|
OUT DWORD DecompressedDataBufSize,
|
|
OUT DWORD *pByteStillToDecompress,
|
|
OUT DWORD *pDecompressedDataLen
|
|
)
|
|
{
|
|
|
|
DWORD dwRetCode;
|
|
DWORD dwRemaingDataSize;
|
|
DWORD dwOverFlow;
|
|
|
|
|
|
DBG_ARAP_CHECK_PAGED_CODE();
|
|
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO,("v42bisDecompress (%lx, %lx, %ld)\n",
|
|
pArapConn, pCompressedData, CompressedDataLen));
|
|
|
|
|
|
*pDecompressedDataLen = 0;
|
|
|
|
dwRemaingDataSize = CompressedDataLen;
|
|
|
|
*pByteStillToDecompress = CompressedDataLen;
|
|
|
|
|
|
|
|
#ifdef V42_DUMP_ENABLED
|
|
if (pArapConn->v42_dump) {
|
|
pArapConn->v42_size = mnp_size;
|
|
pArapConn->v42_type = 2;
|
|
write(pArapConn->v42_dump, &pArapConn->v42_size, 4 + mnp_size);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// if we had an overflow in the previous decomp effort, we have bytes in
|
|
// the overflow buffer: copy those in first.
|
|
//
|
|
if ( (dwOverFlow = pArapConn->pV42bis->decode.OverFlowBytes) > 0)
|
|
{
|
|
if (DecompressedDataBufSize <= dwOverFlow)
|
|
{
|
|
return(ARAPERR_BUF_TOO_SMALL);
|
|
}
|
|
|
|
|
|
DBGPRINT(DBG_COMP_RAS, DBG_LEVEL_INFO,
|
|
("Arap v42bis: (%lx) copying %d overflow bytes first\n",
|
|
pArapConn, dwOverFlow));
|
|
|
|
RtlCopyMemory(pDecompressedData,
|
|
pArapConn->pV42bis->decode.OverFlowBuf,
|
|
dwOverFlow);
|
|
|
|
pDecompressedData += dwOverFlow;
|
|
|
|
DecompressedDataBufSize -= dwOverFlow;
|
|
|
|
pArapConn->pV42bis->decode.OverFlowBytes = 0;
|
|
|
|
*pDecompressedDataLen += dwOverFlow;
|
|
}
|
|
|
|
|
|
//
|
|
// this can happen if we got called because we told in a previous call that
|
|
// there was buffer overflow and there was nothing more left to decompress
|
|
//
|
|
if (CompressedDataLen == 0)
|
|
{
|
|
return(ARAPERR_NO_ERROR);
|
|
}
|
|
|
|
//
|
|
// set decomp buffer to the buffer supplied
|
|
//
|
|
v42bis_init_buffer(&pArapConn->pV42bis->decode,
|
|
pDecompressedData,
|
|
DecompressedDataBufSize);
|
|
|
|
/* decode everything we got */
|
|
dwRetCode = v42bis_decode_buffer(&pArapConn->pV42bis->decode,
|
|
pCompressedData,
|
|
&dwRemaingDataSize);
|
|
|
|
|
|
*pByteStillToDecompress = dwRemaingDataSize;
|
|
|
|
|
|
//
|
|
// how big is the decompressed data?
|
|
//
|
|
*pDecompressedDataLen += pArapConn->pV42bis->decode.output_size;
|
|
|
|
return(dwRetCode);
|
|
}
|
|
|