// SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® module Crafter.Debug.Core:Protocol_impl; import std; import :Value; import :Protocol; using namespace Crafter::Debug; namespace { constexpr std::string_view alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::int32_t Sextet(char c) { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a' + 26; if (c >= '0' && c <= '9') return c - '0' + 52; if (c == '+') return 62; if (c == '/') return 63; return -1; } } Value Crafter::Debug::MakeReply(Value result) { Value reply = Value::MakeObject(); reply.Set("result", std::move(result)); return reply; } Value Crafter::Debug::MakeErrorReply(std::string_view code, std::string_view message) { Value error = Value::MakeObject(); error.Set("code", Value(code)); error.Set("message", Value(message)); Value reply = Value::MakeObject(); reply.Set("error", std::move(error)); return reply; } std::string Crafter::Debug::Base64Encode(std::span bytes) { std::string out; out.reserve((bytes.size() + 2) / 3 * 4); std::size_t i = 0; for (; i + 3 <= bytes.size(); i += 3) { const std::uint32_t chunk = (std::to_integer(bytes[i]) << 16) | (std::to_integer(bytes[i + 1]) << 8) | std::to_integer(bytes[i + 2]); out += alphabet[(chunk >> 18) & 63]; out += alphabet[(chunk >> 12) & 63]; out += alphabet[(chunk >> 6) & 63]; out += alphabet[chunk & 63]; } const std::size_t rest = bytes.size() - i; if (rest == 1) { const std::uint32_t chunk = std::to_integer(bytes[i]) << 16; out += alphabet[(chunk >> 18) & 63]; out += alphabet[(chunk >> 12) & 63]; out += "=="; } else if (rest == 2) { const std::uint32_t chunk = (std::to_integer(bytes[i]) << 16) | (std::to_integer(bytes[i + 1]) << 8); out += alphabet[(chunk >> 18) & 63]; out += alphabet[(chunk >> 12) & 63]; out += alphabet[(chunk >> 6) & 63]; out += '='; } return out; } std::optional> Crafter::Debug::Base64Decode(std::string_view text) { if (text.size() % 4 != 0) return std::nullopt; std::vector out; out.reserve(text.size() / 4 * 3); for (std::size_t i = 0; i < text.size(); i += 4) { const bool last = i + 4 == text.size(); const std::int32_t a = Sextet(text[i]); const std::int32_t b = Sextet(text[i + 1]); const bool pad2 = last && text[i + 2] == '='; const bool pad3 = last && text[i + 3] == '='; const std::int32_t c = pad2 ? 0 : Sextet(text[i + 2]); const std::int32_t d = pad3 ? 0 : Sextet(text[i + 3]); if (a < 0 || b < 0 || c < 0 || d < 0 || (pad2 && !pad3)) return std::nullopt; const std::uint32_t chunk = (static_cast(a) << 18) | (static_cast(b) << 12) | (static_cast(c) << 6) | static_cast(d); out.push_back(static_cast((chunk >> 16) & 0xFF)); if (!pad2) out.push_back(static_cast((chunk >> 8) & 0xFF)); if (!pad3) out.push_back(static_cast(chunk & 0xFF)); } return out; }