Exportable version of WizziLab's modem driver.
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); }