/*++ 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); }