267 lines
6.7 KiB
C++
Executable File
267 lines
6.7 KiB
C++
Executable File
/*
|
|
PIP - Platform Independent Primitives
|
|
Byte array
|
|
Copyright (C) 2016 Ivan Pelipenko peri4ko@gmail.com
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "pibytearray.h"
|
|
#include "pistring.h"
|
|
|
|
/*! \class PIByteArray
|
|
* \brief Byte array
|
|
* \details This class based on PIDeque<uchar> and provide some handle function
|
|
* to manipulate it.
|
|
*
|
|
* \section PIByteArray_sec0 Usage
|
|
* %PIByteArray can be used to store custom data and manipulate it. There are many
|
|
* stream operators to store/restore common types to byte array. Store operators
|
|
* places data at the end of array, restore operators takes data from the beginning
|
|
* of array.
|
|
* In addition there are Base 64 convertions and checksums:
|
|
* * plain 8-bit
|
|
* * plain 32-bit
|
|
*
|
|
* One of the major usage of %PIByteArray is stream functions. You can form binary
|
|
* packet from many types (also dynamic types, e.g. PIVector) with one line:
|
|
* \snippet pibytearray.cpp 0
|
|
*
|
|
* Or you can descibe stream operator of your own type and store/restore vectors of
|
|
* your type:
|
|
* \snippet pibytearray.cpp 1
|
|
*
|
|
* For store/restore custom data blocks there is PIByteArray::RawData class. Stream
|
|
* operators of this class simply store/restore data block to/from byte array.
|
|
* \snippet pibytearray.cpp 2
|
|
*
|
|
* \section PIByteArray_sec1 Attention
|
|
* Stream operator of %PIByteArray store byte array as vector, not simply append
|
|
* content of byte array. This operators useful to transmit custom data as %PIByteArray
|
|
* packed into parent byte array, e.g. to form packet from %PIByteArray.
|
|
* To append one byte array to another use funtion \a append().
|
|
* \snippet pibytearray.cpp 3
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
PIByteArray &PIByteArray::convertToBase64() {
|
|
return *this = toBase64();
|
|
}
|
|
|
|
|
|
PIByteArray &PIByteArray::convertFromBase64() {
|
|
return *this = fromBase64(*this);
|
|
}
|
|
|
|
|
|
PIByteArray PIByteArray::toBase64() const {
|
|
const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
|
|
"ghijklmn" "opqrstuv" "wxyz0123" "456789+/";
|
|
const char padchar = '=';
|
|
int padlen = 0;
|
|
PIByteArray tmp((size() * 4) / 3 + 3);
|
|
int i = 0;
|
|
uchar *out = tmp.data();
|
|
while (i < size_s()) {
|
|
int chunk = 0;
|
|
chunk |= int((*this)[i++]) << 16;
|
|
if (i == size()) {
|
|
padlen = 2;
|
|
} else {
|
|
chunk |= int((*this)[i++]) << 8;
|
|
if (i == size()) padlen = 1;
|
|
else chunk |= int((*this)[i++]);
|
|
}
|
|
int j = (chunk & 0x00fc0000) >> 18;
|
|
int k = (chunk & 0x0003f000) >> 12;
|
|
int l = (chunk & 0x00000fc0) >> 6;
|
|
int m = (chunk & 0x0000003f);
|
|
*out++ = alphabet[j];
|
|
*out++ = alphabet[k];
|
|
if (padlen > 1) *out++ = padchar;
|
|
else *out++ = alphabet[l];
|
|
if (padlen > 0) *out++ = padchar;
|
|
else *out++ = alphabet[m];
|
|
}
|
|
tmp.resize(out - tmp.data());
|
|
return tmp;
|
|
}
|
|
|
|
|
|
PIByteArray PIByteArray::fromBase64(const PIByteArray &base64) {
|
|
unsigned int buf = 0;
|
|
int nbits = 0;
|
|
PIByteArray tmp((base64.size() * 3) / 4);
|
|
int offset = 0;
|
|
for (int i = 0; i < base64.size(); ++i) {
|
|
int ch = base64.at(i);
|
|
int d;
|
|
if (ch >= 'A' && ch <= 'Z') d = ch - 'A';
|
|
else if (ch >= 'a' && ch <= 'z') d = ch - 'a' + 26;
|
|
else if (ch >= '0' && ch <= '9') d = ch - '0' + 52;
|
|
else if (ch == '+') d = 62;
|
|
else if (ch == '/') d = 63;
|
|
else d = -1;
|
|
if (d != -1) {
|
|
buf = (buf << 6) | d;
|
|
nbits += 6;
|
|
if (nbits >= 8) {
|
|
nbits -= 8;
|
|
tmp[offset++] = buf >> nbits;
|
|
buf &= (1 << nbits) - 1;
|
|
}
|
|
}
|
|
}
|
|
tmp.resize(offset);
|
|
return tmp;
|
|
}
|
|
|
|
|
|
PIByteArray & PIByteArray::compressRLE(uchar threshold) {
|
|
PIByteArray t;
|
|
uchar fb, clen, mlen = 255 - threshold;
|
|
for (uint i = 0; i < size();) {
|
|
fb = at(i);
|
|
clen = 1;
|
|
while (at(++i) == fb) {
|
|
++clen;
|
|
if (clen == mlen)
|
|
break;
|
|
}
|
|
if (clen > 1) {
|
|
t.push_back(threshold + clen);
|
|
t.push_back(fb);
|
|
continue;
|
|
}
|
|
if (fb >= threshold) {
|
|
t.push_back(threshold + 1);
|
|
t.push_back(fb);
|
|
} else
|
|
t.push_back(fb);
|
|
}
|
|
*this = t;
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIByteArray & PIByteArray::decompressRLE(uchar threshold) {
|
|
PIByteArray t;
|
|
uchar fb, clen;
|
|
for (uint i = 0; i < size(); ++i) {
|
|
fb = at(i);
|
|
if (fb >= threshold) {
|
|
clen = fb - threshold;
|
|
fb = at(++i);
|
|
for (uint j = 0; j < clen; ++j)
|
|
t.push_back(fb);
|
|
continue;
|
|
} else
|
|
t.push_back(fb);
|
|
}
|
|
*this = t;
|
|
return *this;
|
|
}
|
|
|
|
|
|
uchar PIByteArray::checksumPlain8() const {
|
|
uchar c = 0;
|
|
int sz = size_s();
|
|
for (int i = 0; i < sz; ++i)
|
|
c += at(i);
|
|
c = ~(c + 1);
|
|
return c;
|
|
}
|
|
|
|
|
|
uint PIByteArray::checksumPlain32() const {
|
|
uint c = 0;
|
|
int sz = size_s();
|
|
for (int i = 0; i < sz; ++i)
|
|
c += at(i) * (i + 1);
|
|
c = ~(c + 1);
|
|
return c;
|
|
}
|
|
|
|
|
|
PIString PIByteArray::toString(int base) const {
|
|
PIString ret;
|
|
int sz = size_s();
|
|
for (int i = 0; i < sz; ++i) {
|
|
if (i > 0) ret += " ";
|
|
if (base == 2) ret += "b";
|
|
if (base == 8) ret += "0";
|
|
if (base == 16) ret += "0x";
|
|
ret += PIString::fromNumber(at(i), base);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIByteArray::toHex() const {
|
|
PIByteArray hex(size() * 2);
|
|
uchar *hexData = hex.data();
|
|
const uchar *d = data();
|
|
for (int i = 0; i < size_s(); ++i) {
|
|
int j = (d[i] >> 4) & 0xf;
|
|
if (j <= 9) hexData[i*2] = (j + '0');
|
|
else hexData[i*2] = (j + 'a' - 10);
|
|
j = d[i] & 0xf;
|
|
if (j <= 9) hexData[i*2+1] = (j + '0');
|
|
else hexData[i*2+1] = (j + 'a' - 10);
|
|
}
|
|
return PIString(hex);
|
|
}
|
|
|
|
|
|
PIByteArray PIByteArray::fromString(PIString str) {
|
|
PIByteArray ret;
|
|
if (str.trim().isEmpty()) return ret;
|
|
str.replaceAll("\n", " ").replaceAll("\t", " ").replaceAll(" ", " ");
|
|
PIStringList bl(str.split(" "));
|
|
bool ok(false);
|
|
piForeachC (PIString & b, bl) {
|
|
int bv = b.toInt(-1, &ok);
|
|
if (ok) ret << uchar(bv);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIByteArray PIByteArray::fromHex(PIString str) {
|
|
PIByteArray hexEncoded = str.toByteArray();
|
|
PIByteArray res((hexEncoded.size() + 1)/ 2);
|
|
uchar *result = res.data() + res.size();
|
|
bool odd_digit = true;
|
|
for (int i = hexEncoded.size() - 1; i >= 0; --i) {
|
|
int ch = hexEncoded.at(i);
|
|
int tmp;
|
|
if (ch >= '0' && ch <= '9') tmp = ch - '0';
|
|
else if (ch >= 'a' && ch <= 'f') tmp = ch - 'a' + 10;
|
|
else if (ch >= 'A' && ch <= 'F') tmp = ch - 'A' + 10;
|
|
else continue;
|
|
if (odd_digit) {
|
|
--result;
|
|
*result = tmp;
|
|
odd_digit = false;
|
|
} else {
|
|
*result |= tmp << 4;
|
|
odd_digit = true;
|
|
}
|
|
}
|
|
res.remove(0, result - res.data());
|
|
return res;
|
|
}
|