Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v42bis.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
| author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
|---|---|
| date | Fri, 25 Jun 2010 15:50:58 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 3:c6c5a16ce2f2 | 4:26cd8f1ef0b1 |
|---|---|
| 1 /* | |
| 2 * SpanDSP - a series of DSP components for telephony | |
| 3 * | |
| 4 * v42bis.c | |
| 5 * | |
| 6 * Written by Steve Underwood <steveu@coppice.org> | |
| 7 * | |
| 8 * Copyright (C) 2005 Steve Underwood | |
| 9 * | |
| 10 * All rights reserved. | |
| 11 * | |
| 12 * This program is free software; you can redistribute it and/or modify | |
| 13 * it under the terms of the GNU Lesser General Public License version 2.1, | |
| 14 * as published by the Free Software Foundation. | |
| 15 * | |
| 16 * This program is distributed in the hope that it will be useful, | |
| 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 19 * GNU Lesser General Public License for more details. | |
| 20 * | |
| 21 * You should have received a copy of the GNU Lesser General Public | |
| 22 * License along with this program; if not, write to the Free Software | |
| 23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 24 * | |
| 25 * $Id: v42bis.c,v 1.37 2009/02/10 13:06:47 steveu Exp $ | |
| 26 */ | |
| 27 | |
| 28 /* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. | |
| 29 Currently it performs the core compression and decompression functions OK. | |
| 30 However, a number of the bells and whistles in V.42bis are incomplete. */ | |
| 31 | |
| 32 /*! \file */ | |
| 33 | |
| 34 #if defined(HAVE_CONFIG_H) | |
| 35 #include "config.h" | |
| 36 #endif | |
| 37 | |
| 38 #include <stdio.h> | |
| 39 #include <stdlib.h> | |
| 40 #include <inttypes.h> | |
| 41 #include <string.h> | |
| 42 #include <errno.h> | |
| 43 #include <fcntl.h> | |
| 44 #include <ctype.h> | |
| 45 #include <assert.h> | |
| 46 | |
| 47 #include "spandsp/telephony.h" | |
| 48 #include "spandsp/logging.h" | |
| 49 #include "spandsp/bit_operations.h" | |
| 50 #include "spandsp/v42bis.h" | |
| 51 | |
| 52 #include "spandsp/private/logging.h" | |
| 53 #include "spandsp/private/v42bis.h" | |
| 54 | |
| 55 /* Fixed parameters from the spec. */ | |
| 56 #define V42BIS_N3 8 /* Character size (bits) */ | |
| 57 #define V42BIS_N4 256 /* Number of characters in the alphabet */ | |
| 58 #define V42BIS_N5 (V42BIS_N4 + V42BIS_N6) /* Index number of first dictionary entry used to store a string */ | |
| 59 #define V42BIS_N6 3 /* Number of control codewords */ | |
| 60 | |
| 61 /* Control code words in compressed mode */ | |
| 62 enum | |
| 63 { | |
| 64 V42BIS_ETM = 0, /* Enter transparent mode */ | |
| 65 V42BIS_FLUSH = 1, /* Flush data */ | |
| 66 V42BIS_STEPUP = 2 /* Step up codeword size */ | |
| 67 }; | |
| 68 | |
| 69 /* Command codes in transparent mode */ | |
| 70 enum | |
| 71 { | |
| 72 V42BIS_ECM = 0, /* Enter compression mode */ | |
| 73 V42BIS_EID = 1, /* Escape character in data */ | |
| 74 V42BIS_RESET = 2 /* Force reinitialisation */ | |
| 75 }; | |
| 76 | |
| 77 static __inline__ void push_compressed_raw_octet(v42bis_compress_state_t *ss, int octet) | |
| 78 { | |
| 79 ss->output_buf[ss->output_octet_count++] = (uint8_t) octet; | |
| 80 if (ss->output_octet_count >= ss->max_len) | |
| 81 { | |
| 82 ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count); | |
| 83 ss->output_octet_count = 0; | |
| 84 } | |
| 85 } | |
| 86 /*- End of function --------------------------------------------------------*/ | |
| 87 | |
| 88 static __inline__ void push_compressed_code(v42bis_compress_state_t *ss, int code) | |
| 89 { | |
| 90 ss->output_bit_buffer |= code << (32 - ss->v42bis_parm_c2 - ss->output_bit_count); | |
| 91 ss->output_bit_count += ss->v42bis_parm_c2; | |
| 92 while (ss->output_bit_count >= 8) | |
| 93 { | |
| 94 push_compressed_raw_octet(ss, ss->output_bit_buffer >> 24); | |
| 95 ss->output_bit_buffer <<= 8; | |
| 96 ss->output_bit_count -= 8; | |
| 97 } | |
| 98 } | |
| 99 /*- End of function --------------------------------------------------------*/ | |
| 100 | |
| 101 static __inline__ void push_compressed_octet(v42bis_compress_state_t *ss, int code) | |
| 102 { | |
| 103 ss->output_bit_buffer |= code << (32 - 8 - ss->output_bit_count); | |
| 104 ss->output_bit_count += 8; | |
| 105 while (ss->output_bit_count >= 8) | |
| 106 { | |
| 107 push_compressed_raw_octet(ss, ss->output_bit_buffer >> 24); | |
| 108 ss->output_bit_buffer <<= 8; | |
| 109 ss->output_bit_count -= 8; | |
| 110 } | |
| 111 } | |
| 112 /*- End of function --------------------------------------------------------*/ | |
| 113 | |
| 114 SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t *buf, int len) | |
| 115 { | |
| 116 int ptr; | |
| 117 int i; | |
| 118 uint32_t octet; | |
| 119 uint32_t code; | |
| 120 v42bis_compress_state_t *ss; | |
| 121 | |
| 122 ss = &s->compress; | |
| 123 if ((s->v42bis_parm_p0 & 2) == 0) | |
| 124 { | |
| 125 /* Compression is off - just push the incoming data out */ | |
| 126 for (i = 0; i < len - ss->max_len; i += ss->max_len) | |
| 127 ss->handler(ss->user_data, buf + i, ss->max_len); | |
| 128 if (i < len) | |
| 129 ss->handler(ss->user_data, buf + i, len - i); | |
| 130 return 0; | |
| 131 } | |
| 132 ptr = 0; | |
| 133 if (ss->first && len > 0) | |
| 134 { | |
| 135 octet = buf[ptr++]; | |
| 136 ss->string_code = octet + V42BIS_N6; | |
| 137 if (ss->transparent) | |
| 138 push_compressed_octet(ss, octet); | |
| 139 ss->first = FALSE; | |
| 140 } | |
| 141 while (ptr < len) | |
| 142 { | |
| 143 octet = buf[ptr++]; | |
| 144 if ((ss->dict[ss->string_code].children[octet >> 5] & (1 << (octet & 0x1F)))) | |
| 145 { | |
| 146 /* The leaf exists. Now find it in the table. */ | |
| 147 /* TODO: This is a brute force scan for a match. We need something better. */ | |
| 148 for (code = 0; code < ss->v42bis_parm_c3; code++) | |
| 149 { | |
| 150 if (ss->dict[code].parent_code == ss->string_code && ss->dict[code].node_octet == octet) | |
| 151 break; | |
| 152 } | |
| 153 } | |
| 154 else | |
| 155 { | |
| 156 /* The leaf does not exist. */ | |
| 157 code = s->v42bis_parm_n2; | |
| 158 } | |
| 159 /* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry | |
| 160 created by the last invocation of the string matching procedure, then the | |
| 161 next character shall be read and appended to the string and this step | |
| 162 repeated. */ | |
| 163 if (code < ss->v42bis_parm_c3 && code != ss->latest_code) | |
| 164 { | |
| 165 /* The string was found */ | |
| 166 ss->string_code = code; | |
| 167 ss->string_length++; | |
| 168 } | |
| 169 else | |
| 170 { | |
| 171 /* The string is not in the table. */ | |
| 172 if (!ss->transparent) | |
| 173 { | |
| 174 /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */ | |
| 175 while (ss->v42bis_parm_c1 >= ss->v42bis_parm_c3 && ss->v42bis_parm_c3 <= s->v42bis_parm_n2) | |
| 176 { | |
| 177 /* We need to increase the codeword size */ | |
| 178 /* 7.4(a) */ | |
| 179 push_compressed_code(ss, V42BIS_STEPUP); | |
| 180 /* 7.4(b) */ | |
| 181 ss->v42bis_parm_c2++; | |
| 182 /* 7.4(c) */ | |
| 183 ss->v42bis_parm_c3 <<= 1; | |
| 184 /* 7.4(d) this might need to be repeated, so we loop */ | |
| 185 } | |
| 186 /* 7.5 Transfer - output the last state of the string */ | |
| 187 push_compressed_code(ss, ss->string_code); | |
| 188 } | |
| 189 /* 7.6 Dictionary updating */ | |
| 190 /* 6.4 Add the string to the dictionary */ | |
| 191 /* 6.4(b) The string is not in the table. */ | |
| 192 if (code != ss->latest_code && ss->string_length < s->v42bis_parm_n7) | |
| 193 { | |
| 194 ss->latest_code = ss->v42bis_parm_c1; | |
| 195 /* 6.4(a) The length of the string is in range for adding to the dictionary */ | |
| 196 /* If the last code was a leaf, it no longer is */ | |
| 197 ss->dict[ss->string_code].leaves++; | |
| 198 ss->dict[ss->string_code].children[octet >> 5] |= (1 << (octet & 0x1F)); | |
| 199 /* The new one is definitely a leaf */ | |
| 200 ss->dict[ss->v42bis_parm_c1].parent_code = (uint16_t) ss->string_code; | |
| 201 ss->dict[ss->v42bis_parm_c1].leaves = 0; | |
| 202 ss->dict[ss->v42bis_parm_c1].node_octet = (uint8_t) octet; | |
| 203 /* 7.7 Node recovery */ | |
| 204 /* 6.5 Recovering a dictionary entry to use next */ | |
| 205 for (;;) | |
| 206 { | |
| 207 /* 6.5(a) and (b) */ | |
| 208 if ((int) (++ss->v42bis_parm_c1) >= s->v42bis_parm_n2) | |
| 209 ss->v42bis_parm_c1 = V42BIS_N5; | |
| 210 /* 6.5(c) We need to reuse a leaf node */ | |
| 211 if (ss->dict[ss->v42bis_parm_c1].leaves) | |
| 212 continue; | |
| 213 if (ss->dict[ss->v42bis_parm_c1].parent_code == 0xFFFF) | |
| 214 break; | |
| 215 /* 6.5(d) Detach the leaf node from its parent, and re-use it */ | |
| 216 /* Possibly make the parent a leaf node again */ | |
| 217 ss->dict[ss->dict[ss->v42bis_parm_c1].parent_code].leaves--; | |
| 218 ss->dict[ss->dict[ss->v42bis_parm_c1].parent_code].children[ss->dict[ss->v42bis_parm_c1].node_octet >> 5] &= ~(1 << (ss->dict[ss->v42bis_parm_c1].node_octet & 0x1F)); | |
| 219 ss->dict[ss->v42bis_parm_c1].parent_code = 0xFFFF; | |
| 220 break; | |
| 221 } | |
| 222 } | |
| 223 else | |
| 224 { | |
| 225 ss->latest_code = 0xFFFFFFFF; | |
| 226 } | |
| 227 /* 7.8 Data compressibility test */ | |
| 228 /* Filter on the balance of what went into the compressor, and what came out */ | |
| 229 ss->compressibility_filter += ((((8*ss->string_length - ss->v42bis_parm_c2) << 20) - ss->compressibility_filter) >> 10); | |
| 230 if (ss->compression_mode == V42BIS_COMPRESSION_MODE_DYNAMIC) | |
| 231 { | |
| 232 /* Work out if it is appropriate to change between transparent and | |
| 233 compressed mode. */ | |
| 234 if (ss->transparent) | |
| 235 { | |
| 236 if (ss->compressibility_filter > 0) | |
| 237 { | |
| 238 if (++ss->compressibility_persistence > 1000) | |
| 239 { | |
| 240 /* Schedule a switch to compressed mode */ | |
| 241 ss->change_transparency = -1; | |
| 242 ss->compressibility_persistence = 0; | |
| 243 } | |
| 244 } | |
| 245 else | |
| 246 { | |
| 247 ss->compressibility_persistence = 0; | |
| 248 } | |
| 249 } | |
| 250 else | |
| 251 { | |
| 252 if (ss->compressibility_filter < 0) | |
| 253 { | |
| 254 if (++ss->compressibility_persistence > 1000) | |
| 255 { | |
| 256 /* Schedule a switch to transparent mode */ | |
| 257 ss->change_transparency = 1; | |
| 258 ss->compressibility_persistence = 0; | |
| 259 } | |
| 260 } | |
| 261 else | |
| 262 { | |
| 263 ss->compressibility_persistence = 0; | |
| 264 } | |
| 265 } | |
| 266 } | |
| 267 if (ss->change_transparency) | |
| 268 { | |
| 269 if (ss->change_transparency < 0) | |
| 270 { | |
| 271 if (ss->transparent) | |
| 272 { | |
| 273 printf("Going compressed\n"); | |
| 274 /* 7.8.1 Transition to compressed mode */ | |
| 275 /* Switch out of transparent now, between codes. We need to send the octet which did not | |
| 276 match, just before switching. */ | |
| 277 if (octet == ss->escape_code) | |
| 278 { | |
| 279 push_compressed_octet(ss, ss->escape_code++); | |
| 280 push_compressed_octet(ss, V42BIS_EID); | |
| 281 } | |
| 282 else | |
| 283 { | |
| 284 push_compressed_octet(ss, octet); | |
| 285 } | |
| 286 push_compressed_octet(ss, ss->escape_code++); | |
| 287 push_compressed_octet(ss, V42BIS_ECM); | |
| 288 ss->transparent = FALSE; | |
| 289 } | |
| 290 } | |
| 291 else | |
| 292 { | |
| 293 if (!ss->transparent) | |
| 294 { | |
| 295 printf("Going transparent\n"); | |
| 296 /* 7.8.2 Transition to transparent mode */ | |
| 297 /* Switch into transparent now, between codes, and the unmatched octet should | |
| 298 go out in transparent mode, just below */ | |
| 299 push_compressed_code(ss, V42BIS_ETM); | |
| 300 ss->transparent = TRUE; | |
| 301 } | |
| 302 } | |
| 303 ss->change_transparency = 0; | |
| 304 } | |
| 305 /* 7.8.3 Reset function - TODO */ | |
| 306 ss->string_code = octet + V42BIS_N6; | |
| 307 ss->string_length = 1; | |
| 308 } | |
| 309 if (ss->transparent) | |
| 310 { | |
| 311 if (octet == ss->escape_code) | |
| 312 { | |
| 313 push_compressed_octet(ss, ss->escape_code++); | |
| 314 push_compressed_octet(ss, V42BIS_EID); | |
| 315 } | |
| 316 else | |
| 317 { | |
| 318 push_compressed_octet(ss, octet); | |
| 319 } | |
| 320 } | |
| 321 } | |
| 322 return 0; | |
| 323 } | |
| 324 /*- End of function --------------------------------------------------------*/ | |
| 325 | |
| 326 SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s) | |
| 327 { | |
| 328 v42bis_compress_state_t *ss; | |
| 329 | |
| 330 ss = &s->compress; | |
| 331 if (!ss->transparent) | |
| 332 { | |
| 333 /* Output the last state of the string */ | |
| 334 push_compressed_code(ss, ss->string_code); | |
| 335 /* TODO: We use a positive FLUSH at all times. It is really needed, if the | |
| 336 previous step resulted in no leftover bits. */ | |
| 337 push_compressed_code(ss, V42BIS_FLUSH); | |
| 338 } | |
| 339 while (ss->output_bit_count > 0) | |
| 340 { | |
| 341 push_compressed_raw_octet(ss, ss->output_bit_buffer >> 24); | |
| 342 ss->output_bit_buffer <<= 8; | |
| 343 ss->output_bit_count -= 8; | |
| 344 } | |
| 345 /* Now push out anything remaining. */ | |
| 346 if (ss->output_octet_count > 0) | |
| 347 { | |
| 348 ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count); | |
| 349 ss->output_octet_count = 0; | |
| 350 } | |
| 351 return 0; | |
| 352 } | |
| 353 /*- End of function --------------------------------------------------------*/ | |
| 354 | |
| 355 #if 0 | |
| 356 SPAN_DECLARE(int) v42bis_compress_dump(v42bis_state_t *s) | |
| 357 { | |
| 358 int i; | |
| 359 | |
| 360 for (i = 0; i < V42BIS_MAX_CODEWORDS; i++) | |
| 361 { | |
| 362 if (s->compress.dict[i].parent_code != 0xFFFF) | |
| 363 { | |
| 364 printf("Entry %4x, prior %4x, leaves %d, octet %2x\n", i, s->compress.dict[i].parent_code, s->compress.dict[i].leaves, s->compress.dict[i].node_octet); | |
| 365 } | |
| 366 } | |
| 367 return 0; | |
| 368 } | |
| 369 /*- End of function --------------------------------------------------------*/ | |
| 370 #endif | |
| 371 | |
| 372 SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t *buf, int len) | |
| 373 { | |
| 374 int ptr; | |
| 375 int i; | |
| 376 int this_length; | |
| 377 uint8_t *string; | |
| 378 uint32_t code; | |
| 379 uint32_t new_code; | |
| 380 int code_len; | |
| 381 v42bis_decompress_state_t *ss; | |
| 382 uint8_t decode_buf[V42BIS_MAX_STRING_SIZE]; | |
| 383 | |
| 384 ss = &s->decompress; | |
| 385 if ((s->v42bis_parm_p0 & 1) == 0) | |
| 386 { | |
| 387 /* Compression is off - just push the incoming data out */ | |
| 388 for (i = 0; i < len - ss->max_len; i += ss->max_len) | |
| 389 ss->handler(ss->user_data, buf + i, ss->max_len); | |
| 390 if (i < len) | |
| 391 ss->handler(ss->user_data, buf + i, len - i); | |
| 392 return 0; | |
| 393 } | |
| 394 ptr = 0; | |
| 395 code_len = (ss->transparent) ? 8 : ss->v42bis_parm_c2; | |
| 396 for (;;) | |
| 397 { | |
| 398 /* Fill up the bit buffer. */ | |
| 399 while (ss->input_bit_count < 32 - 8 && ptr < len) | |
| 400 { | |
| 401 ss->input_bit_count += 8; | |
| 402 ss->input_bit_buffer |= (uint32_t) buf[ptr++] << (32 - ss->input_bit_count); | |
| 403 } | |
| 404 if (ss->input_bit_count < code_len) | |
| 405 break; | |
| 406 new_code = ss->input_bit_buffer >> (32 - code_len); | |
| 407 ss->input_bit_count -= code_len; | |
| 408 ss->input_bit_buffer <<= code_len; | |
| 409 if (ss->transparent) | |
| 410 { | |
| 411 code = new_code; | |
| 412 if (ss->escaped) | |
| 413 { | |
| 414 ss->escaped = FALSE; | |
| 415 if (code == V42BIS_ECM) | |
| 416 { | |
| 417 printf("Hit V42BIS_ECM\n"); | |
| 418 ss->transparent = FALSE; | |
| 419 code_len = ss->v42bis_parm_c2; | |
| 420 } | |
| 421 else if (code == V42BIS_EID) | |
| 422 { | |
| 423 printf("Hit V42BIS_EID\n"); | |
| 424 ss->output_buf[ss->output_octet_count++] = ss->escape_code - 1; | |
| 425 if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7) | |
| 426 { | |
| 427 ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count); | |
| 428 ss->output_octet_count = 0; | |
| 429 } | |
| 430 } | |
| 431 else if (code == V42BIS_RESET) | |
| 432 { | |
| 433 printf("Hit V42BIS_RESET\n"); | |
| 434 } | |
| 435 else | |
| 436 { | |
| 437 printf("Hit V42BIS_???? - %" PRIu32 "\n", code); | |
| 438 } | |
| 439 } | |
| 440 else if (code == ss->escape_code) | |
| 441 { | |
| 442 ss->escape_code++; | |
| 443 ss->escaped = TRUE; | |
| 444 } | |
| 445 else | |
| 446 { | |
| 447 ss->output_buf[ss->output_octet_count++] = (uint8_t) code; | |
| 448 if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7) | |
| 449 { | |
| 450 ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count); | |
| 451 ss->output_octet_count = 0; | |
| 452 } | |
| 453 } | |
| 454 } | |
| 455 else | |
| 456 { | |
| 457 if (new_code < V42BIS_N6) | |
| 458 { | |
| 459 /* We have a control code. */ | |
| 460 switch (new_code) | |
| 461 { | |
| 462 case V42BIS_ETM: | |
| 463 printf("Hit V42BIS_ETM\n"); | |
| 464 ss->transparent = TRUE; | |
| 465 code_len = 8; | |
| 466 break; | |
| 467 case V42BIS_FLUSH: | |
| 468 printf("Hit V42BIS_FLUSH\n"); | |
| 469 v42bis_decompress_flush(s); | |
| 470 break; | |
| 471 case V42BIS_STEPUP: | |
| 472 /* We need to increase the codeword size */ | |
| 473 printf("Hit V42BIS_STEPUP\n"); | |
| 474 if (ss->v42bis_parm_c3 >= s->v42bis_parm_n2) | |
| 475 { | |
| 476 /* Invalid condition */ | |
| 477 return -1; | |
| 478 } | |
| 479 code_len = ++ss->v42bis_parm_c2; | |
| 480 ss->v42bis_parm_c3 <<= 1; | |
| 481 break; | |
| 482 } | |
| 483 continue; | |
| 484 } | |
| 485 if (ss->first) | |
| 486 { | |
| 487 ss->first = FALSE; | |
| 488 ss->octet = new_code - V42BIS_N6; | |
| 489 ss->output_buf[0] = (uint8_t) ss->octet; | |
| 490 ss->output_octet_count = 1; | |
| 491 if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7) | |
| 492 { | |
| 493 ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count); | |
| 494 ss->output_octet_count = 0; | |
| 495 } | |
| 496 ss->old_code = new_code; | |
| 497 continue; | |
| 498 } | |
| 499 /* Start at the end of the buffer, and decode backwards */ | |
| 500 string = &decode_buf[V42BIS_MAX_STRING_SIZE - 1]; | |
| 501 /* Check the received code is valid. It can't be too big, as we pulled only the expected number | |
| 502 of bits from the input stream. It could, however, be unknown. */ | |
| 503 if (ss->dict[new_code].parent_code == 0xFFFF) | |
| 504 return -1; | |
| 505 /* Otherwise we do a straight decode of the new code. */ | |
| 506 code = new_code; | |
| 507 /* Trace back through the octets which form the string, and output them. */ | |
| 508 while (code >= V42BIS_N5) | |
| 509 { | |
| 510 if (code > 4095) {printf("Code is 0x%" PRIu32 "\n", code); exit(2);} | |
| 511 *string-- = ss->dict[code].node_octet; | |
| 512 code = ss->dict[code].parent_code; | |
| 513 } | |
| 514 *string = (uint8_t) (code - V42BIS_N6); | |
| 515 ss->octet = code - V42BIS_N6; | |
| 516 /* Output the decoded string. */ | |
| 517 this_length = V42BIS_MAX_STRING_SIZE - (int) (string - decode_buf); | |
| 518 memcpy(ss->output_buf + ss->output_octet_count, string, this_length); | |
| 519 ss->output_octet_count += this_length; | |
| 520 if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7) | |
| 521 { | |
| 522 ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count); | |
| 523 ss->output_octet_count = 0; | |
| 524 } | |
| 525 /* 6.4 Add the string to the dictionary */ | |
| 526 if (ss->last_length < s->v42bis_parm_n7) | |
| 527 { | |
| 528 /* 6.4(a) The string does not exceed N7 in length */ | |
| 529 if (ss->last_old_code != ss->old_code | |
| 530 || | |
| 531 ss->last_extra_octet != *string) | |
| 532 { | |
| 533 /* 6.4(b) The string is not in the table. */ | |
| 534 ss->dict[ss->old_code].leaves++; | |
| 535 /* The new one is definitely a leaf */ | |
| 536 ss->dict[ss->v42bis_parm_c1].parent_code = (uint16_t) ss->old_code; | |
| 537 ss->dict[ss->v42bis_parm_c1].leaves = 0; | |
| 538 ss->dict[ss->v42bis_parm_c1].node_octet = (uint8_t) ss->octet; | |
| 539 /* 6.5 Recovering a dictionary entry to use next */ | |
| 540 for (;;) | |
| 541 { | |
| 542 /* 6.5(a) and (b) */ | |
| 543 if (++ss->v42bis_parm_c1 >= s->v42bis_parm_n2) | |
| 544 ss->v42bis_parm_c1 = V42BIS_N5; | |
| 545 /* 6.5(c) We need to reuse a leaf node */ | |
| 546 if (ss->dict[ss->v42bis_parm_c1].leaves) | |
| 547 continue; | |
| 548 /* 6.5(d) This is a leaf node, so re-use it */ | |
| 549 /* Possibly make the parent a leaf node again */ | |
| 550 if (ss->dict[ss->v42bis_parm_c1].parent_code != 0xFFFF) | |
| 551 ss->dict[ss->dict[ss->v42bis_parm_c1].parent_code].leaves--; | |
| 552 ss->dict[ss->v42bis_parm_c1].parent_code = 0xFFFF; | |
| 553 break; | |
| 554 } | |
| 555 } | |
| 556 } | |
| 557 /* Record the addition to the dictionary, so we can check for repeat attempts | |
| 558 at the next code - see II.4.3 */ | |
| 559 ss->last_old_code = ss->old_code; | |
| 560 ss->last_extra_octet = *string; | |
| 561 | |
| 562 ss->old_code = new_code; | |
| 563 ss->last_length = this_length; | |
| 564 } | |
| 565 } | |
| 566 return 0; | |
| 567 } | |
| 568 /*- End of function --------------------------------------------------------*/ | |
| 569 | |
| 570 SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s) | |
| 571 { | |
| 572 v42bis_decompress_state_t *ss; | |
| 573 | |
| 574 ss = &s->decompress; | |
| 575 /* Push out anything remaining. */ | |
| 576 if (ss->output_octet_count > 0) | |
| 577 { | |
| 578 ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count); | |
| 579 ss->output_octet_count = 0; | |
| 580 } | |
| 581 return 0; | |
| 582 } | |
| 583 /*- End of function --------------------------------------------------------*/ | |
| 584 | |
| 585 #if 0 | |
| 586 SPAN_DECLARE(int) v42bis_decompress_dump(v42bis_state_t *s) | |
| 587 { | |
| 588 int i; | |
| 589 | |
| 590 for (i = 0; i < V42BIS_MAX_CODEWORDS; i++) | |
| 591 { | |
| 592 if (s->decompress.dict[i].parent_code != 0xFFFF) | |
| 593 { | |
| 594 printf("Entry %4x, prior %4x, leaves %d, octet %2x\n", i, s->decompress.dict[i].parent_code, s->decompress.dict[i].leaves, s->decompress.dict[i].node_octet); | |
| 595 } | |
| 596 } | |
| 597 return 0; | |
| 598 } | |
| 599 /*- End of function --------------------------------------------------------*/ | |
| 600 #endif | |
| 601 | |
| 602 SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode) | |
| 603 { | |
| 604 s->compress.compression_mode = mode; | |
| 605 switch (mode) | |
| 606 { | |
| 607 case V42BIS_COMPRESSION_MODE_ALWAYS: | |
| 608 s->compress.change_transparency = -1; | |
| 609 break; | |
| 610 case V42BIS_COMPRESSION_MODE_NEVER: | |
| 611 s->compress.change_transparency = 1; | |
| 612 break; | |
| 613 } | |
| 614 } | |
| 615 /*- End of function --------------------------------------------------------*/ | |
| 616 | |
| 617 SPAN_DECLARE(v42bis_state_t *) v42bis_init(v42bis_state_t *s, | |
| 618 int negotiated_p0, | |
| 619 int negotiated_p1, | |
| 620 int negotiated_p2, | |
| 621 v42bis_frame_handler_t frame_handler, | |
| 622 void *frame_user_data, | |
| 623 int max_frame_len, | |
| 624 v42bis_data_handler_t data_handler, | |
| 625 void *data_user_data, | |
| 626 int max_data_len) | |
| 627 { | |
| 628 int i; | |
| 629 | |
| 630 if (negotiated_p1 < 512 || negotiated_p1 > 65535) | |
| 631 return NULL; | |
| 632 if (negotiated_p2 < 6 || negotiated_p2 > V42BIS_MAX_STRING_SIZE) | |
| 633 return NULL; | |
| 634 if (s == NULL) | |
| 635 { | |
| 636 if ((s = (v42bis_state_t *) malloc(sizeof(*s))) == NULL) | |
| 637 return NULL; | |
| 638 } | |
| 639 memset(s, 0, sizeof(*s)); | |
| 640 | |
| 641 s->compress.handler = frame_handler; | |
| 642 s->compress.user_data = frame_user_data; | |
| 643 s->compress.max_len = (max_frame_len < 1024) ? max_frame_len : 1024; | |
| 644 | |
| 645 s->decompress.handler = data_handler; | |
| 646 s->decompress.user_data = data_user_data; | |
| 647 s->decompress.max_len = (max_data_len < 1024) ? max_data_len : 1024; | |
| 648 | |
| 649 s->v42bis_parm_p0 = negotiated_p0; /* default is both ways off */ | |
| 650 | |
| 651 s->v42bis_parm_n1 = top_bit(negotiated_p1 - 1) + 1; | |
| 652 s->v42bis_parm_n2 = negotiated_p1; | |
| 653 s->v42bis_parm_n7 = negotiated_p2; | |
| 654 | |
| 655 /* 6.5 */ | |
| 656 s->compress.v42bis_parm_c1 = | |
| 657 s->decompress.v42bis_parm_c1 = V42BIS_N5; | |
| 658 | |
| 659 s->compress.v42bis_parm_c2 = | |
| 660 s->decompress.v42bis_parm_c2 = V42BIS_N3 + 1; | |
| 661 | |
| 662 s->compress.v42bis_parm_c3 = | |
| 663 s->decompress.v42bis_parm_c3 = 2*V42BIS_N4; | |
| 664 | |
| 665 s->compress.first = | |
| 666 s->decompress.first = TRUE; | |
| 667 for (i = 0; i < V42BIS_MAX_CODEWORDS; i++) | |
| 668 { | |
| 669 s->compress.dict[i].parent_code = | |
| 670 s->decompress.dict[i].parent_code = 0xFFFF; | |
| 671 s->compress.dict[i].leaves = | |
| 672 s->decompress.dict[i].leaves = 0; | |
| 673 } | |
| 674 /* Point the root nodes for decompression to themselves. It doesn't matter much what | |
| 675 they are set to, as long as they are considered "known" codes. */ | |
| 676 for (i = 0; i < V42BIS_N5; i++) | |
| 677 s->decompress.dict[i].parent_code = (uint16_t) i; | |
| 678 s->compress.string_code = 0xFFFFFFFF; | |
| 679 s->compress.latest_code = 0xFFFFFFFF; | |
| 680 | |
| 681 s->decompress.last_old_code = 0xFFFFFFFF; | |
| 682 s->decompress.last_extra_octet = -1; | |
| 683 | |
| 684 s->compress.compression_mode = V42BIS_COMPRESSION_MODE_DYNAMIC; | |
| 685 | |
| 686 return s; | |
| 687 } | |
| 688 /*- End of function --------------------------------------------------------*/ | |
| 689 | |
| 690 SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s) | |
| 691 { | |
| 692 return 0; | |
| 693 } | |
| 694 /*- End of function --------------------------------------------------------*/ | |
| 695 | |
| 696 SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s) | |
| 697 { | |
| 698 free(s); | |
| 699 return 0; | |
| 700 } | |
| 701 /*- End of function --------------------------------------------------------*/ | |
| 702 /*- End of file ------------------------------------------------------------*/ |
