Fix decoding into a std::byte

Decoding into a std::byte output iterator was not working because the
`*out++ = val` assignment doesn't work when the output is std::byte and
val is a char/unsigned char/uint8_t.  Instead we need to explicitly
cast, but figuring out what we have to cast to is a little bit tricky.

This PR makes it work (and bumps the version for this and the is_hex
fix).
This commit is contained in:
Jason Rhinelander 2020-12-14 03:18:58 -04:00
parent 1959f8747d
commit bd9313bf19
6 changed files with 78 additions and 4 deletions

View File

@ -9,7 +9,7 @@ include(GNUInstallDirs)
set(LOKIMQ_VERSION_MAJOR 1)
set(LOKIMQ_VERSION_MINOR 2)
set(LOKIMQ_VERSION_PATCH 2)
set(LOKIMQ_VERSION_PATCH 3)
set(LOKIMQ_VERSION "${LOKIMQ_VERSION_MAJOR}.${LOKIMQ_VERSION_MINOR}.${LOKIMQ_VERSION_PATCH}")
message(STATUS "lokimq v${LOKIMQ_VERSION}")

View File

@ -32,6 +32,7 @@
#include <array>
#include <iterator>
#include <cassert>
#include "byte_type.h"
namespace lokimq {
@ -153,7 +154,8 @@ void from_base32z(InputIt begin, InputIt end, OutputIt out) {
curr = curr << 5 | detail::b32z_lut.from_b32z(static_cast<unsigned char>(*begin++));
if (bits >= 3) {
bits -= 3; // Added 5, removing 8
*out++ = static_cast<uint8_t>(curr >> bits);
*out++ = static_cast<detail::byte_type_t<OutputIt>>(
static_cast<uint8_t>(curr >> bits));
curr &= (1 << bits) - 1;
} else {
bits += 5;

View File

@ -32,6 +32,7 @@
#include <array>
#include <iterator>
#include <cassert>
#include "byte_type.h"
namespace lokimq {
@ -190,7 +191,8 @@ void from_base64(InputIt begin, InputIt end, OutputIt out) {
bits = 6;
else {
bits -= 2; // Added 6, removing 8
*out++ = static_cast<uint8_t>(curr >> bits);
*out++ = static_cast<detail::byte_type_t<OutputIt>>(
static_cast<uint8_t>(curr >> bits));
curr &= (1 << bits) - 1;
}
}

28
lokimq/byte_type.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
// Specializations for assigning from a char into an output iterator, used by hex/base32z/base64
// decoding to bytes.
#include <iterator>
#include <type_traits>
namespace lokimq::detail {
// Fallback - we just try a char
template <typename OutputIt, typename = void>
struct byte_type { using type = char; };
// Support for things like std::back_inserter:
template <typename OutputIt>
struct byte_type<OutputIt, std::void_t<typename OutputIt::container_type>> {
using type = typename OutputIt::container_type::value_type; };
// iterator, raw pointers:
template <typename OutputIt>
struct byte_type<OutputIt, std::enable_if_t<std::is_reference_v<typename std::iterator_traits<OutputIt>::reference>>> {
using type = std::remove_reference_t<typename std::iterator_traits<OutputIt>::reference>; };
template <typename OutputIt>
using byte_type_t = typename byte_type<OutputIt>::type;
}

View File

@ -32,6 +32,7 @@
#include <array>
#include <iterator>
#include <cassert>
#include "byte_type.h"
namespace lokimq {
@ -139,7 +140,8 @@ void from_hex(InputIt begin, InputIt end, OutputIt out) {
while (begin != end) {
auto a = *begin++;
auto b = *begin++;
*out++ = from_hex_pair(static_cast<unsigned char>(a), static_cast<unsigned char>(b));
*out++ = static_cast<detail::byte_type_t<OutputIt>>(
from_hex_pair(static_cast<unsigned char>(a), static_cast<unsigned char>(b)));
}
}

View File

@ -2,6 +2,7 @@
#include "lokimq/base32z.h"
#include "lokimq/base64.h"
#include "common.h"
#include <iterator>
using namespace std::literals;
@ -198,3 +199,42 @@ TEST_CASE("base64 encoding/decoding", "[encoding][decoding][base64]") {
REQUIRE( lokimq::is_base64(b64_bytes) );
REQUIRE( lokimq::from_base64(b64_bytes) == "\xff\x00"sv );
}
TEST_CASE("std::byte decoding", "[decoding][hex][base32z][base64]") {
// Decoding to std::byte is a little trickier because you can't assign to a byte without an
// explicit cast, which means we have to properly detect that output is going to a std::byte
// output.
// hex
auto b_in = "ff42"s;
std::vector<std::byte> b_out;
lokimq::from_hex(b_in.begin(), b_in.end(), std::back_inserter(b_out));
REQUIRE( b_out == std::vector{std::byte{0xff}, std::byte{0x42}} );
b_out.emplace_back();
lokimq::from_hex(b_in.begin(), b_in.end(), b_out.begin() + 1);
REQUIRE( b_out == std::vector{std::byte{0xff}, std::byte{0xff}, std::byte{0x42}} );
lokimq::from_hex(b_in.begin(), b_in.end(), b_out.data());
REQUIRE( b_out == std::vector{std::byte{0xff}, std::byte{0x42}, std::byte{0x42}} );
// base32z
b_in = "yojky"s;
b_out.clear();
lokimq::from_base32z(b_in.begin(), b_in.end(), std::back_inserter(b_out));
REQUIRE( b_out == std::vector{std::byte{0x04}, std::byte{0x12}, std::byte{0xa0}} );
b_out.emplace_back();
lokimq::from_base32z(b_in.begin(), b_in.end(), b_out.begin() + 1);
REQUIRE( b_out == std::vector{std::byte{0x04}, std::byte{0x04}, std::byte{0x12}, std::byte{0xa0}} );
lokimq::from_base32z(b_in.begin(), b_in.end(), b_out.data());
REQUIRE( b_out == std::vector{std::byte{0x04}, std::byte{0x12}, std::byte{0xa0}, std::byte{0xa0}} );
// base64
b_in = "yojk"s;
b_out.clear();
lokimq::from_base64(b_in.begin(), b_in.end(), std::back_inserter(b_out));
REQUIRE( b_out == std::vector{std::byte{0xca}, std::byte{0x88}, std::byte{0xe4}} );
b_out.emplace_back();
lokimq::from_base64(b_in.begin(), b_in.end(), b_out.begin() + 1);
REQUIRE( b_out == std::vector{std::byte{0xca}, std::byte{0xca}, std::byte{0x88}, std::byte{0xe4}} );
lokimq::from_base64(b_in.begin(), b_in.end(), b_out.data());
REQUIRE( b_out == std::vector{std::byte{0xca}, std::byte{0x88}, std::byte{0xe4}, std::byte{0xe4}} );
}