Logo Search packages:      
Sourcecode: iaxmodem version File versions  Download package

v42bis.c

Go to the documentation of this file.
/*
 * SpanDSP - a series of DSP components for telephony
 *
 * v42bis.c
 *
 * Written by Steve Underwood <steveu@coppice.org>
 *
 * Copyright (C) 2005 Steve Underwood
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: v42bis.c,v 1.26 2006/11/03 13:37:21 steveu Exp $
 */

/* 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 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#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 "spandsp/telephony.h"
#include "spandsp/logging.h"
#include "spandsp/bit_operations.h"
#include "spandsp/v42bis.h"

/* Fixed parameters from the spec. */
#define V42BIS_N3               8   /* Character size (bits) */
#define V42BIS_N4               256 /* Number of characters in the alphabet */
#define V42BIS_N5               (V42BIS_N4 + V42BIS_N6)  /* Index number of first dictionary entry used to store a string */
#define V42BIS_N6               3   /* Number of control codewords */

/* 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_compressed_octet(v42bis_compress_state_t *ss, int octet)
{
    ss->output_buf[ss->output_octet_count++] = (uint8_t) octet;
    if (ss->output_octet_count >= ss->max_len)
    {
        ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
        ss->output_octet_count = 0;
    }
}
/*- End of function --------------------------------------------------------*/

static __inline__ void push_compressed_code(v42bis_compress_state_t *ss, int code)
{
    ss->output_bit_buffer |= code << (32 - ss->v42bis_parm_c2 - ss->output_bit_count);
    ss->output_bit_count += ss->v42bis_parm_c2;
    while (ss->output_bit_count >= 8)
    {
        push_compressed_octet(ss, ss->output_bit_buffer >> 24);
        ss->output_bit_buffer <<= 8;
        ss->output_bit_count -= 8;
    }
}
/*- End of function --------------------------------------------------------*/

00098 int v42bis_compress(v42bis_state_t *s, const uint8_t *buf, int len)
{
    int ptr;
    int i;
    uint32_t octet;
    uint32_t code;
    v42bis_compress_state_t *ss;

    ss = &s->compress;
    if ((s->v42bis_parm_p0 & 2) == 0)
    {
        /* Compression is off - just push the incoming data out */
        for (i = 0;  i < len - ss->max_len;  i += ss->max_len)
            ss->handler(ss->user_data, buf + i, ss->max_len);
        if (i < len)
            ss->handler(ss->user_data, buf + i, len - i);
        return 0;
    }
    ptr = 0;
    if (ss->first  &&  len > 0)
    {
        octet = buf[ptr++];
        ss->string_code = octet + V42BIS_N6;
        if (ss->transparent)
            push_compressed_octet(ss, octet);
        ss->first = FALSE;
    }
    while (ptr < len)
    {
        octet = buf[ptr++];
        if ((ss->dict[ss->string_code].children[octet >> 5] & (1 << (octet & 0x1F))))
        {
            /* The leaf exists. Now find it in the table. */
            /* TODO: This is a brute force scan for a match. We need something better. */
            for (code = 0;  code < s->v42bis_parm_n2;  code++)
            {
                if (ss->dict[code].parent_code == ss->string_code  &&  ss->dict[code].node_octet == octet)
                    break;
            }
        }
        else
        {
            /* The leaf does not exist. */
            code = s->v42bis_parm_n2;
        }
        /* 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. */
        if (code < s->v42bis_parm_n2  &&  code != ss->latest_code)
        {
            /* The string was found */
            ss->string_code = code;
            ss->string_length++;
        }
        else
        {
            /* The string is not in the table. */
            if (!ss->transparent)
            {
                /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */
                while (ss->v42bis_parm_c1 >= ss->v42bis_parm_c3)
                {
                    /* We need to increase the codeword size */
                    /* 7.4(a) */
                    push_compressed_code(ss, V42BIS_STEPUP);
                    /* 7.4(b) */
                    ss->v42bis_parm_c2++;
                    /* 7.4(c) */
                    ss->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(ss, ss->string_code);
            }
            /* 7.6    Dictionary updating */
            /* 6.4    Add the string to the dictionary */
            /* 6.4(b) The string is not in the table. */
            if (code != ss->latest_code  &&  ss->string_length < s->v42bis_parm_n7)
            {
                ss->latest_code = ss->v42bis_parm_c1;
                /* 6.4(a) The length of the string is in range for adding to the dictionary */
                /* If the last code was a leaf, it no longer is */
                ss->dict[ss->string_code].leaves++;
                ss->dict[ss->string_code].children[octet >> 5] |= (1 << (octet & 0x1F));
                /* The new one is definitely a leaf */
                ss->dict[ss->v42bis_parm_c1].parent_code = (uint16_t) ss->string_code;
                ss->dict[ss->v42bis_parm_c1].leaves = 0;
                ss->dict[ss->v42bis_parm_c1].node_octet = (uint8_t) octet;
                /* 7.7    Node recovery */
                /* 6.5    Recovering a dictionary entry to use next */
                for (;;)
                {
                    /* 6.5(a) and (b) */
                    if ((int) (++ss->v42bis_parm_c1) >= s->v42bis_parm_n2)
                        ss->v42bis_parm_c1 = V42BIS_N5;
                    /* 6.5(c) We need to reuse a leaf node */
                    if (ss->dict[ss->v42bis_parm_c1].leaves)
                        continue;
                    if (ss->dict[ss->v42bis_parm_c1].parent_code == 0xFFFF)
                        break;
                    /* 6.5(d) Detach the leaf node from its parent, and re-use it */
                    /* Possibly make the parent a leaf node again */
                    ss->dict[ss->dict[ss->v42bis_parm_c1].parent_code].leaves--;
                    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));
                    ss->dict[ss->v42bis_parm_c1].parent_code = 0xFFFF;
                    break;
                }
            }
            else
            {
                ss->latest_code = 0xFFFFFFFF;
            }
            /* 7.8 Data compressibility test */
            /* Filter on the balance of what went into the compressor, and what came out */
            ss->compressibility_filter += ((((8*ss->string_length - ss->v42bis_parm_c2) << 20) - ss->compressibility_filter) >> 10);
            if (ss->transparent)
            {
                if (ss->compressibility_filter > 0)
                    ss->compressibility_persistence++;
                else
                    ss->compressibility_persistence = 0;
                /* 7.8.1 Transition to compressed mode - TODO */
                if (ss->compressibility_persistence > 1000)
                {
//printf("Compress\n");
#if 0
                    /* Switch out of transparent now, between codes. We need to send the octet which did not
                       match, just before switching. */
                    if (octet == ss->escape_code)
                    {
                        push_compressed_octet(ss, ss->escape_code++);
                        push_compressed_octet(ss, V42BIS_EID);
                    }
                    else
                    {
                        push_compressed_octet(ss, octet);
                    }
                    push_compressed_octet(ss, ss->escape_code++);
                    push_compressed_octet(ss, V42BIS_ECM);
                    ss->transparent = FALSE;
#endif
                    ss->compressibility_persistence = 0;
                }
            }
            else
            {
                if (ss->compressibility_filter < 0)
                    ss->compressibility_persistence++;
                else
                    ss->compressibility_persistence = 0;
                /* 7.8.2 Transition to transparent mode - TODO */
                if (ss->compressibility_persistence > 1000)
                {
//printf("Transparent\n");
#if 0
                    /* Switch into transparent now, between codes, and the unmatched octet should
                       go out in transparent mode, just below */
                    push_compressed_code(ss, V42BIS_ETM);
                    ss->transparent = TRUE;
#endif
                    ss->compressibility_persistence = 0;
                }
            }
            /* 7.8.3 Reset function - TODO */
            ss->string_code = octet + V42BIS_N6;
            ss->string_length = 1;
        }
        if (ss->transparent)
        {
            if (octet == ss->escape_code)
            {
                push_compressed_octet(ss, ss->escape_code++);
                push_compressed_octet(ss, V42BIS_EID);
            }
            else
            {
                push_compressed_octet(ss, octet);
            }
        }
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

00283 int v42bis_compress_flush(v42bis_state_t *s)
{
    v42bis_compress_state_t *ss;

    ss = &s->compress;
    if (!ss->transparent)
    {
        /* Output the last state of the string */
        push_compressed_code(ss, ss->string_code);
        /* TODO: We use a positive FLUSH at all times. It is really needed, if the
           previous step resulted in no leftover bits. */
        push_compressed_code(ss, V42BIS_FLUSH);
        while (ss->output_bit_count > 0)
        {
            push_compressed_octet(ss, ss->output_bit_buffer >> 24);
            ss->output_bit_buffer <<= 8;
            ss->output_bit_count -= 8;
        }
    }
    /* Now push out anything remaining. */
    if (ss->output_octet_count > 0)
    {
        ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
        ss->output_octet_count = 0;
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

#if 0
int v42bis_compress_dump(v42bis_state_t *s)
{
    int i;
    
    for (i = 0;  i < V42BIS_MAX_CODEWORDS;  i++)
    {
        if (s->compress.dict[i].parent_code != 0xFFFF)
        {
            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);
        }
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/
#endif

00329 int v42bis_decompress(v42bis_state_t *s, const uint8_t *buf, int len)
{
    int ptr;
    int i;
    int this_length;
    uint8_t *string;
    uint32_t code;
    uint32_t new_code;
    v42bis_decompress_state_t *ss;
    uint8_t decode_buf[V42BIS_MAX_STRING_SIZE];

    ss = &s->decompress;
    if ((s->v42bis_parm_p0 & 1) == 0)
    {
        /* Compression is off - just push the incoming data out */
        for (i = 0;  i < len - ss->max_len;  i += ss->max_len)
            ss->handler(ss->user_data, buf + i, ss->max_len);
        if (i < len)
            ss->handler(ss->user_data, buf + i, len - i);
        return 0;
    }
    ptr = 0;
    for (;;)
    {
        if (ss->transparent)
        {
            /* TODO: implement transparent mode */
            while (ptr < len)
            {
                code = buf[ptr++];
                if (ss->escaped)
                {
                    ss->escaped = FALSE;
                    switch (code)
                    {
                    case V42BIS_ECM:
                        break;
                    case V42BIS_EID:
                        ss->transparent = FALSE;
                        break;
                    case V42BIS_RESET:
                        break;
                    }
                }
                else if (code == ss->escape_code)
                {
                    ss->escape_code++;
                    ss->escaped = TRUE;
                }
                else
                {
                }
            }
            if (ss->transparent)
                break;
        }
        /* Gather enough bits for at least one whole code word */
        while (ss->input_bit_count <= ss->v42bis_parm_c2  &&  ptr < len)
        {
            ss->input_bit_count += 8;
            ss->input_bit_buffer |= (uint32_t) buf[ptr++] << (32 - ss->input_bit_count);
        }
        if (ss->input_bit_count <= ss->v42bis_parm_c2)
            break;
        new_code = ss->input_bit_buffer >> (32 - ss->v42bis_parm_c2);
        ss->input_bit_count -= ss->v42bis_parm_c2;
        ss->input_bit_buffer <<= ss->v42bis_parm_c2;
        if (new_code < V42BIS_N6)
        {
            /* We have a control code. */
            switch (new_code)
            {
            case V42BIS_ETM:
                printf("Hit V42BIS_ETM\n");
                ss->transparent = TRUE;
                break;
            case V42BIS_FLUSH:
                printf("Hit V42BIS_FLUSH\n");
                v42bis_decompress_flush(s);
                break;
            case V42BIS_STEPUP:
                /* We need to increase the codeword size */
                printf("Hit V42BIS_STEPUP\n");
                if (ss->v42bis_parm_c3 >= s->v42bis_parm_n2)
                {
                    /* Invalid condition */
                    return -1;
                }
                ss->v42bis_parm_c2++;
                ss->v42bis_parm_c3 <<= 1;
                break;
            }
            continue;
        }
        if (ss->first)
        {
            ss->first = FALSE;
            ss->octet = new_code - V42BIS_N6;
            ss->output_buf[0] = (uint8_t) ss->octet;
            ss->output_octet_count = 1;
            if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7)
            {
                ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
                ss->output_octet_count = 0;
            }
            ss->old_code = new_code;
            continue;
        }
        /* Start at the end of the buffer, and decode backwards */
        string = &decode_buf[V42BIS_MAX_STRING_SIZE - 1];
        /* Check the received code is valid. It can't be too big, as we pulled only the expected number
           of bits from the input stream. It could, however, be unknown. */
        if (ss->dict[new_code].parent_code == 0xFFFF)
            return -1;
        /* Otherwise we do a straight decode of the new code. */
        code = new_code;
        /* Trace back through the octets which form the string, and output them. */
        while (code >= V42BIS_N5)
        {
            *string-- = ss->dict[code].node_octet;
            code = ss->dict[code].parent_code;
        }
        *string = (uint8_t) (code - V42BIS_N6);
        ss->octet = code - V42BIS_N6;
        /* Output the decoded string. */
        this_length = V42BIS_MAX_STRING_SIZE - (int) (string - decode_buf);
        memcpy(ss->output_buf + ss->output_octet_count, string, this_length);
        ss->output_octet_count += this_length;
        if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7)
        {
            ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
            ss->output_octet_count = 0;
        }
        /* 6.4 Add the string to the dictionary */
        if (ss->last_length < s->v42bis_parm_n7)
        {
            /* 6.4(a) The string does not exceed N7 in length */
            if (ss->last_old_code != ss->old_code
                ||
                ss->last_extra_octet != *string)
            {
                /* 6.4(b) The string is not in the table. */
                ss->dict[ss->old_code].leaves++;
                /* The new one is definitely a leaf */
                ss->dict[ss->v42bis_parm_c1].parent_code = (uint16_t) ss->old_code;
                ss->dict[ss->v42bis_parm_c1].leaves = 0;
                ss->dict[ss->v42bis_parm_c1].node_octet = (uint8_t) ss->octet;
                /* 6.5 Recovering a dictionary entry to use next */
                for (;;)
                {
                    /* 6.5(a) and (b) */
                    if (++ss->v42bis_parm_c1 >= s->v42bis_parm_n2)
                        ss->v42bis_parm_c1 = V42BIS_N5;
                    /* 6.5(c) We need to reuse a leaf node */
                    if (ss->dict[ss->v42bis_parm_c1].leaves)
                        continue;
                    /* 6.5(d) This is a leaf node, so re-use it */
                    /* Possibly make the parent a leaf node again */
                    if (ss->dict[ss->v42bis_parm_c1].parent_code != 0xFFFF)
                        ss->dict[ss->dict[ss->v42bis_parm_c1].parent_code].leaves--;
                    ss->dict[ss->v42bis_parm_c1].parent_code = 0xFFFF;
                    break;
                }
            }
        }
        /* Record the addition to the dictionary, so we can check for repeat attempts
           at the next code - see II.4.3 */
        ss->last_old_code = ss->old_code;
        ss->last_extra_octet = *string;

        ss->old_code = new_code;
        ss->last_length = this_length;
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

00506 int v42bis_decompress_flush(v42bis_state_t *s)
{
    v42bis_decompress_state_t *ss;

    ss = &s->decompress;
    /* Push out anything remaining. */
    if (ss->output_octet_count > 0)
    {
        ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
        ss->output_octet_count = 0;
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

#if 0
int v42bis_decompress_dump(v42bis_state_t *s)
{
    int i;
    
    for (i = 0;  i < V42BIS_MAX_CODEWORDS;  i++)
    {
        if (s->decompress.dict[i].parent_code != 0xFFFF)
        {
            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);
        }
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/
#endif

00538 v42bis_state_t *v42bis_init(v42bis_state_t *s,
                            int negotiated_p0,
                            int negotiated_p1,
                            int negotiated_p2,
                            v42bis_frame_handler_t frame_handler,
                            void *frame_user_data,
                            int max_frame_len,
                            v42bis_data_handler_t data_handler,
                            void *data_user_data,
                            int max_data_len)
{
    int i;

    if (negotiated_p1 < 512  ||  negotiated_p1 > 65535)
        return NULL;
    if (negotiated_p2 < 6  ||  negotiated_p2 > V42BIS_MAX_STRING_SIZE)
        return NULL;
    if (s == NULL)
    {
        if ((s = (v42bis_state_t *) malloc(sizeof(*s))) == NULL)
            return NULL;
    }
    memset(s, 0, sizeof(*s));

    s->compress.handler = frame_handler;
    s->compress.user_data = frame_user_data;
    s->compress.max_len = (max_frame_len < 1024)  ?  max_frame_len  :  1024;

    s->decompress.handler = data_handler;
    s->decompress.user_data = data_user_data;
    s->decompress.max_len = (max_data_len < 1024)  ?  max_data_len  :  1024;

    s->v42bis_parm_p0 = negotiated_p0;  /* default is both ways off */

    s->v42bis_parm_n1 = top_bit(negotiated_p1 - 1) + 1;
    s->v42bis_parm_n2 = negotiated_p1;
    s->v42bis_parm_n7 = negotiated_p2;

    /* 6.5 */
    s->compress.v42bis_parm_c1 =
    s->decompress.v42bis_parm_c1 = V42BIS_N5;

    s->compress.v42bis_parm_c2 =
    s->decompress.v42bis_parm_c2 = V42BIS_N3 + 1;

    s->compress.v42bis_parm_c3 =
    s->decompress.v42bis_parm_c3 = 2*V42BIS_N4;

    s->compress.first =
    s->decompress.first = TRUE;
    for (i = 0;  i < V42BIS_MAX_CODEWORDS;  i++)
    {
        s->compress.dict[i].parent_code =
        s->decompress.dict[i].parent_code = 0xFFFF;
        s->compress.dict[i].leaves =
        s->decompress.dict[i].leaves = 0;
    }
    /* Point the root nodes for decompression to themselves. It doesn't matter much what
       they are set to, as long as they are considered "known" codes. */
    for (i = 0;  i < V42BIS_N5;  i++)
        s->decompress.dict[i].parent_code = (uint16_t) i;
    s->compress.string_code = 0xFFFFFFFF;
    s->compress.latest_code = 0xFFFFFFFF;
    
    s->decompress.last_old_code = 0xFFFFFFFF;
    s->decompress.last_extra_octet = -1;

    return s;
}
/*- End of function --------------------------------------------------------*/

00609 int v42bis_release(v42bis_state_t *s)
{
    free(s);
    return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index