This repository has been archived on 2022-01-16. You can view files and clone it, but cannot push or open issues or pull requests.
corelibs/src/encoding/baseven/baseven.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;
}