/* * This file is part of corelibs. (https://git.redxen.eu/corelibs) * Copyright (c) 2021 Alex-David Denes * * corelibs is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * corelibs 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 corelibs. If not, see . */ #include "encoding/baseven/baseven.h" #include // assert() #include #include // bool #include // putchar() #include // malloc() free() #define ENCODED 8 #define DECODED 7 #define RESERVED (ENCODED - DECODED) #define RESERVEDBIT (1 << 7) static cl_error_t corelibs_baseven_encode (const uint8_t *, size_t, uint8_t *, size_t), corelibs_baseven_decode (const uint8_t *, size_t, uint8_t *, size_t); static uintmax_t corelibs_baseven_size_encoded (uintmax_t), corelibs_baseven_size_decoded (uintmax_t); static uint8_t corelibs_baseven_bits_first (uint8_t, uint8_t), corelibs_baseven_bits_last (uint8_t, uint8_t); static bool corelibs_baseven_bits_nth (uint8_t, uint8_t); const struct corelibs_baseven_interface cl_baseven = { .encode = corelibs_baseven_encode, .decode = corelibs_baseven_decode, .size = { .decoded = corelibs_baseven_size_decoded, .encoded = corelibs_baseven_size_encoded, }, }; static cl_error_t corelibs_baseven_encode (const uint8_t *src, size_t len, uint8_t *dest, size_t max) { cl_error_t err = cl_error.err.ok; if (src == NULL || dest == NULL) err = cl_error.err.data.inval; if (corelibs_baseven_size_encoded (len) > max) err = cl_error.err.mem.oob; if (!err) { const uint8_t *const end = src + len; uint8_t bits = 0, last = 0; while (src < end) { const uint8_t rev = RESERVED, keep = DECODED; last |= corelibs_baseven_bits_first (*src, rev) >> (bits + rev); // Save first bit into the last byte to append to byte series *dest = corelibs_baseven_bits_last (*src, keep) | RESERVEDBIT; // Write the kept bytes in the output buffer src++; dest++; bits++; if (bits == DECODED || src == end) { *(dest++) = last | RESERVEDBIT; bits = 0; last = 0; } } } return err; } static cl_error_t corelibs_baseven_decode (const uint8_t *src, size_t len, uint8_t *dest, size_t max) { cl_error_t err = cl_error.err.ok; if (src == NULL || dest == NULL) err = cl_error.err.data.inval; if (corelibs_baseven_size_decoded (len) > max) err = cl_error.err.mem.oob; if (!err) { while (1) { const uint8_t con = ((ENCODED - 1) < len) ? (ENCODED - 1) : (uint8_t) len - 1; // Count of bytes to the extra post-encoding byte as OFFSET (8 -> 7) for (uint8_t p = 0; p < con; p++) { dest[p] = (uint8_t) ((corelibs_baseven_bits_nth (src[con], p + 1) << (ENCODED - 1)) // Get n-th bit from last byte and shift it to the beginning | (src[p] ^ RESERVEDBIT) // Discard reserved bit and merge it with bit from last ); } if ((ENCODED - 1) < len) { dest += con; len -= con; src += ENCODED; } else { break; } } } return err; } static uintmax_t corelibs_baseven_size_encoded (uintmax_t b) { return b + ((b / DECODED) + (bool) (b % DECODED)); } static uintmax_t corelibs_baseven_size_decoded (uintmax_t b) { return b - ((b / ENCODED) + (bool) (b % ENCODED)); } static uint8_t corelibs_baseven_bits_first (const uint8_t byte, const uint8_t cnt) { assert (cnt < ENCODED); const uint8_t fb = (uint8_t) ~0; return byte & ((fb >> cnt) ^ fb); } static uint8_t corelibs_baseven_bits_last (const uint8_t byte, const uint8_t cnt) { assert (cnt < ENCODED); const uint8_t fb = (uint8_t) ~0; return byte & ((fb << cnt) ^ fb); } static bool corelibs_baseven_bits_nth (const uint8_t byte, const uint8_t index) { assert (index < ENCODED); uint8_t off = (ENCODED - 1) - index; /* * 00100000 * * 00000001 << ((8 - 1) - 2) * * 10000000 << (7) * * 00100000 >> (2) * * 00100000 >> ((8 - 1) - 2) * * 00000001 >> (5) */ return (byte & (1 << off)) >> off; }