156 lines
4.4 KiB
C
156 lines
4.4 KiB
C
/*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "encoding/baseven/baseven.h"
|
|
|
|
#include <assert.h> // assert()
|
|
#include <inttypes.h>
|
|
#include <stdbool.h> // bool
|
|
#include <stdio.h> // putchar()
|
|
#include <stdlib.h> // 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;
|
|
}
|