1/*
2 * Copyright 2020, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#pragma once
14
15#include <stdio.h>
16#include <stdint.h>
17#include <stddef.h>
18#include <utils/arith.h>
19
20/* A streaming base64 encoder */
21
22typedef struct {
23    FILE *output;
24    uint16_t buffer;
25    size_t bits;
26} base64_t;
27
28#define BASE64_LOOKUP (\
29    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
30    "abcdefghijklmnopqrstuvwxyz" \
31    "0123456789+/" \
32)
33
34/* Create a new base64 streamer that streams to the given output */
35static inline base64_t base64_new(FILE *output)
36{
37    return (base64_t) {
38        .output = output,
39        .buffer = 0,
40        .bits = 0,
41    };
42}
43
44/* Lookup the character for a given bit pattern */
45static inline uint8_t base64_lookup(uint8_t bit)
46{
47    return BASE64_LOOKUP[bit & MASK(6)];
48}
49
50/* Write a byte to a base64 stream */
51static inline int base64_putbyte(base64_t *streamer, uint8_t byte)
52{
53    /* Buffer the byte */
54    streamer->buffer <<= 8;
55    streamer->buffer |= byte;
56    streamer->bits += 8;
57
58    /* Write any bits to the output */
59    while (streamer->bits >= 6) {
60        streamer->bits -= 6;
61        uint8_t part = streamer->buffer >> streamer->bits;
62        fputc(base64_lookup(part), streamer->output);
63    }
64
65    return 0;
66}
67
68/* Write any remaining data to the output */
69static inline int base64_terminate(base64_t *streamer)
70{
71    if (streamer->bits > 0) {
72        size_t padding = 6 - streamer->bits;
73        streamer->buffer <<= padding;
74        fputc(base64_lookup(streamer->buffer), streamer->output);
75        while (padding > 0) {
76            fputc('=', streamer->output);
77            padding -= 2;
78        }
79    }
80
81    /* Reset the streamer */
82    streamer->bits = 0;
83
84    return 0;
85}
86