This seems to be generically useful by itself. I'd probably be OK with it, if it has a suitable (non device-related) home.
On Mon, Jan 15, 2024 at 2:46 AM Sebastian Huber <sebastian.hu...@embedded-brains.de> wrote: > > From: Matthew Joyce <matthew.jo...@embedded-brains.de> > > --- > cpukit/dev/iobase64decode.c | 166 ++++++++++++++++ > cpukit/include/rtems/dev/io.h | 63 ++++++ > spec/build/cpukit/librtemscpu.yml | 1 + > .../build/testsuites/unit/unit-no-clock-0.yml | 1 + > testsuites/unit/tc-io-base64-decode.c | 180 ++++++++++++++++++ > 5 files changed, 411 insertions(+) > create mode 100644 cpukit/dev/iobase64decode.c > create mode 100644 testsuites/unit/tc-io-base64-decode.c > > diff --git a/cpukit/dev/iobase64decode.c b/cpukit/dev/iobase64decode.c > new file mode 100644 > index 0000000000..5d3fe1c3cb > --- /dev/null > +++ b/cpukit/dev/iobase64decode.c > @@ -0,0 +1,166 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > + > +/** > + * @file > + * > + * @ingroup RTEMSDeviceIO > + * > + * @brief This source file contains the implementation of > + * _IO_Base64_decode_initialize() and _IO_Base64_decode(). > + */ > + > +/* > + * Copyright (C) 2023 embedded brains GmbH & Co. KG > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS > IS" > + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE > + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR > + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS > + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN > + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) > + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE > + * POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#include <rtems/dev/io.h> > + > +#define SPACE 253 > + > +#define PAD 254 > + > +#define INVALID 255 > + > +const uint8_t _IO_Base64_decode_table[128] = { > + ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, > + ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7, > + ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, > + ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, > + ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, > + ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, > + ['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, > + ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, > + ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35, > + ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, > + ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, > + ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, > + ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, > + ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, > + ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, > + ['8'] = 60, ['9'] = 61, ['+'] = 62, ['-'] = 62, > + ['/'] = 63, ['_'] = 63, ['='] = PAD, [' '] = SPACE, > + ['\t'] = SPACE, ['\n'] = SPACE, ['\v'] = SPACE, ['\f'] = SPACE, > + ['\r'] = SPACE, [0] = INVALID, [1] = INVALID, [2] = INVALID, > + [3] = INVALID, [4] = INVALID, [5] = INVALID, [6] = INVALID, > + [7] = INVALID, [8] = INVALID, [14] = INVALID, [15] = INVALID, > + [16] = INVALID, [17] = INVALID, [18] = INVALID, [19] = INVALID, > + [20] = INVALID, [21] = INVALID, [22] = INVALID, [23] = INVALID, > + [24] = INVALID, [25] = INVALID, [26] = INVALID, [27] = INVALID, > + [28] = INVALID, [29] = INVALID, [30] = INVALID, [31] = INVALID, > + [33] = INVALID, [34] = INVALID, [35] = INVALID, [36] = INVALID, > + [37] = INVALID, [38] = INVALID, [39] = INVALID, [40] = INVALID, > + [41] = INVALID, [42] = INVALID, [44] = INVALID, [46] = INVALID, > + [58] = INVALID, [59] = INVALID, [60] = INVALID, [62] = INVALID, > + [63] = INVALID, [64] = INVALID, [91] = INVALID, [92] = INVALID, > + [93] = INVALID, [94] = INVALID, [96] = INVALID, [123] = INVALID, > + [124] = INVALID, [125] = INVALID, [126] = INVALID, [127] = INVALID}; > + > +void _IO_Base64_decode_initialize(IO_Base64_decode_control* self, > + uint8_t* target, > + size_t target_size) { > + self->state = IO_BASE64_DECODE_STATE_0; > + self->target = target; > + self->target_end = target + target_size; > +} > + > +IO_Base64_decode_status _IO_Base64_decode(IO_Base64_decode_control* self, > + char ch) { > + uint8_t decoded_ch; > + uint8_t next_ch; > + uint8_t* target; > + const uint8_t* target_end; > + IO_Base64_decode_state next_state; > + > + if ((unsigned char)ch >= 128) { > + return IO_BASE64_DECODE_INVALID_INPUT; > + } > + > + decoded_ch = _IO_Base64_decode_table[(unsigned char)ch]; > + > + if (decoded_ch == SPACE) { > + return IO_BASE64_DECODE_SUCCESS; > + } > + > + target = self->target; > + > + if (decoded_ch == PAD) { > + self->target_end = target; > + return IO_BASE64_DECODE_SUCCESS; > + } > + > + if (decoded_ch == INVALID) { > + return IO_BASE64_DECODE_INVALID_INPUT; > + } > + > + target_end = self->target_end; > + > + if (target == target_end) { > + return IO_BASE64_DECODE_OVERFLOW; > + } > + > + switch (self->state) { > + case IO_BASE64_DECODE_STATE_0: > + *target = decoded_ch << 2; > + next_state = IO_BASE64_DECODE_STATE_1; > + break; > + > + case IO_BASE64_DECODE_STATE_1: > + *target |= decoded_ch >> 4; > + next_ch = (decoded_ch & 0x0fU) << 4; > + ++target; > + > + if (target != target_end) { > + *target = next_ch; > + } else if (next_ch != 0) { > + return IO_BASE64_DECODE_OVERFLOW; > + } > + > + next_state = IO_BASE64_DECODE_STATE_2; > + break; > + > + case IO_BASE64_DECODE_STATE_2: > + *target |= decoded_ch >> 2; > + next_ch = (decoded_ch & 0x03U) << 6; > + ++target; > + > + if (target != target_end) { > + *target = next_ch; > + } else if (next_ch != 0) { > + return IO_BASE64_DECODE_OVERFLOW; > + } > + > + next_state = IO_BASE64_DECODE_STATE_3; > + break; > + > + default: /* IO_BASE64_DECODE_STATE_3 */ > + *target |= decoded_ch; > + ++target; > + next_state = IO_BASE64_DECODE_STATE_0; > + break; > + } > + > + self->state = next_state; > + self->target = target; > + return IO_BASE64_DECODE_SUCCESS; > +} > diff --git a/cpukit/include/rtems/dev/io.h b/cpukit/include/rtems/dev/io.h > index efe0a32d0a..9b724e5df8 100644 > --- a/cpukit/include/rtems/dev/io.h > +++ b/cpukit/include/rtems/dev/io.h > @@ -178,6 +178,69 @@ int _IO_Base64url( > int wordlen > ); > > +/** > + * @brief Represents the base64 decoder state. > + */ > +typedef enum { > + IO_BASE64_DECODE_STATE_0, > + IO_BASE64_DECODE_STATE_1, > + IO_BASE64_DECODE_STATE_2, > + IO_BASE64_DECODE_STATE_3 > +} IO_Base64_decode_state; > + > +/** > + * @brief Contains the base64 decoder control. > + */ > +typedef struct { > + IO_Base64_decode_state state; > + uint8_t *target; > + const uint8_t *target_end; > +} IO_Base64_decode_control; > + > +/** > + * @brief Maps a 7-bit character to the 6-bit integer as defined by the > base64 > + * or base64url encoding or a special value. > + */ > +extern const uint8_t _IO_Base64_decode_table[ 128 ]; > + > +/** > + * @brief Initializes the base64 decoder. > + * > + * @param[out] self is the base64 decoder control to initialize. > + * > + * @param[out] target is the base address of the target area for decoding. > + * > + * @param target_size is the size in bytes of the target area for decoding. > + */ > +void _IO_Base64_decode_initialize( > + IO_Base64_decode_control *self, > + uint8_t *target, > + size_t target_size > +); > + > +/** > + * @brief Represents the base64 decoder status. > + */ > +typedef enum { > + IO_BASE64_DECODE_SUCCESS, > + IO_BASE64_DECODE_OVERFLOW, > + IO_BASE64_DECODE_INVALID_INPUT > +} IO_Base64_decode_status; > + > +/** > + * @brief Decodes the character. > + * > + * The decoder accepts base64 and base64url encodings. White space is > ignored. > + * > + * @param[in, out] self is the base64 decoder control. > + * > + * @param ch is the character to decode. > + */ > +IO_Base64_decode_status _IO_Base64_decode( > + IO_Base64_decode_control *self, > + char ch > +); > + > /** > * @brief Issues a couple of no-operation instructions. > * > diff --git a/spec/build/cpukit/librtemscpu.yml > b/spec/build/cpukit/librtemscpu.yml > index 9202c31715..56e5243673 100644 > --- a/spec/build/cpukit/librtemscpu.yml > +++ b/spec/build/cpukit/librtemscpu.yml > @@ -540,6 +540,7 @@ source: > - cpukit/dev/i2c/ti-tmp112.c > - cpukit/dev/i2c/xilinx-axi-i2c.c > - cpukit/dev/iobase64.c > +- cpukit/dev/iobase64decode.c > - cpukit/dev/ioprintf.c > - cpukit/dev/iorelax.c > - cpukit/dev/iovprintf.c > diff --git a/spec/build/testsuites/unit/unit-no-clock-0.yml > b/spec/build/testsuites/unit/unit-no-clock-0.yml > index 5d15bf52b7..be5cafe990 100644 > --- a/spec/build/testsuites/unit/unit-no-clock-0.yml > +++ b/spec/build/testsuites/unit/unit-no-clock-0.yml > @@ -13,6 +13,7 @@ links: [] > source: > - testsuites/unit/tc-compiler-builtins.c > - testsuites/unit/tc-config.c > +- testsuites/unit/tc-io-base64-decode.c > - testsuites/unit/tc-misaligned-builtin-memcpy.c > - testsuites/unit/tc-score-msgq.c > - testsuites/unit/tc-score-rbtree.c > diff --git a/testsuites/unit/tc-io-base64-decode.c > b/testsuites/unit/tc-io-base64-decode.c > new file mode 100644 > index 0000000000..b09390960f > --- /dev/null > +++ b/testsuites/unit/tc-io-base64-decode.c > @@ -0,0 +1,180 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > + > +/* > + * Copyright (C) 2023 embedded brains GmbH & Co. KG > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS > IS" > + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE > + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR > + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS > + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN > + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) > + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE > + * POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#include <rtems/dev/io.h> > + > +#include <rtems/test.h> > + > +typedef struct { > + IO_Base64_decode_control base; > + uint8_t buf[64]; > +} test_control; > + > +static int test_run(test_control* self, const char* payload) { > + size_t len = strlen(payload); > + > + for (size_t i = 0; i < len; ++i) { > + int rv = _IO_Base64_decode(&self->base, payload[i]); > + > + if (rv != 0) { > + return rv; > + } > + } > + > + return 0; > +} > + > +static int test_payload(test_control* self, const char* payload) { > + memset(self->buf, 0xff, sizeof(self->buf)); > + _IO_Base64_decode_initialize(&self->base, &self->buf[0], > sizeof(self->buf)); > + return test_run(self, payload); > +} > + > +T_TEST_CASE(IOBase64Decode) { > + int rv; > + test_control instance; > + test_control* self = &instance; > + > + rv = test_payload(self, "POOL"); > + T_eq_int(rv, IO_BASE64_DECODE_SUCCESS); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_0); > + T_eq_ptr(self->base.target, &self->buf[3]); > + const uint8_t expected1[] = {0x3c, 0xe3, 0x8b, 0xff}; > + T_eq_mem(&self->buf[0], expected1, sizeof(expected1)); > + > + rv = test_payload(self, "ABCDEFGH"); > + T_eq_int(rv, IO_BASE64_DECODE_SUCCESS); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_0); > + T_eq_ptr(self->base.target, &self->buf[6]); > + const uint8_t expected2[] = {0x00, 0x10, 0x83, 0x10, 0x51, 0x87, 0xff}; > + T_eq_mem(&self->buf[0], expected2, sizeof(expected2)); > + > + /* Non-base64 character results in an error */ > + rv = test_payload(self, "PO*OL"); > + T_eq_int(rv, IO_BASE64_DECODE_INVALID_INPUT); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_2); > + T_eq_ptr(self->base.target, &self->buf[1]); > + const uint8_t expected3[] = {0x3c}; > + T_eq_mem(&self->buf[0], expected3, sizeof(expected3)); > + > + /* Other non-base64 character results in an error */ > + rv = test_payload(self, "PO\x80OL"); > + T_eq_int(rv, IO_BASE64_DECODE_INVALID_INPUT); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_2); > + T_eq_ptr(self->base.target, &self->buf[1]); > + T_eq_mem(&self->buf[0], expected3, sizeof(expected3)); > + > + /* Space characters should be ignored */ > + rv = test_payload(self, "P OOL"); > + T_eq_int(rv, IO_BASE64_DECODE_SUCCESS); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_0); > + T_eq_ptr(self->base.target, &self->buf[3]); > + const uint8_t expected4[] = {0x3c, 0xe3, 0x8b, 0xff}; > + T_eq_mem(&self->buf[0], expected4, sizeof(expected4)); > + > + /* Handle pad characters */ > + rv = test_payload(self, "POOL=="); > + T_eq_int(rv, IO_BASE64_DECODE_SUCCESS); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_0); > + T_eq_ptr(self->base.target, &self->buf[3]); > + T_eq_ptr(self->base.target, self->base.target_end); > + const uint8_t expected5[] = {0x3c, 0xe3, 0x8b, 0xff}; > + T_eq_mem(&self->buf[0], expected5, sizeof(expected5)); > + > + /* If characters come after pad characters, an error results */ > + rv = test_payload(self, "POOL==xy"); > + T_eq_int(rv, IO_BASE64_DECODE_OVERFLOW); > + T_eq_int(self->base.state, IO_BASE64_DECODE_SUCCESS); > + T_eq_ptr(self->base.target, &self->buf[3]); > + T_eq_ptr(self->base.target, self->base.target_end); > + const uint8_t expected6[] = {0x3c, 0xe3, 0x8b, 0xff}; > + T_eq_mem(&self->buf[0], expected6, sizeof(expected6)); > + > + rv = test_payload(self, "POOLPOOL"); > + T_eq_int(rv, IO_BASE64_DECODE_SUCCESS); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_0); > + T_eq_ptr(self->base.target, &self->buf[6]); > + const uint8_t expected7[] = {0x3c, 0xe3, 0x8b, 0x3c, 0xe3, 0x8b, 0xff}; > + T_eq_mem(&self->buf[0], expected7, sizeof(expected7)); > + > + /* > + * Test valid payload with series of target sizes. All target sizes > + * less than three are invalid for the given payload and will result > + * in an error. > + */ > + const uint8_t expected9[] = {0x3c, 0xe3, 0x8b, 0xff}; > + > + for (size_t i = 0; i < 4; ++i) { > + memset(&self->buf[0], 0xff, sizeof(self->buf)); > + _IO_Base64_decode_initialize(&self->base, &self->buf[0], i); > + rv = test_run(self, "POOL"); > + > + if (i < 3) { > + T_eq_int(rv, IO_BASE64_DECODE_OVERFLOW); > + T_eq_int(self->base.state, i); > + T_ne_ptr(self->base.target, &self->buf[3]); > + T_ne_mem(&self->buf[0], expected9, sizeof(expected9)); > + } else { > + T_eq_int(rv, IO_BASE64_DECODE_SUCCESS); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_0); > + T_eq_ptr(self->base.target, &self->buf[3]); > + T_eq_mem(&self->buf[0], expected9, sizeof(expected9)); > + } > + } > + > + /* No overflow in state 1 */ > + memset(&self->buf[0], 0xff, sizeof(self->buf)); > + _IO_Base64_decode_initialize(&self->base, &self->buf[0], 1); > + rv = test_run(self, "AA"); > + T_eq_int(rv, IO_BASE64_DECODE_SUCCESS); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_2); > + T_eq_ptr(self->base.target, &self->buf[1]); > + const uint8_t expected10[] = {0x00, 0xff}; > + T_eq_mem(&self->buf[0], expected10, sizeof(expected10)); > + > + /* No overflow in state 2 */ > + memset(&self->buf[0], 0xff, sizeof(self->buf)); > + _IO_Base64_decode_initialize(&self->base, &self->buf[0], 2); > + rv = test_run(self, "AAA"); > + T_eq_int(rv, IO_BASE64_DECODE_SUCCESS); > + T_eq_int(self->base.state, IO_BASE64_DECODE_STATE_3); > + T_eq_ptr(self->base.target, &self->buf[2]); > + const uint8_t expected11[] = {0x00, 0x00, 0xff}; > + T_eq_mem(&self->buf[0], expected11, sizeof(expected11)); > +} > + > +T_TEST_CASE(IOBase64DecodeInitialize) { > + IO_Base64_decode_control instance; > + IO_Base64_decode_control* self = &instance; > + uint8_t buf[1]; > + > + memset(self, 0xff, sizeof(*self)); > + _IO_Base64_decode_initialize(self, buf, sizeof(buf)); > + T_eq_int(self->state, IO_BASE64_DECODE_STATE_0); > + T_eq_ptr(self->target, &buf[0]); > + T_eq_ptr(self->target_end, &buf[1]); > +} > -- > 2.35.3 > > _______________________________________________ > devel mailing list > devel@rtems.org > http://lists.rtems.org/mailman/listinfo/devel _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel