Exportable version of WizziLab's modem driver.

Dependents:   modem_ref_helper

src/kal_codec.cpp

Committer:
marin_wizzi
Date:
2021-10-29
Revision:
67:e458db8402dc
Parent:
41:6f83174ffed4

File content as of revision 67:e458db8402dc:

/// @copyright
/// ========================================================================={{{
/// Copyright (c) 20XX WizziLab                                                /
/// All rights reserved                                                        /
///                                                                            /
/// IMPORTANT: This Software may not be modified, copied or distributed unless /
/// embedded on a WizziLab product. Other than for the foregoing purpose, this /
/// Software and/or its documentation may not be used, reproduced, copied,     /
/// prepared derivative works of, modified, performed, distributed, displayed  /
/// or sold for any purpose. For the sole purpose of embedding this Software   /
/// on a WizziLab product, copy, modification and distribution of this         /
/// Software is granted provided that the following conditions are respected:  /
///                                                                            /
/// *  Redistributions of source code must retain the above copyright notice,  /
///    this list of conditions and the following disclaimer                    /
///                                                                            /
/// *  Redistributions in binary form must reproduce the above copyright       /
///    notice, this list of conditions and the following disclaimer in the     /
///    documentation and/or other materials provided with the distribution.    /
///                                                                            /
/// *  The name of WizziLab can not be used to endorse or promote products     /
///    derived from this software without specific prior written permission.   /
///                                                                            /
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS        /
/// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  /
/// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR /
/// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR          /
/// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,      /
/// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,        /
/// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,            /
/// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     /
/// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING    /
/// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS         /
/// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.               /
/// WIZZILAB HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,       /
/// ENHANCEMENTS OR MODIFICATIONS.                                             /
///                                                                            /
/// Should you have any questions regarding your right to use this Software,   /
/// contact WizziLab at www.wizzilab.com.                                      /
///                                                                            /
/// =========================================================================}}}
/// @endcopyright
///
/// =======================================================================
///
/// @file           kal_codec.c
/// @brief          Codec Utilities
///
/// =======================================================================

#include <string.h>
#include "hal_types.h"
#include "kal_codec.h"


// ======================================================================
//
//
//                  ASCII to Binary Codec Toolkits 
//
//
// ======================================================================

const char k_nybble_chars[] = "0123456789ABCDEF";

//======================================================================
// hex2ascii
//----------------------------------------------------------------------
/// @brief  Convert hexadecimal number to ascii
/// @param  char* in        input buffer (hex)
/// @param  char* out      output buffer (ascii)
/// @param  u16 len         length of inoput buffer in bytes
//======================================================================
void hex2ascii( char* in, char* out, u16 length)
{
    u8 b;
    while(length--) {
        b = *in++;
        *out++ = k_nybble_chars[ ( b >> 4 ) & 0x0F ];
        *out++ = k_nybble_chars[ b & 0x0F ];
    }
}

//======================================================================
// itoa
//----------------------------------------------------------------------
/// @brief  Converts an integer value to a null-terminated string using the specified base and stores the result
/// in the array given by result parameter.
/// @param  int value       integer to convert
/// @param  char* result    converted string
/// @param  int base        base used for conversion 2, 8, 10, 16
/// @return A pointer to the resulting null-terminated string, same as parameter result
//======================================================================
char* itoa(int value, char* result, int base) 
{
    // check that the base if valid
    //if (base < 2 || base > 16) { return NULL; }

    char* ptr = result, *ptr1 = result, tmp_char;
    int tmp_value;

    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "fedcba9876543210123456789abcdef" [15 + (tmp_value - value * base)];
    } while ( value );

    // Apply negative sign
    if (tmp_value < 0) *ptr++ = '-';
    //*ptr-- = '\0';
    result = ptr--;
    while(ptr1 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr1;
        *ptr1++ = tmp_char;
    }
    return result;
}

//======================================================================
// atoitok
//----------------------------------------------------------------------
/// @brief  Extracts from a ASCII string a value (decimal or hex) and
///         convert it to an integer value.
///         - Blank characters are skipped
///         - hexa values must begin with '0x' or '0X'
///         - '-' sign is handled
///         - returns after one value has been parsed or null character
///           or CR character has been found.
/// @param  p pointer to pointer to the string to be parsed. The pointer
///         to string is modified: at the end of the call it points to
///         the character following the last parsed one.
//======================================================================
int atoitok(u8 **p)
{
    int k = 0;
    u8 base16=0;
    s8 sign = 1;
    while ((**p==' ')||(**p=='\t')||(**p==',')) (*p)++;
    if (**p=='-')
    {
        sign = -1;
        (*p)++;
    }
    if ((**p=='0') && (*(*p+1)=='x'))
    {
        base16 = 1;
        *p += 2;
    }
    while (**p && **p!=' ' && **p!=0xa && **p!=',') {
        if (!base16)
        {
            k = (k<<3)+(k<<1)+(**p)-'0';
        }
        else
        {
            if (**p > '9')
                if (**p > 'F')
                    k = (k<<4)+(10+(**p)-'a');
                else
                    k = (k<<4)+(10+(**p)-'A');
            else
                k = (k<<4)+(**p)-'0';
        }
        (*p)++;
    }
    return sign*k;
}

//======================================================================
// kal_atoi
//----------------------------------------------------------------------
/// @brief  Extracts from a ASCII string a decimal value and
///         convert it to an integer value.
///         '-' and '+' signs are handled
/// @param  s               char*                   string to convert
/// @retval                 s32                     integer value
//======================================================================
s32 kal_atoi(u8* s)
{
    s32 k = 0;
    s8 sign = 1;

    if (*s == '-')
    {
        sign = -1;
        s++;
    }
    else if (*s == '+')
    {
        s++;
    }

    while (*s >= '0' && *s <= '9')
    {
        k = 10 * k + *s - '0';
        s++;
    }

    return (sign * k);
}

//======================================================================
// kal_atoi_float
//----------------------------------------------------------------------
/// @brief  Extracts from a ASCII string a float value and
///         convert it to an integer value * 10^number_of_decimals.
///         '-' and '+' signs are handled
/// @param  s               char*                   string to convert
/// @param  d               u8                      Number of decimals
/// @retval                 s32                     integer value
//======================================================================
s32 kal_atoi_float(u8* s, u8 d)
{
    s32 k_int = 0;
    s32 k_dec = 0;
    s8 sign = 1;
    u8 i;

    // Check sign
    if (*s == '-')
    {
        sign = -1;
        s++;
    }
    else if (*s == '+')
    {
        s++;
    }

    // Calculate integer part
    while (*s >= '0' && *s <= '9')
    {
        k_int = 10 * k_int + *s - '0';
        s++;
    }

    for (i = 0; i < d; i++)
    {
        k_int *= 10;
    }

    // Skip float separator
    if (*s == '.')
    {
        s++;
    }
    else
    {
        return (sign * k_int);
    }

    // Calculate decimal part
    while (*s >= '0' && *s <= '9' && d--)
    {
        k_dec = 10 * k_dec + *s - '0';
        s++;
    }

    // If string is finished but all the decimals aren't there, complete as if they were followed by 0s
    for (i = 0; i < d; i++)
    {
        k_dec *= 10;
    }

    return (sign * (k_int + k_dec));
}
//======================================================================
// kal_atoi_hex
//----------------------------------------------------------------------
/// @brief  Extracts from a ASCII string a hex value and
///         convert it to an integer value.
/// @param  s               char*                   string to convert
/// @retval                 u32                     integer value
//======================================================================
u32 kal_atoi_hex(u8* s)
{
    u32 k = 0;

    while (*s != '\0')
    {
        if (*s > '9')
        {
            k = 16 * k + 10 + (*s) - ((*s > 'F') ? 'a' : 'A');
        }
        else
        {
            k = 16 * k + (*s) - '0';
        }
        s++;
    }

    return (k);
}

//======================================================================
// kal_base64_strlen
//----------------------------------------------------------------------
/// @brief  Real length (excluding padding) of Base64-encoded string
///         Note : the input string always has 4x chars, if not return 0
/// @param  in              char*                   string to decode
/// @retval                 u16                     string length
//======================================================================
u16 kal_base64_strlen(const char* in)
{
    u16 len, j;
    register const char *p;

    // Useful strlen
    len = strlen(in);

    // the input string always has 4x chars
    if (len % 4)
    {
        return 0;
    }

    // remove padding
    for (j = 0, p = in + len - 1; len > 0 && *p == '=' ; j++, p--, len--) {;}

    // there is 0, 1 or 2 padding chars
    if (j > 2)
    {
        return 0;
    }

    return len;
}

//======================================================================
// kal_base64 codec tables
//======================================================================
const char k_kal_base64_encode[] =  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const u8   k_kal_base64_decode[] = {     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
                                         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
                                         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62, 63, 62, 62, 63,
                                        52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0,  0,  0,  0,
                                         0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
                                        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0, 63,
                                         0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
                                        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51     
                                    };

//======================================================================
// kal_base64_encode
//----------------------------------------------------------------------
/// @brief  Encode Base64-encoded buffer. The output buffer is supposed to have enough space
///         Beware, the stream encoding is Big Endian.
/// @param  out             char*                   string result
/// @param  in              u8*                     binary buffer to encode
/// @param  len             u16                     length of the binary buffer in bytes
/// @retval                 void 
//======================================================================
void kal_base64_encode(char *out, u8* in, u16 len)
{
    u16 i = 0;
    register u8 a,b,c;

    if (len > 1)
    {
        for (; i < len - 2; i += 3) 
        {
            a = *in++;
            b = *in++;
            c = *in++;
            *out++ = k_kal_base64_encode[(((a >> 2) & 0x3F))];
            *out++ = k_kal_base64_encode[(((a << 4) & 0x30) | ((b >> 4) & 0x0F))];
            *out++ = k_kal_base64_encode[(((b << 2) & 0x3C) | ((c >> 6) & 0x03))];
            *out++ = k_kal_base64_encode[(((c >> 0) & 0x3F))];
        }
    }

    if (i < len) 
    {
        a = *in++;
        *out++ = k_kal_base64_encode[(((a >> 2) & 0x3F))];

        if (i == (len - 1)) 
        {
            *out++ = k_kal_base64_encode[(((a << 4) & 0x30))];
            *out++ = '=';
        }
        else 
        {
            b = *in++;
            *out++ = k_kal_base64_encode[(((a << 4) & 0x30) | ((b >> 4) & 0x0F))];
            *out++ = k_kal_base64_encode[(((b << 2) & 0x3C))];
        }

        *out++ = '=';
    }

    *out++ = '\0';
    return;
}

//======================================================================
// kal_base64_decode
//----------------------------------------------------------------------
/// @brief  Decode Base64-encoded buffer. The output buffer is supposed to have enough space
/// @param  out             u8*                     binary buffer result
/// @param  in              char*                   string to decode
/// @param  len             u16                     string length, excluding padding
/// @retval                 void 
//======================================================================
void kal_base64_decode(u8 *out, const char* in, u16 len)
{
    register u8 a,b,c,d;

    // Decode
    for (; len > 3; len -= 4)
    {
        a = k_kal_base64_decode[(u8)*in++];
        b = k_kal_base64_decode[(u8)*in++];
        c = k_kal_base64_decode[(u8)*in++];
        d = k_kal_base64_decode[(u8)*in++];
        
        *out++ = (a << 2) | (b >> 4);
        *out++ = (b << 4) | (c >> 2);
        *out++ = (c << 6) | (d >> 0);
    }

    // Note len == 1 is not possible 
    if (len > 1)
    {
        a = k_kal_base64_decode[(u8)*in++];
        b = k_kal_base64_decode[(u8)*in++];
        *out++ = (a << 2) | (b >> 4);
    }
    if (len > 2)
    {
        c = k_kal_base64_decode[(u8)*in++];
        *out++ = (b << 4) | (c >> 2);
    }
}

//======================================================================
// kal_tolower
//----------------------------------------------------------------------
/// @brief  Changes inplace to lower character a non-constant string
/// @param  s               u8*                     String to transform
/// @retval                 void
//======================================================================
void kal_tolower(u8* s)
{
    while (*s != '\0')
    {
        if (*s >= 'A' && *s <= 'Z')
        {
            *s += (u8)('a' - 'A');
        }
        s++;
    }
}


//======================================================================
// kal_toupper
//----------------------------------------------------------------------
/// @brief  Changes inplace to upper character a non-constant string
/// @param  s               u8*                     String to transform
/// @retval                 void
//======================================================================
void kal_toupper(u8* s)
{
    while (*s != '\0')
    {
        if (*s >= 'a' && *s <= 'z')
        {
            *s -= (u8)('a' - 'A');
        }
        s++;
    }
}

//======================================================================
// kal_crc8
//----------------------------------------------------------------------
/// @brief  Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial.
///         A table-based algorithm would be faster, but for only a few
///         bytes it isn't worth the code size.
/// @param  in          u8*                     input buffer
/// @param  len         u32                     input buffer length in bytes
/// @retval             void
//======================================================================
/* Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial.  A
 * table-based algorithm would be faster, but for only a few bytes it isn't
 * worth the code size. */
u8 kal_crc8(u8* in, u32 len) 
{
    const u8 *data = in;
    u32 crc = 0;
    u8 i;
    u32 j;

    for (j = len; j; j--, data++)
    {
        crc ^= (*data << 8);
        for(i = 8; i; i--)
        {
            if (crc & 0x8000)
            {
                crc ^= (0x1070 << 3);
            }
            crc <<= 1;
        }
    }
    return (u8)(crc >> 8);
}

//======================================================================
// kal_ctf_encode
//----------------------------------------------------------------------
/// @brief  Compress u32 to D7A Compressed Time format (CTF).
///         The ceil flag is used to define rounding, so that 
///         kal_ctf_decode(kal_ctf_encode(val, FALSE) <= val (floor)
///         kal_ctf_decode(kal_ctf_encode(val, TRUE)  >= val (ceiling)
/// @param  val         u32                     value to encode
/// @param  ceil        u8                      ceil value when TRUE
/// @retval             kal_ctf_t               compressed value 
//======================================================================
kal_ctf_t kal_ctf_encode(u32 val, u8 ceil)
{
    u8 exp;
    u32 tmp = val;
    kal_ctf_t ctf;
    
    // serch best (smallest) exponent
    for (exp = 0; tmp > 31; exp++, tmp >>= 2) {};
    
    if (exp < 8)
    {
        ctf.bf.exp = exp;
        ctf.bf.mant = tmp;

        // manage floor
        if (ceil)
        {
            if (kal_ctf_decode(ctf) < val)
            { 
                if (ctf.bf.mant < 31)
                {
                    ctf.bf.mant++;
                }
                else
                {
                    if (ctf.bf.exp < 7)
                    {
                        // mant = 31+1 = 4*8
                        ctf.bf.exp++;
                        ctf.bf.mant = 8;
                    }
                    else
                    {
                        // exp = 7+1 -> overflow
                        ctf.byte = 0xff;
                    }
                }
            }
        }
    }
    else
    {
        ctf.byte = 0xff;
    }

    return ctf;
}

// =======================================================================
// kal_ctf_decode
// -----------------------------------------------------------------------
/// @brief  Decompress from Compressed Time Format to u32
/// @param  ctf         kal_ctf_t           compressed value in CTF
/// @retval             u32                 decode result
// =======================================================================
u32 kal_ctf_decode(kal_ctf_t ctf)
{
    return ((1 << (2*ctf.bf.exp)) * ctf.bf.mant);
}