/* This file is part of limb https://lila.oss/limb
* Copyright (C) 2023 Olivier Brunel jjk@jjacky.com */
/* SPDX-License-Identifier: GPL-2.0-only */
#include <errno.h>
#include <limb/base.h>
#include <limb/bytestr.h>
#include <limb/u64.h>
ssize_t
base_scan(char *dst, int base, const char *data, size_t dlen, const char *alpha, int strict)
{
size_t bits, bin, bout;
ssize_t w = 0;
switch (base) {
case 2: bits = 1; bin = 8; bout = 1; break;
case 4: bits = 2; bin = 4; bout = 1; break;
case 8: bits = 3; bin = 8; bout = 3; break;
case 16: bits = 4; bin = 2; bout = 1; break;
case 32: bits = 5; bin = 8; bout = 5; break;
case 64: bits = 6; bin = 4; bout = 3; break;
case 128: bits = 7; bin = 8; bout = 7; break;
default: return (errno = ERANGE, -1);
}
if (strict && dlen % bin)
return (errno = EINVAL, -1);
while (dlen) {
size_t l = (dlen > bin) ? bin : dlen;
size_t e = (size_t) -1;
u64 u = 0;
for (size_t i = 0; i < l; ++i) {
int n = byte_chr(alpha, base + 1, data[i]);
if (n == base + 1)
return (errno = EINVAL, -1);
if (e == (size_t) -1) {
if (n < base)
u |= (u64) n << (64 - bits * (i + 1));
else /* n == base, i.e. padding */
e = i;
} else if (n < base) {
/* got something after padding */
return (errno = EINVAL, -1);
}
}
u64p_be(&u);
size_t n = bout;
/* was there padding? */
if (e != (size_t) -1) {
/* if strict, check none of the unused bits were set */
if (strict && (byte_chr(alpha, base + 1, data[e - 1])
& ((1 << ((bits * e) % 8)) - 1)))
return (errno = EINVAL, -1);
/* padding means writing less than bout bytes */
n = bout * e / bin;
}
/* less than bin bytes in /requires/ padding */
if (l < bin && e == (size_t) -1)
return (errno = EINVAL, -1);
if (dst) {
memcpy(dst, &u, n);
dst += n;
}
w += n;
data += l;
dlen -= l;
}
return w;
}