aureooms/js-codec-base64

View on GitHub
src/_decode.js

Summary

Maintainability
D
1 day
Test Coverage
import {ValueError} from '@failure-abstraction/error';
import {iter} from '@iterable-iterator/iter';
import {next, StopIteration} from '@iterable-iterator/next';

import Base64DecodeError from './Base64DecodeError.js';
import DEFAULT_OPTIONS from './DEFAULT_OPTIONS.js';
import byte1tochar2 from './byte1tochar2.js';
import byte2tochar3 from './byte2tochar3.js';
import byte3tochar4 from './byte3tochar4.js';
import variants from './variants.js';

export default function* _decode(bytes, options = DEFAULT_OPTIONS) {
    if (options.variant) {
        if (Object.hasOwn(variants, options.variant)) {
            options = variants[options.variant];
        } else {
            throw new ValueError(`unknown Base64 variant ${options.variant}`);
        }
    }

    let start = 0;

    const {alphabet, padding} = options;

    const it = iter(bytes);

    while (true) {
        let a;
        let b;
        let c;

        try {
            a = next(it);
        } catch (error) {
            if (error instanceof StopIteration) {
                break;
            } else {
                throw error;
            }
        }

        if (a < 0x00 || a > 0xff) {
            const reason = `byte out of range 0x00 <= ${a} <= 0xFF`;
            const position = {start, end: start + 1};
            throw new Base64DecodeError(reason, bytes, position);
        }

        ++start;

        try {
            b = next(it);
        } catch (error) {
            if (error instanceof StopIteration) {
                yield* byte1tochar2(alphabet, a);
                if (padding) {
                    yield padding;
                    yield padding;
                }

                break;
            } else {
                throw error;
            }
        }

        if (b < 0x00 || b > 0xff) {
            const reason = `byte out of range 0x00 <= ${b} <= 0xFF`;
            const position = {start, end: start + 1};
            throw new Base64DecodeError(reason, bytes, position);
        }

        ++start;

        try {
            c = next(it);
        } catch (error) {
            if (error instanceof StopIteration) {
                yield* byte2tochar3(alphabet, a, b);
                if (padding) {
                    yield padding;
                }

                break;
            } else {
                throw error;
            }
        }

        if (c < 0x00 || c > 0xff) {
            const reason = `byte out of range 0x00 <= ${c} <= 0xFF`;
            const position = {start, end: start + 1};
            throw new Base64DecodeError(reason, bytes, position);
        }

        yield* byte3tochar4(alphabet, a, b, c);

        ++start;
    }
}