/* PIP - Platform Independent Primitives Byte array Copyright (C) 2014 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 * * */ #pragma pack(push, 1) const char PIByteArray::base64Table[64] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f}; const char PIByteArray::base64InvTable[256] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3E, 0x0, 0x0, 0x0, 0x3F, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; #pragma pack(pop) int PIHuffman::nodeCompare(const void * f, const void * s) { return (reinterpret_cast(const_cast(s))->freq - reinterpret_cast(const_cast(f))->freq); } PIDeque PIHuffman::compress(const PIDeque & src) { calcFrequencies(src); return src; } void PIHuffman::calcFrequencies(const PIDeque & src) { nodes.resize(256); for (int i = 0; i < 256; ++i) { nodes[i].parent = nodes[i].right = nodes[i].left = 0; nodes[i].freq = 0; nodes[i].word.resize(1); nodes[i].word[0] = static_cast(i); } for (int i = 0; i < src.size_s(); ++i) nodes[src[i]].freq++; std::qsort(nodes.data(), 256, sizeof(node), nodeCompare); for (int i = 255; i >= 0; --i) if (nodes[i].freq > 0 && i < 255) {nodes.remove(i + 1, 255 - i); break;} for (int i = 0; i < nodes.size_s(); ++i) cout << string((char*)nodes[i].word.data(), 1) << ": " << nodes[i].freq << endl; } PIHuffman PIByteArray::huffman; PIByteArray & PIByteArray::convertToBase64() { base64HelpStruct hs; PIByteArray t; if (size() == 0) return *this; int sz = (size_s() / 3) * 3; for (int i = 0; i < sz; ++i) { hs.byte.byte0 = hs.byte.byte1 = hs.byte.byte2 = 0; hs.byte.byte0 = at(i); hs.byte.byte1 = at(++i); hs.byte.byte2 = at(++i); t.push_back(base64Table[hs.ascii.ascii0]); t.push_back(base64Table[hs.ascii.ascii1]); t.push_back(base64Table[hs.ascii.ascii2]); t.push_back(base64Table[hs.ascii.ascii3]); } hs.byte.byte0 = hs.byte.byte1 = hs.byte.byte2 = 0; sz = size() % 3; switch (sz) { case 1: hs.byte.byte0 = back(); t.push_back(base64Table[hs.ascii.ascii0]); t.push_back(base64Table[hs.ascii.ascii1]); t.push_back('='); t.push_back('='); break; case 2: hs.byte.byte0 = at(size() - 2); hs.byte.byte1 = back(); t.push_back(base64Table[hs.ascii.ascii0]); t.push_back(base64Table[hs.ascii.ascii1]); t.push_back(base64Table[hs.ascii.ascii2]); t.push_back('='); break; default: break; } *this = t; return *this; } PIByteArray & PIByteArray::convertFromBase64() { base64HelpStruct hs; PIByteArray t; uint sz = size(); if (sz == 0) return *this; for (uint i = 0; i < sz; ++i) { hs.byte.byte0 = hs.byte.byte1 = hs.byte.byte2 = 0; hs.ascii.ascii0 = base64InvTable[at(i)]; hs.ascii.ascii1 = base64InvTable[at(++i)]; hs.ascii.ascii2 = base64InvTable[at(++i)]; hs.ascii.ascii3 = base64InvTable[at(++i)]; t.push_back(hs.byte.byte0); t.push_back(hs.byte.byte1); t.push_back(hs.byte.byte2); } if (back() == '=') t.pop_back(); if (sz > 1) if (at(sz - 2) == '=') t.pop_back(); *this = t; return *this; } 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; } 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; }