| /* |
| * SpanDSP - a series of DSP components for telephony |
| * |
| * v42bis.c |
| * |
| * Written by Steve Underwood <steveu@coppice.org> |
| * |
| * Copyright (C) 2005, 2011 Steve Underwood |
| * |
| * All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU Lesser General Public License version 2.1, |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| /* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. |
| Currently it performs the core compression and decompression functions OK. |
| However, a number of the bells and whistles in V.42bis are incomplete. */ |
| |
| /*! \file */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <inttypes.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <ctype.h> |
| #include <assert.h> |
| |
| #include <openbsc/v42bis.h> |
| #include <openbsc/v42bis_private.h> |
| #include <openbsc/debug.h> |
| #include <osmocom/core/talloc.h> |
| |
| |
| #define span_log(x,y,msg, ...) DEBUGP(DV42BIS,msg, ##__VA_ARGS__) |
| #define span_log_init(x,y,z) |
| #define span_log_set_protocol(x,y) |
| |
| |
| #define FALSE 0 |
| #define TRUE 1 |
| |
| /* Fixed parameters from the spec. */ |
| /* Character size (bits) */ |
| #define V42BIS_N3 8 |
| /* Number of characters in the alphabet */ |
| #define V42BIS_N4 256 |
| /* Index number of first dictionary entry used to store a string */ |
| #define V42BIS_N5 (V42BIS_N4 + V42BIS_N6) |
| /* Number of control codewords */ |
| #define V42BIS_N6 3 |
| /* V.42bis/9.2 */ |
| #define V42BIS_ESC_STEP 51 |
| |
| /* Compreeibility monitoring parameters for assessing automated switches between |
| transparent and compressed mode */ |
| #define COMPRESSIBILITY_MONITOR (256*V42BIS_N3) |
| #define COMPRESSIBILITY_MONITOR_HYSTERESIS 11 |
| |
| /* Control code words in compressed mode */ |
| enum |
| { |
| V42BIS_ETM = 0, /* Enter transparent mode */ |
| V42BIS_FLUSH = 1, /* Flush data */ |
| V42BIS_STEPUP = 2 /* Step up codeword size */ |
| }; |
| |
| /* Command codes in transparent mode */ |
| enum |
| { |
| V42BIS_ECM = 0, /* Enter compression mode */ |
| V42BIS_EID = 1, /* Escape character in data */ |
| V42BIS_RESET = 2 /* Force reinitialisation */ |
| }; |
| |
| static __inline__ void push_octet(v42bis_comp_state_t *s, int octet) |
| { |
| s->output_buf[s->output_octet_count++] = (uint8_t) octet; |
| if (s->output_octet_count >= s->max_output_len) |
| { |
| s->handler(s->user_data, s->output_buf, s->output_octet_count); |
| s->output_octet_count = 0; |
| } |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len) |
| { |
| int i; |
| int chunk; |
| |
| i = 0; |
| while ((s->output_octet_count + len - i) >= s->max_output_len) |
| { |
| chunk = s->max_output_len - s->output_octet_count; |
| memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk); |
| s->handler(s->user_data, s->output_buf, s->max_output_len); |
| s->output_octet_count = 0; |
| i += chunk; |
| } |
| chunk = len - i; |
| if (chunk > 0) |
| { |
| memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk); |
| s->output_octet_count += chunk; |
| } |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code) |
| { |
| s->bit_buffer |= code << s->bit_count; |
| s->bit_count += s->v42bis_parm_c2; |
| while (s->bit_count >= 8) |
| { |
| push_octet(s, s->bit_buffer & 0xFF); |
| s->bit_buffer >>= 8; |
| s->bit_count -= 8; |
| } |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static __inline__ void push_octet_alignment(v42bis_comp_state_t *s) |
| { |
| if ((s->bit_count & 7)) |
| { |
| s->bit_count += (8 - (s->bit_count & 7)); |
| while (s->bit_count >= 8) |
| { |
| push_octet(s, s->bit_buffer & 0xFF); |
| s->bit_buffer >>= 8; |
| s->bit_count -= 8; |
| } |
| } |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static __inline__ void flush_octets(v42bis_comp_state_t *s) |
| { |
| if (s->output_octet_count > 0) |
| { |
| s->handler(s->user_data, s->output_buf, s->output_octet_count); |
| s->output_octet_count = 0; |
| } |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static void dictionary_init(v42bis_comp_state_t *s) |
| { |
| int i; |
| |
| memset(s->dict, 0, sizeof(s->dict)); |
| for (i = 0; i < V42BIS_N4; i++) |
| s->dict[i + V42BIS_N6].node_octet = i; |
| s->v42bis_parm_c1 = V42BIS_N5; |
| s->v42bis_parm_c2 = V42BIS_N3 + 1; |
| s->v42bis_parm_c3 = V42BIS_N4 << 1; |
| s->last_matched = 0; |
| s->update_at = 0; |
| s->last_added = 0; |
| s->bit_buffer = 0; |
| s->bit_count = 0; |
| s->flushed_length = 0; |
| s->string_length = 0; |
| s->escape_code = 0; |
| s->transparent = TRUE; |
| s->escaped = FALSE; |
| s->compression_performance = COMPRESSIBILITY_MONITOR; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet) |
| { |
| uint16_t e; |
| |
| if (at == 0) |
| return octet + V42BIS_N6; |
| e = s->dict[at].child; |
| while (e) |
| { |
| if (s->dict[e].node_octet == octet) |
| return e; |
| e = s->dict[e].next; |
| } |
| return 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet) |
| { |
| uint16_t newx; |
| uint16_t next; |
| uint16_t e; |
| |
| newx = s->v42bis_parm_c1; |
| s->dict[newx].node_octet = octet; |
| s->dict[newx].parent = at; |
| s->dict[newx].child = 0; |
| s->dict[newx].next = s->dict[at].child; |
| s->dict[at].child = newx; |
| next = newx; |
| /* 6.5 Recovering a dictionary entry to use next */ |
| do |
| { |
| /* 6.5(a) and (b) */ |
| if (++next == s->v42bis_parm_n2) |
| next = V42BIS_N5; |
| } |
| while (s->dict[next].child); |
| /* 6.5(c) We need to reuse a leaf node */ |
| if (s->dict[next].parent) |
| { |
| /* 6.5(d) Detach the leaf node from its parent, and re-use it */ |
| e = s->dict[next].parent; |
| if (s->dict[e].child == next) |
| { |
| s->dict[e].child = s->dict[next].next; |
| } |
| else |
| { |
| e = s->dict[e].child; |
| while (s->dict[e].next != next) |
| e = s->dict[e].next; |
| s->dict[e].next = s->dict[next].next; |
| } |
| } |
| s->v42bis_parm_c1 = next; |
| return newx; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static void send_string(v42bis_comp_state_t *s) |
| { |
| push_octets(s, s->string, s->string_length); |
| s->string_length = 0; |
| s->flushed_length = 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code) |
| { |
| int i; |
| uint16_t p; |
| |
| /* Work out the length */ |
| for (i = 0, p = code; p; i++) |
| p = s->dict[p].parent; |
| s->string_length += i; |
| /* Now expand the known length of string */ |
| i = s->string_length - 1; |
| for (p = code; p; ) |
| { |
| s->string[i--] = s->dict[p].node_octet; |
| p = s->dict[p].parent; |
| } |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code) |
| { |
| int i; |
| |
| /* Update compressibility metric */ |
| /* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */ |
| s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR); |
| if (s->transparent) |
| { |
| for (i = 0; i < s->string_length; i++) |
| { |
| push_octet(s, s->string[i]); |
| if (s->string[i] == s->escape_code) |
| { |
| push_octet(s, V42BIS_EID); |
| s->escape_code += V42BIS_ESC_STEP; |
| } |
| } |
| } |
| else |
| { |
| /* Allow for any escape octets in the string */ |
| for (i = 0; i < s->string_length; i++) |
| { |
| if (s->string[i] == s->escape_code) |
| s->escape_code += V42BIS_ESC_STEP; |
| } |
| /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */ |
| while (code >= s->v42bis_parm_c3) |
| { |
| /* We need to increase the codeword size */ |
| /* 7.4(a) */ |
| push_compressed_code(s, V42BIS_STEPUP); |
| /* 7.4(b) */ |
| s->v42bis_parm_c2++; |
| /* 7.4(c) */ |
| s->v42bis_parm_c3 <<= 1; |
| /* 7.4(d) this might need to be repeated, so we loop */ |
| } |
| /* 7.5 Transfer - output the last state of the string */ |
| push_compressed_code(s, code); |
| } |
| s->string_length = 0; |
| s->flushed_length = 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static void go_compressed(v42bis_state_t *ss) |
| { |
| v42bis_comp_state_t *s; |
| |
| s = &ss->compress; |
| if (!s->transparent) |
| return; |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n"); |
| /* Switch out of transparent now, between codes. We need to send the octet which did not |
| match, just before switching. */ |
| if (s->last_matched) |
| { |
| s->update_at = s->last_matched; |
| send_encoded_data(s, s->last_matched); |
| s->last_matched = 0; |
| } |
| push_octet(s, s->escape_code); |
| push_octet(s, V42BIS_ECM); |
| s->bit_buffer = 0; |
| s->transparent = FALSE; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static void go_transparent(v42bis_state_t *ss) |
| { |
| v42bis_comp_state_t *s; |
| |
| s = &ss->compress; |
| if (s->transparent) |
| return; |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n"); |
| /* Switch into transparent now, between codes, and the unmatched octet should |
| go out in transparent mode, just below */ |
| if (s->last_matched) |
| { |
| s->update_at = s->last_matched; |
| send_encoded_data(s, s->last_matched); |
| s->last_matched = 0; |
| } |
| s->last_added = 0; |
| push_compressed_code(s, V42BIS_ETM); |
| push_octet_alignment(s); |
| s->transparent = TRUE; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static void monitor_for_mode_change(v42bis_state_t *ss) |
| { |
| v42bis_comp_state_t *s; |
| |
| s = &ss->compress; |
| switch (s->compression_mode) |
| { |
| case V42BIS_COMPRESSION_MODE_DYNAMIC: |
| /* 7.8 Data compressibility test */ |
| if (s->transparent) |
| { |
| if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS) |
| { |
| /* 7.8.1 Transition to compressed mode */ |
| go_compressed(ss); |
| } |
| } |
| else |
| { |
| if (s->compression_performance > COMPRESSIBILITY_MONITOR) |
| { |
| /* 7.8.2 Transition to transparent mode */ |
| go_transparent(ss); |
| } |
| } |
| /* 7.8.3 Reset function - TODO */ |
| break; |
| case V42BIS_COMPRESSION_MODE_ALWAYS: |
| if (s->transparent) |
| go_compressed(ss); |
| break; |
| case V42BIS_COMPRESSION_MODE_NEVER: |
| if (!s->transparent) |
| go_transparent(ss); |
| break; |
| } |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static int v42bis_comp_init(v42bis_comp_state_t *s, |
| int p1, |
| int p2, |
| put_msg_func_t handler, |
| void *user_data, |
| int max_output_len) |
| { |
| memset(s, 0, sizeof(*s)); |
| s->v42bis_parm_n2 = p1; |
| s->v42bis_parm_n7 = p2; |
| s->handler = handler; |
| s->user_data = user_data; |
| s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH) ? max_output_len : V42BIS_MAX_OUTPUT_LENGTH; |
| s->output_octet_count = 0; |
| dictionary_init(s); |
| return 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| static int comp_exit(v42bis_comp_state_t *s) |
| { |
| s->v42bis_parm_n2 = 0; |
| return 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len) |
| { |
| v42bis_comp_state_t *s; |
| int i; |
| uint16_t code; |
| |
| s = &ss->compress; |
| if (!s->v42bis_parm_p0) |
| { |
| /* Compression is off - just push the incoming data out */ |
| push_octets(s, buf, len); |
| return 0; |
| } |
| for (i = 0; i < len; ) |
| { |
| /* 6.4 Add the string to the dictionary */ |
| if (s->update_at) |
| { |
| if (match_octet(s, s->update_at, buf[i]) == 0) |
| s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]); |
| s->update_at = 0; |
| } |
| /* Match string */ |
| while (i < len) |
| { |
| code = match_octet(s, s->last_matched, buf[i]); |
| if (code == 0) |
| { |
| s->update_at = s->last_matched; |
| send_encoded_data(s, s->last_matched); |
| s->last_matched = 0; |
| break; |
| } |
| if (code == s->last_added) |
| { |
| s->last_added = 0; |
| send_encoded_data(s, s->last_matched); |
| s->last_matched = 0; |
| break; |
| } |
| s->last_matched = code; |
| /* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry |
| created by the last invocation of the string matching procedure, then the |
| next character shall be read and appended to the string and this step |
| repeated. */ |
| s->string[s->string_length++] = buf[i++]; |
| /* 6.4(a) The string must not exceed N7 in length */ |
| if (s->string_length + s->flushed_length == s->v42bis_parm_n7) |
| { |
| send_encoded_data(s, s->last_matched); |
| s->last_matched = 0; |
| break; |
| } |
| } |
| monitor_for_mode_change(ss); |
| } |
| return 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss) |
| { |
| v42bis_comp_state_t *s; |
| int len; |
| |
| s = &ss->compress; |
| if (s->update_at) |
| return 0; |
| if (s->last_matched) |
| { |
| len = s->string_length; |
| send_encoded_data(s, s->last_matched); |
| s->flushed_length += len; |
| } |
| if (!s->transparent) |
| { |
| s->update_at = s->last_matched; |
| s->last_matched = 0; |
| s->flushed_length = 0; |
| push_compressed_code(s, V42BIS_FLUSH); |
| push_octet_alignment(s); |
| } |
| flush_octets(s); |
| return 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len) |
| { |
| v42bis_comp_state_t *s; |
| int i; |
| int j; |
| int yyy; |
| uint16_t code; |
| uint16_t p; |
| uint8_t ch; |
| uint8_t in; |
| |
| s = &ss->decompress; |
| if (!s->v42bis_parm_p0) |
| { |
| /* Compression is off - just push the incoming data out */ |
| push_octets(s, buf, len); |
| return 0; |
| } |
| for (i = 0; i < len; ) |
| { |
| if (s->transparent) |
| { |
| in = buf[i]; |
| if (s->escaped) |
| { |
| /* Command */ |
| s->escaped = FALSE; |
| switch (in) |
| { |
| case V42BIS_ECM: |
| /* Enter compressed mode */ |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n"); |
| send_string(s); |
| s->transparent = FALSE; |
| s->update_at = s->last_matched; |
| s->last_matched = 0; |
| i++; |
| continue; |
| case V42BIS_EID: |
| /* Escape symbol */ |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n"); |
| in = s->escape_code; |
| s->escape_code += V42BIS_ESC_STEP; |
| break; |
| case V42BIS_RESET: |
| /* Reset dictionary */ |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n"); |
| /* TODO: */ |
| send_string(s); |
| dictionary_init(s); |
| i++; |
| continue; |
| default: |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in); |
| return -1; |
| } |
| } |
| else if (in == s->escape_code) |
| { |
| s->escaped = TRUE; |
| i++; |
| continue; |
| } |
| |
| yyy = TRUE; |
| for (j = 0; j < 2 && yyy; j++) |
| { |
| if (s->update_at) |
| { |
| if (match_octet(s, s->update_at, in) == 0) |
| s->last_added = add_octet_to_dictionary(s, s->update_at, in); |
| s->update_at = 0; |
| } |
| |
| code = match_octet(s, s->last_matched, in); |
| if (code == 0) |
| { |
| s->update_at = s->last_matched; |
| send_string(s); |
| s->last_matched = 0; |
| } |
| else if (code == s->last_added) |
| { |
| s->last_added = 0; |
| send_string(s); |
| s->last_matched = 0; |
| } |
| else |
| { |
| s->last_matched = code; |
| s->string[s->string_length++] = in; |
| if (s->string_length + s->flushed_length == s->v42bis_parm_n7) |
| { |
| send_string(s); |
| s->last_matched = 0; |
| } |
| i++; |
| yyy = FALSE; |
| } |
| } |
| } |
| else |
| { |
| /* Get code from input */ |
| while (s->bit_count < s->v42bis_parm_c2 && i < len) |
| { |
| s->bit_buffer |= buf[i++] << s->bit_count; |
| s->bit_count += 8; |
| } |
| if (s->bit_count < s->v42bis_parm_c2) |
| continue; |
| code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1); |
| s->bit_buffer >>= s->v42bis_parm_c2; |
| s->bit_count -= s->v42bis_parm_c2; |
| |
| if (code < V42BIS_N6) |
| { |
| /* We have a control code. */ |
| switch (code) |
| { |
| case V42BIS_ETM: |
| /* Enter transparent mode */ |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n"); |
| s->bit_count = 0; |
| s->transparent = TRUE; |
| s->last_matched = 0; |
| s->last_added = 0; |
| break; |
| case V42BIS_FLUSH: |
| /* Flush signal */ |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n"); |
| s->bit_count = 0; |
| break; |
| case V42BIS_STEPUP: |
| /* Increase code word size */ |
| span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n"); |
| s->v42bis_parm_c2++; |
| s->v42bis_parm_c3 <<= 1; |
| if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3)) |
| return -1; |
| break; |
| } |
| continue; |
| } |
| /* Regular codeword */ |
| if (code == s->v42bis_parm_c1) |
| return -1; |
| expand_codeword_to_string(s, code); |
| if (s->update_at) |
| { |
| ch = s->string[0]; |
| if ((p = match_octet(s, s->update_at, ch)) == 0) |
| { |
| s->last_added = add_octet_to_dictionary(s, s->update_at, ch); |
| if (code == s->v42bis_parm_c1) |
| return -1; |
| } |
| else if (p == s->last_added) |
| { |
| s->last_added = 0; |
| } |
| } |
| s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7) ? 0 : code; |
| /* Allow for any escapes which may be in this string */ |
| for (j = 0; j < s->string_length; j++) |
| { |
| if (s->string[j] == s->escape_code) |
| s->escape_code += V42BIS_ESC_STEP; |
| } |
| send_string(s); |
| } |
| } |
| return 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss) |
| { |
| v42bis_comp_state_t *s; |
| int len; |
| |
| s = &ss->decompress; |
| len = s->string_length; |
| send_string(s); |
| s->flushed_length += len; |
| flush_octets(s); |
| return 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode) |
| { |
| s->compress.compression_mode = mode; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| |
| SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx, |
| 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 *) talloc_zero_size(ctx,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); |
| talloc_free(s); |
| return 0; |
| } |
| /*- End of function --------------------------------------------------------*/ |
| /*- End of file ------------------------------------------------------------*/ |