/* 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 . */ #include "pibytearray.h" #include "pistring.h" /*! \class PIByteArray * \brief Byte array * \details This class based on PIDeque 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; }