diff --git a/include/string.hpp b/include/string.hpp index 00b3ece8..6ebf7887 100644 --- a/include/string.hpp +++ b/include/string.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include namespace rack { @@ -28,6 +29,7 @@ std::string ellipsize(const std::string& s, size_t len); std::string ellipsizePrefix(const std::string& s, size_t len); bool startsWith(const std::string& str, const std::string& prefix); bool endsWith(const std::string& str, const std::string& suffix); + /** Extracts the directory of the path. Example: directory("dir/file.txt") // "dir" Calls POSIX dirname(). @@ -52,20 +54,33 @@ std::string filenameExtension(const std::string& filename); Returns "" if the symbol is not found. */ std::string absolutePath(const std::string& path); + /** Scores how well a query matches a string. A score of 0 means no match. The score is arbitrary and is only meaningful for sorting. */ float fuzzyScore(const std::string& s, const std::string& query); + /** Converts a byte array to a Base64-encoded string. https://en.wikipedia.org/wiki/Base64 */ -std::string toBase64(const uint8_t* data, size_t len); +std::string toBase64(const uint8_t* data, size_t dataLen); +std::string toBase64(const std::vector& data); /** Converts a Base64-encoded string to a byte array. `outLen` is set to the length of the byte array. If non-NULL, caller must delete[] the result. */ -uint8_t* fromBase64(const std::string& str, size_t* outLen); +std::vector fromBase64(const std::string& str); + +/** Compress bytes with zlib. +*/ +std::vector compress(const uint8_t* data, size_t dataLen); +std::vector compress(const std::vector& data); +/** Uncompress bytes with zlib. +Unfortunately the output buffer cannot be computed from the compressed data, so you may need to hard-code the maximum expected size. +*/ +void uncompress(const uint8_t* compressed, size_t compressedLen, uint8_t* data, size_t* dataLen); +void uncompress(const std::vector& compressed, uint8_t* data, size_t* dataLen); struct CaseInsensitiveCompare { diff --git a/src/string.cpp b/src/string.cpp index d3ba7b46..80eac59f 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -4,6 +4,7 @@ #include // for tolower and toupper #include // for transform #include // for dirname and basename +#include namespace rack { @@ -154,10 +155,10 @@ float fuzzyScore(const std::string& s, const std::string& query) { } -std::string toBase64(const uint8_t* data, size_t len) { +std::string toBase64(const uint8_t* data, size_t dataLen) { static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - size_t numBlocks = size_t((len + 2) / 3); + size_t numBlocks = size_t((dataLen + 2) / 3); size_t strLen = numBlocks * 4; std::string str; str.reserve(strLen); @@ -166,7 +167,7 @@ std::string toBase64(const uint8_t* data, size_t len) { // Encode block uint32_t block = 0; int i; - for (i = 0; i < 3 && 3 * b + i < len; i++) { + for (i = 0; i < 3 && 3 * b + i < dataLen; i++) { block |= uint32_t(data[3 * b + i]) << (8 * (2 - i)); } @@ -180,10 +181,15 @@ std::string toBase64(const uint8_t* data, size_t len) { } -uint8_t* fromBase64(const std::string& str, size_t* outLen) { +std::string toBase64(const std::vector& data) { + return toBase64(data.data(), data.size()); +} + + +std::vector fromBase64(const std::string& str) { size_t strLen = str.size(); if (strLen % 4 != 0) - return NULL; + throw std::runtime_error("String length not a factor of 4"); size_t numBlocks = strLen / 4; size_t len = numBlocks * 3; @@ -193,10 +199,8 @@ uint8_t* fromBase64(const std::string& str, size_t* outLen) { if (str[strLen - 2] == '=') len--; } - if (outLen) - *outLen = len; - uint8_t* data = new uint8_t[len]; + std::vector data(len); for (size_t b = 0; b < numBlocks; b++) { // Encode block @@ -226,8 +230,8 @@ uint8_t* fromBase64(const std::string& str, size_t* outLen) { break; } else { - // Ignore invalid characters, such as whitespace. - continue; + // Since we assumed the string was a factor of 4 bytes, fail if an invalid character, such as whitespace, was found. + throw std::runtime_error("String contains non-base64 character"); } block |= uint32_t(d) << (6 * (3 - i)); @@ -243,5 +247,33 @@ uint8_t* fromBase64(const std::string& str, size_t* outLen) { } +std::vector compress(const uint8_t* data, size_t dataLen) { + std::vector compressed; + size_t outCap = ::compressBound(dataLen); + compressed.resize(outCap); + int err = ::compress2(compressed.data(), &outCap, data, dataLen, Z_BEST_COMPRESSION); + if (err) + throw std::runtime_error("Zlib error"); + compressed.resize(outCap); + return compressed; +} + + +std::vector compress(const std::vector& data) { + return compress(data.data(), data.size()); +} + + +void uncompress(const uint8_t* compressed, size_t compressedLen, uint8_t* data, size_t* dataLen) { + int err = ::uncompress(data, dataLen, compressed, compressedLen); + (void) err; +} + + +void uncompress(const std::vector& compressed, uint8_t* data, size_t* dataLen) { + uncompress(compressed.data(), compressed.size(), data, dataLen); +} + + } // namespace string } // namespace rack