/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef TRINITY_BASE_ENCODING_HPP #define TRINITY_BASE_ENCODING_HPP #include "Define.h" #include "Optional.h" #include #include #include namespace Trinity { namespace Impl { template struct GenericBaseEncoding { static constexpr std::size_t BITS_PER_CHAR = Encoding::BITS_PER_CHAR; static constexpr std::size_t PAD_TO = std::lcm(8u, BITS_PER_CHAR); static_assert(BITS_PER_CHAR < 8, "Encoding parameters are invalid"); static constexpr uint8 DECODE_ERROR = Encoding::DECODE_ERROR; static constexpr char PADDING = Encoding::PADDING; static constexpr std::size_t EncodedSize(std::size_t size) { size *= 8; // bits in input if (size % PAD_TO) // pad to boundary size += (PAD_TO - (size % PAD_TO)); return (size / BITS_PER_CHAR); } static constexpr std::size_t DecodedSize(std::size_t size) { size *= BITS_PER_CHAR; // bits in input if (size % PAD_TO) // pad to boundary size += (PAD_TO - (size % PAD_TO)); return (size / 8); } static std::string Encode(std::vector const& data) { auto it = data.begin(), end = data.end(); if (it == end) return ""; std::string s; s.reserve(EncodedSize(data.size())); uint8 bitsLeft = 8; // in current byte do { uint8 thisC = 0; if (bitsLeft >= BITS_PER_CHAR) { bitsLeft -= BITS_PER_CHAR; thisC = ((*it >> bitsLeft) & ((1 << BITS_PER_CHAR)-1)); if (!bitsLeft) { ++it; bitsLeft = 8; } } else { thisC = (*it & ((1 << bitsLeft) - 1)) << (BITS_PER_CHAR - bitsLeft); bitsLeft += (8 - BITS_PER_CHAR); if ((++it) != end) thisC |= (*it >> bitsLeft); } s.append(1, Encoding::Encode(thisC)); } while (it != end); while (bitsLeft != 8) { if (bitsLeft > BITS_PER_CHAR) bitsLeft -= BITS_PER_CHAR; else bitsLeft += (8 - BITS_PER_CHAR); s.append(1, PADDING); } return s; } static Optional> Decode(std::string const& data) { auto it = data.begin(), end = data.end(); if (it == end) return std::vector(); std::vector v; v.reserve(DecodedSize(data.size())); uint8 currentByte = 0; uint8 bitsLeft = 8; // in current byte while ((it != end) && (*it != PADDING)) { uint8 cur = Encoding::Decode(*(it++)); if (cur == DECODE_ERROR) return {}; if (bitsLeft > BITS_PER_CHAR) { bitsLeft -= BITS_PER_CHAR; currentByte |= (cur << bitsLeft); } else { bitsLeft = BITS_PER_CHAR - bitsLeft; // in encoded char currentByte |= (cur >> bitsLeft); v.push_back(currentByte); currentByte = (cur & ((1 << bitsLeft) - 1)); bitsLeft = 8 - bitsLeft; // in byte again currentByte <<= bitsLeft; } } if (currentByte) return {}; // decode error, trailing non-zero bits // process padding while ((it != end) && (*it == PADDING) && (bitsLeft != 8)) { if (bitsLeft > BITS_PER_CHAR) bitsLeft -= BITS_PER_CHAR; else bitsLeft += (8 - BITS_PER_CHAR); ++it; } // ok, all padding should be consumed, and we should be at end of string if (it == end) return v; // anything else is an error return {}; } }; } } #endif