v42bis.c
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
int v42bis_release(v42bis_state_t *s)
|
691 |
{ |
692 |
return 0; |
693 |
} |
694 |
/*- End of function --------------------------------------------------------*/
|
695 |
|
696 |
int v42bis_free(v42bis_state_t *s)
|
697 |
{ |
698 |
free(s); |
699 |
return 0; |
700 |
} |
701 |
/*- End of function --------------------------------------------------------*/
|
702 |
/*- End of file ------------------------------------------------------------*/
|