From c63b1a4591a0a54d3ccd449573f408739ea29af2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20Smr=C5=BE?= <roman.smrz@seznam.cz>
Date: Sun, 31 Jan 2021 21:13:19 +0100
Subject: Storage: hexadecimal encoding of binary record items

---
 src/base64.h    | 107 --------------------------------------------------------
 src/storage.cpp |  21 ++++++++---
 2 files changed, 16 insertions(+), 112 deletions(-)
 delete mode 100644 src/base64.h

diff --git a/src/base64.h b/src/base64.h
deleted file mode 100644
index 324a5dd..0000000
--- a/src/base64.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-namespace { namespace base64 {
-
-	const static char encodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-	const static char padCharacter = '=';
-
-	std::string encode(const std::vector<uint8_t> & input)
-	{
-		std::string encoded;
-		encoded.reserve(((input.size()/3) + (input.size() % 3 > 0)) * 4);
-		uint32_t temp;
-		auto cursor = input.begin();
-		for (size_t i = 0; i < input.size() / 3; i++)
-		{
-			temp  = (*cursor++) << 16; // Convert to big endian
-			temp += (*cursor++) << 8;
-			temp += (*cursor++);
-			encoded.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]);
-			encoded.append(1, encodeLookup[(temp & 0x0003F000) >> 12]);
-			encoded.append(1, encodeLookup[(temp & 0x00000FC0) >> 6 ]);
-			encoded.append(1, encodeLookup[(temp & 0x0000003F)      ]);
-		}
-		switch (input.size() % 3)
-		{
-		case 1:
-			temp  = (*cursor++) << 16; // Convert to big endian
-			encoded.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]);
-			encoded.append(1, encodeLookup[(temp & 0x0003F000) >> 12]);
-			encoded.append(2, padCharacter);
-			break;
-		case 2:
-			temp  = (*cursor++) << 16; // Convert to big endian
-			temp += (*cursor++) << 8;
-			encoded.append(1, encodeLookup[(temp & 0x00FC0000) >> 18]);
-			encoded.append(1, encodeLookup[(temp & 0x0003F000) >> 12]);
-			encoded.append(1, encodeLookup[(temp & 0x00000FC0) >> 6 ]);
-			encoded.append(1, padCharacter);
-			break;
-		}
-		return encoded;
-	}
-
-	std::vector<uint8_t> decode(const std::string & input)
-	{
-		if (input.length() % 4) // Sanity check
-			throw std::runtime_error("Non-Valid base64!");
-
-		size_t padding = 0;
-		if (input.length()) {
-			if (input[input.length() - 1] == padCharacter)
-				padding++;
-			if (input[input.length() - 2] == padCharacter)
-				padding++;
-		}
-
-		// Setup a vector to hold the result
-		std::vector<uint8_t> decoded;
-		decoded.reserve(((input.length()/4)*3) - padding);
-		uint32_t temp = 0; // Holds decoded quanta
-		auto cursor = input.begin();
-		while (cursor < input.end())
-		{
-			for (size_t quantumPosition = 0; quantumPosition < 4; quantumPosition++)
-			{
-				temp <<= 6;
-				if      (*cursor >= 0x41 && *cursor <= 0x5A)   // This area will need tweaking if
-					temp |= *cursor - 0x41;                // you are using an alternate alphabet
-				else if (*cursor >= 0x61 && *cursor <= 0x7A)
-					temp |= *cursor - 0x47;
-				else if (*cursor >= 0x30 && *cursor <= 0x39)
-					temp |= *cursor + 0x04;
-				else if (*cursor == 0x2B)
-					temp |= 0x3E; // change to 0x2D for URL alphabet
-				else if (*cursor == 0x2F)
-					temp |= 0x3F; // change to 0x5F for URL alphabet
-				else if (*cursor == padCharacter) // pad
-				{
-					switch (input.end() - cursor)
-					{
-					case 1: //One pad character
-						decoded.push_back((temp >> 16) & 0x000000FF);
-						decoded.push_back((temp >> 8 ) & 0x000000FF);
-						return decoded;
-					case 2: //Two pad characters
-						decoded.push_back((temp >> 10) & 0x000000FF);
-						return decoded;
-					default:
-						throw std::runtime_error("Invalid Padding in Base 64!");
-					}
-				}  else
-					throw std::runtime_error("Non-Valid Character in Base 64!");
-				cursor++;
-			}
-			decoded.push_back((temp >> 16) & 0x000000FF);
-			decoded.push_back((temp >> 8 ) & 0x000000FF);
-			decoded.push_back((temp      ) & 0x000000FF);
-		}
-		return decoded;
-	}
-
-} }
diff --git a/src/storage.cpp b/src/storage.cpp
index 6e2f118..4cfa08d 100644
--- a/src/storage.cpp
+++ b/src/storage.cpp
@@ -1,5 +1,4 @@
 #include "storage.h"
-#include "base64.h"
 
 #include <charconv>
 #include <chrono>
@@ -1018,9 +1017,17 @@ optional<RecordT<S>> RecordT<S>::decode(const S & st,
 			}
 		else if (type == "t")
 			items->emplace_back(name, value);
-		else if (type == "b")
-			items->emplace_back(name, base64::decode(value));
-		else if (type == "d")
+		else if (type == "b") {
+			if (value.size() % 2)
+				return nullopt;
+			vector<uint8_t> binary(value.size() / 2, 0);
+
+			for (size_t i = 0; i < binary.size(); i++)
+				std::from_chars(value.data() + 2 * i,
+						value.data() + 2 * i + 2,
+						binary[i], 16);
+			items->emplace_back(name, std::move(binary));
+		} else if (type == "d")
 			items->emplace_back(name, ZonedTime(value));
 		else if (type == "u")
 			items->emplace_back(name, UUID(value));
@@ -1100,7 +1107,11 @@ vector<uint8_t> RecordT<S>::encodeInner() const
 			value = *x;
 		} else if (auto x = item.asBinary()) {
 			type = "b";
-			value = base64::encode(*x);
+			value.resize(x->size() * 2, '0');
+			for (size_t i = 0; i < x->size(); i++)
+				std::to_chars(value.data() + 2 * i + ((*x)[i] < 0x10),
+						value.data() + 2 * i + 2,
+						(*x)[i], 16);
 		} else if (auto x = item.asDate()) {
 			type = "d";
 			value = string(*x);
-- 
cgit v1.2.3