/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2024 Melin Software HB
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 3 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 .
Melin Software HB - software@melin.nu - www.melin.nu
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
************************************************************************/
#include "stdafx.h"
#include "meos_util.h"
#include
#include
#include "binencoder.h"
using namespace std;
Encoder92::Encoder92() {
int d = 32;
fill(reverse_table, reverse_table + 128, 0);
fill(used, used + 128, false);
for (int j = 0; j < 92; j++) {
table[j] = d++;
while (d == '"' || d == '<' || d == '&' || d=='>')
d++;
}
table[91] = '\t';
swap(table[0], table[1]);
for (int j = 0; j < 92; j++) {
reverse_table[table[j]] = j;
used[table[j]] = true;
}
}
void Encoder92::encode92(const uint8_t datain[13], uint8_t dataout[16]) {
uint64_t w64bitA = *(uint64_t*)&datain[0];
uint32_t w32bitB = *(uint32_t*)&datain[8];
uint32_t w8bitC = datain[12];
// Consume 60 bits of data from w64bitA (4 bits will remain)
for (int i = 0; i < 10; i++) {
dataout[i] = w64bitA & 0b00111111;
w64bitA = w64bitA >> 6;
}
// Consume 30 bits of data from w32bitB (2 bits will remain)
for (int i = 10; i < 15; i++) {
dataout[i] = w32bitB & 0b00111111;
w32bitB = w32bitB >> 6;
}
// Consume remaining 2 + 4 bits
dataout[15] = int8_t(w32bitB | (w64bitA << 2));
for (int i = 0; i < 16; i+=2) {
int extraData = (w8bitC & 0x1);
w8bitC = w8bitC >> 1;
int combined = (extraData << 12) | dataout[i] | (dataout[i+1] << 6); // 13 bits 0 -- 8192
dataout[i] = combined % 92;
dataout[i+1] = combined / 92;
}
}
void Encoder92::decode92(const uint8_t datain[16], uint8_t dataout[13]) {
uint8_t datain_64[16];
uint32_t w8bitC = 0;
for (int i = 0; i < 16; i += 2) {
int combined = datain[i] + datain[i + 1] * 92;
datain_64[i] = combined & 0b00111111;
combined = combined >> 6;
datain_64[i+1] = combined & 0b00111111;
combined = combined >> 6;
w8bitC |= (combined << (i/2));
}
uint64_t w64bitA = 0;
uint32_t w32bitB = 0;
// Reconstruct 60 bits of data to w64bitA
for (int i = 9; i >= 0; i--) {
w64bitA <<= 6;
w64bitA |= datain_64[i];
}
// Reconstruct 30 bits of data to w32bitB
for (int i = 14; i >= 10; i--) {
w32bitB <<= 6;
w32bitB |= datain_64[i];
}
// Add remaining bits
w32bitB = w32bitB | ((datain_64[15] & 0b11) << 30);
uint64_t highBits = (datain_64[15] >> 2) & 0b1111;
w64bitA = w64bitA | (highBits << 60ll);
*((uint64_t*)&dataout[0]) = w64bitA;
*((uint32_t*)&dataout[8]) = w32bitB;
dataout[12] = int8_t(w8bitC);
}
void Encoder92::encode92(const vector& bytesIn, string& encodedString) {
int m13 = bytesIn.size()%13;
int extra = m13 > 0 ? 13 - m13 : 0;
int blocks = (bytesIn.size() + extra) / 13;
encodedString = itos(bytesIn.size()) + ":";
int outLen = encodedString.length();
encodedString.resize(encodedString.size() + blocks * 16, ' ');
const uint8_t* inPtr = bytesIn.data();
//uint8_t* outPtr = encodedString.data() + outLen;
uint8_t datain[13];
fill(datain, datain + 13, 0);
uint8_t dataout[16];
int fullBlocks = blocks;
if (extra > 0)
fullBlocks--;
for (int i = 0; i < fullBlocks; i++) {
//for (int j = 0; j < 13; j++)
encode92(inPtr, dataout);
inPtr += 13;
for (int j = 0; j < 16; j++)
encodedString[outLen++] = table[dataout[j]];
}
if (extra > 0) {
for (int j = 0; j < m13; j++) {
datain[j] = inPtr[j];
}
encode92(datain, dataout);
for (int j = 0; j < 16; j++)
encodedString[outLen++] = table[dataout[j]];
}
}
void Encoder92::decode92(const string& encodedString, vector& bytesOut) {
bytesOut.clear();
if (encodedString.empty())
return;
unsigned size = atoi(encodedString.c_str());
string start = itos(size);
int len = start.length();
if (encodedString.size() < len || encodedString[len] != ':' || size<0)
throw std::exception("Invalid data");
int m13 = size % 13;
int extra = m13 > 0 ? 13 - m13 : 0;
int blocks = (size + extra) / 13;
int inDataSize = blocks * 16;
if (encodedString.size() < len + 1 + inDataSize)
throw std::exception("Invalid data");
bytesOut.resize(size);
auto outPtr = bytesOut.data();
uint8_t datain[16];
uint8_t dataout[13];
int fullBlocks = blocks;
if (extra > 0)
fullBlocks--;
const char *inPtr = encodedString.c_str() + len + 1;
for (int i = 0; i < fullBlocks; i++) {
for (int j = 0; j < 16; j++) {
int v = inPtr[j];
if (v < 0 || v > 127 || !used[v])
throw std::exception("Invalid data");
datain[j] = reverse_table[inPtr[j]];
}
decode92(datain, outPtr);
inPtr += 16;
outPtr += 13;
}
if (extra > 0) {
for (int j = 0; j < 16; j++) {
int v = inPtr[j];
if (v < 0 || v > 127 || !used[v])
throw std::exception("Invalid data");
datain[j] = reverse_table[inPtr[j]];
}
decode92(datain, dataout);
for (int j = 0; j < m13; j++)
outPtr[j] = dataout[j];
}
}