Welcome to little lamb

Code » limb » master » tree

[master] / src / liblimb / base.h / base_scan.c

/* 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;
}