diff options
Diffstat (limited to 'zen')
-rw-r--r-- | zen/base64.h | 175 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/basic_math.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/build_info.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/crc.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/dir_watcher.cpp | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/dir_watcher.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/error_log.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/file_access.cpp | 25 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/file_access.h | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/file_error.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/file_id_def.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/file_io.cpp | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/file_io.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/file_traverser.cpp | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/file_traverser.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/format_unit.cpp | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/format_unit.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/globals.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/guid.h | 11 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/http.cpp | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/http.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/i18n.h | 0 | ||||
-rw-r--r-- | zen/json.h | 541 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/legacy_compiler.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/perf.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/process_priority.cpp | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/process_priority.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/recycler.cpp | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/recycler.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/ring_buffer.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/scope_guard.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/serialize.h | 1 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/shell_execute.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/shutdown.cpp | 34 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/shutdown.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/socket.h | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/stl_tools.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/string_base.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/string_tools.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/string_traits.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/symlink_target.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/sys_error.cpp | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/sys_error.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/thread.cpp | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/thread.h | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/time.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/type_traits.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/utf.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/warn_static.h | 0 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/zlib_wrap.cpp | 79 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/zlib_wrap.h | 21 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/zstring.cpp | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | zen/zstring.h | 0 |
53 files changed, 837 insertions, 60 deletions
diff --git a/zen/base64.h b/zen/base64.h new file mode 100644 index 00000000..54a0a98b --- /dev/null +++ b/zen/base64.h @@ -0,0 +1,175 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef BASE64_H_08473021856321840873021487213453214 +#define BASE64_H_08473021856321840873021487213453214 + +#include <iterator> +#include "type_traits.h" + + +namespace zen +{ +//http://en.wikipedia.org/wiki/Base64 +/* +Usage: + const std::string input = "Sample text"; + std::string output; + zen::encodeBase64(input.begin(), input.end(), std::back_inserter(output)); + //output contains "U2FtcGxlIHRleHQ=" +*/ +template <class InputIterator, class OutputIterator> +OutputIterator encodeBase64(InputIterator first, InputIterator last, OutputIterator result); //nothrow! + +template <class InputIterator, class OutputIterator> +OutputIterator decodeBase64(InputIterator first, InputIterator last, OutputIterator result); //nothrow! + +std::string stringEncodeBase64(const std::string& str); +std::string stringDecodeBase64(const std::string& str); + + + + + + + + + + +//------------------------- implementation ------------------------------- +namespace impl +{ +//64 chars for base64 encoding + padding char +constexpr char ENCODING_MIME[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +constexpr signed char DECODING_MIME[] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 64, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 + }; +const unsigned char INDEX_PAD = 64; //"=" +} + + +template <class InputIterator, class OutputIterator> inline +OutputIterator encodeBase64(InputIterator first, InputIterator last, OutputIterator result) +{ + using namespace impl; + static_assert(sizeof(typename std::iterator_traits<InputIterator>::value_type) == 1); + static_assert(arraySize(ENCODING_MIME) == 64 + 1 + 1); + static_assert(arrayAccumulate<int>(ENCODING_MIME) + INDEX_PAD == 5602); + + while (first != last) + { + const unsigned char a = static_cast<unsigned char>(*first++); + *result++ = ENCODING_MIME[a >> 2]; + + if (first == last) + { + *result++ = ENCODING_MIME[((a & 0x3) << 4)]; + *result++ = ENCODING_MIME[INDEX_PAD]; + *result++ = ENCODING_MIME[INDEX_PAD]; + break; + } + const unsigned char b = static_cast<unsigned char>(*first++); + *result++ = ENCODING_MIME[((a & 0x3) << 4) | (b >> 4)]; + + if (first == last) + { + *result++ = ENCODING_MIME[((b & 0xf) << 2)]; + *result++ = ENCODING_MIME[INDEX_PAD]; + break; + } + const unsigned char c = static_cast<unsigned char>(*first++); + *result++ = ENCODING_MIME[((b & 0xf) << 2) | (c >> 6)]; + *result++ = ENCODING_MIME[c & 0x3f]; + } + return result; +} + + +template <class InputIterator, class OutputIterator> inline +OutputIterator decodeBase64(InputIterator first, InputIterator last, OutputIterator result) +{ + using namespace impl; + static_assert(sizeof(typename std::iterator_traits<InputIterator>::value_type) == 1); + static_assert(arraySize(DECODING_MIME) == 128); + static_assert(arrayAccumulate<int>(DECODING_MIME) + INDEX_PAD == 2081); + + const unsigned char INDEX_END = INDEX_PAD + 1; + + auto readIndex = [&]() -> unsigned char //return index within [0, 64] or INDEX_END if end of input + { + for (;;) + { + if (first == last) + return INDEX_END; + + const unsigned char ch = static_cast<unsigned char>(*first++); + if (ch < 128) //we're in lower ASCII table half + { + const int index = DECODING_MIME[ch]; + if (0 <= index && index <= static_cast<int>(INDEX_PAD)) //skip all unknown characters (including carriage return, line-break, tab) + return static_cast<unsigned char>(index); + } + } + }; + + for (;;) + { + const unsigned char index1 = readIndex(); + const unsigned char index2 = readIndex(); + if (index1 >= INDEX_PAD || index2 >= INDEX_PAD) + { + assert(index1 == INDEX_END && index2 == INDEX_END); + break; + } + *result++ = static_cast<char>((index1 << 2) | (index2 >> 4)); + + const unsigned char index3 = readIndex(); + if (index3 >= INDEX_PAD) //padding + { + assert(index3 == INDEX_PAD); + break; + } + *result++ = static_cast<char>(((index2 & 0xf) << 4) | (index3 >> 2)); + + const unsigned char index4 = readIndex(); + if (index4 >= INDEX_PAD) //padding + { + assert(index4 == INDEX_PAD); + break; + } + *result++ = static_cast<char>(((index3 & 0x3) << 6) | index4); + } + return result; +} + + +inline +std::string stringEncodeBase64(const std::string& str) +{ + std::string out; + encodeBase64(str.begin(), str.end(), std::back_inserter(out)); + return out; +} + + +inline +std::string stringDecodeBase64(const std::string& str) +{ + std::string out; + decodeBase64(str.begin(), str.end(), std::back_inserter(out)); + return out; +} +} + +#endif //BASE64_H_08473021856321840873021487213453214 diff --git a/zen/basic_math.h b/zen/basic_math.h index 75f5d3b8..75f5d3b8 100755..100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h diff --git a/zen/build_info.h b/zen/build_info.h index e80f3721..e80f3721 100755..100644 --- a/zen/build_info.h +++ b/zen/build_info.h diff --git a/zen/crc.h b/zen/crc.h index df460a03..df460a03 100755..100644 --- a/zen/crc.h +++ b/zen/crc.h diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index f5ed0488..f5ed0488 100755..100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h index f552e2b2..f552e2b2 100755..100644 --- a/zen/dir_watcher.h +++ b/zen/dir_watcher.h diff --git a/zen/error_log.h b/zen/error_log.h index 4a3f5f2c..4a3f5f2c 100755..100644 --- a/zen/error_log.h +++ b/zen/error_log.h diff --git a/zen/file_access.cpp b/zen/file_access.cpp index d257b7c2..a60eccb2 100755..100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -60,7 +60,7 @@ std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath) if (startsWith(itemPath, std::string("/media/") + username + "/")) pc = doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - if (!pc && startsWith(itemPath, "/run/media/")) //Centos, Suse: e.g. /run/media/zenju/DEVICE_NAME + if (!pc && startsWith(itemPath, "/run/media/")) //CentOS, Suse: e.g. /run/media/zenju/DEVICE_NAME if (const char* username = ::getenv("USER")) if (startsWith(itemPath, std::string("/run/media/") + username + "/")) pc = doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/); @@ -131,8 +131,8 @@ std::optional<ItemType> zen::itemStillExists(const Zstring& itemPath) //throw Fi try { traverseFolder(*parentPath, - [&](const FileInfo& fi) { if (fi.itemName == itemName) throw ItemType::FILE; }, - [&](const FolderInfo& fi) { if (fi.itemName == itemName) throw ItemType::FOLDER; }, + [&](const FileInfo& fi) { if (fi.itemName == itemName) throw ItemType::FILE; }, + [&](const FolderInfo& fi) { if (fi.itemName == itemName) throw ItemType::FOLDER; }, [&](const SymlinkInfo& si) { if (si.itemName == itemName) throw ItemType::SYMLINK; }, [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); } @@ -190,18 +190,18 @@ FileDetails zen::getFileDetails(const Zstring& itemPath) //throw FileError { makeUnsigned(fileInfo.st_size), fileInfo.st_mtime, - fileInfo.st_dev, - //FileIndex fileIndex = fileInfo.st_ino; + fileInfo.st_dev, + //FileIndex fileIndex = fileInfo.st_ino; }; } Zstring zen::getTempFolderPath() //throw FileError { - const char* buf = ::getenv("TMPDIR"); //no extended error reporting - if (!buf) - throw FileError(_("Cannot get process information."), L"getenv: TMPDIR not found."); - return buf; + if (const char* buf = ::getenv("TMPDIR")) //no extended error reporting + return buf; + + return P_tmpdir; //usually resolves to "/tmp" } @@ -263,8 +263,8 @@ void removeDirectoryImpl(const Zstring& folderPath) //throw FileError //get all files and directories from current directory (WITHOUT subdirectories!) traverseFolder(folderPath, - [&](const FileInfo& fi) { filePaths .push_back(fi.fullPath); }, - [&](const FolderInfo& fi) { folderPaths .push_back(fi.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies! + [&](const FileInfo& fi) { filePaths.push_back(fi.fullPath); }, + [&](const FolderInfo& fi) { folderPaths.push_back(fi.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies! [&](const SymlinkInfo& si) { symlinkPaths.push_back(si.fullPath); }, [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); @@ -334,7 +334,7 @@ void moveAndRenameFileSub(const Zstring& pathSource, const Zstring& pathTarget, infoSrc.st_ino != infoTrg.st_ino) throwException(EEXIST); //that's what we're really here for //else: continue with a rename in case - //caveat: if we have a hardlink referenced by two different paths, the source one will be unlinked => fine, but not exactly a "rename"... + //caveat: if we have a hardlink referenced by two different paths, the source one will be unlinked => fine, but not exactly a "rename"... } //else: not existing or access error (hopefully ::rename will also fail!) } @@ -574,7 +574,6 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File return; //already existing => possible, if createDirectoryIfMissingRecursion() is run in parallel } catch (FileError&) {} //not yet existing or access error - //catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } -> details needed??? throw; } diff --git a/zen/file_access.h b/zen/file_access.h index d981dcc3..e06c64ac 100755..100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -102,7 +102,7 @@ struct FileCopyResult FileCopyResult copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked, X //accummulated delta != file size! consider ADS, sparse, compressed files - const IOCallback& notifyUnbufferedIO /*throw X*/); + const IOCallback& notifyUnbufferedIO /*throw X*/); } #endif //FILE_ACCESS_H_8017341345614857 diff --git a/zen/file_error.h b/zen/file_error.h index 101d6543..101d6543 100755..100644 --- a/zen/file_error.h +++ b/zen/file_error.h diff --git a/zen/file_id_def.h b/zen/file_id_def.h index 55ee77f5..55ee77f5 100755..100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 1c6ab6f2..1c6ab6f2 100755..100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp diff --git a/zen/file_io.h b/zen/file_io.h index bf23d22c..bf23d22c 100755..100644 --- a/zen/file_io.h +++ b/zen/file_io.h diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index cc6e0c0b..cc6e0c0b 100755..100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 5c1683f8..5c1683f8 100755..100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 3e75278b..3e75278b 100755..100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp diff --git a/zen/format_unit.h b/zen/format_unit.h index de5a0811..de5a0811 100755..100644 --- a/zen/format_unit.h +++ b/zen/format_unit.h diff --git a/zen/globals.h b/zen/globals.h index 024147fa..024147fa 100755..100644 --- a/zen/globals.h +++ b/zen/globals.h diff --git a/zen/guid.h b/zen/guid.h index a26688f8..c89e8082 100755..100644 --- a/zen/guid.h +++ b/zen/guid.h @@ -18,13 +18,11 @@ namespace zen inline std::string generateGUID() //creates a 16-byte GUID { -#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25) //getentropy() requires glibc 2.25 (ldd --version) PS: Centos 7 is on 2.17 std::string guid(16, '\0'); - if (::getentropy(&guid[0], 16) != 0) //"The maximum permitted value for the length argument is 256" +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25) //getentropy() requires glibc 2.25 (ldd --version) PS: CentOS 7 is on 2.17 + if (::getentropy(&guid[0], guid.size()) != 0) //"The maximum permitted value for the length argument is 256" throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] Failed to generate GUID." + "\n" + utfTo<std::string>(formatSystemError(L"getentropy", errno))); - return guid; - #else class RandomGeneratorPosix { @@ -55,10 +53,9 @@ std::string generateGUID() //creates a 16-byte GUID const int fd_ = ::open("/dev/urandom", O_RDONLY | O_CLOEXEC); }; thread_local RandomGeneratorPosix gen; - std::string guid(16, '\0'); - gen.getBytes(&guid[0], 16); - return guid; + gen.getBytes(&guid[0], guid.size()); #endif + return guid; } } diff --git a/zen/http.cpp b/zen/http.cpp index 1f89bf20..1f89bf20 100755..100644 --- a/zen/http.cpp +++ b/zen/http.cpp diff --git a/zen/http.h b/zen/http.h index 5d84be2c..5d84be2c 100755..100644 --- a/zen/http.h +++ b/zen/http.h diff --git a/zen/i18n.h b/zen/i18n.h index 2ecee45a..2ecee45a 100755..100644 --- a/zen/i18n.h +++ b/zen/i18n.h diff --git a/zen/json.h b/zen/json.h new file mode 100644 index 00000000..374a3f14 --- /dev/null +++ b/zen/json.h @@ -0,0 +1,541 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef JSON_H_0187348321748321758934215734 +#define JSON_H_0187348321748321758934215734 + +#include <zen/string_tools.h> + + +namespace zen +{ +//https://tools.ietf.org/html/rfc8259 +struct JsonValue +{ + enum class Type + { + null, // + boolean, //primitive types + number, // + string, // + object, + array, + }; + + explicit JsonValue() {} + explicit JsonValue(Type t) : type(t) {} + explicit JsonValue(bool b) : type(Type::boolean), primVal(b ? "true" : "false") {} + explicit JsonValue(int64_t num) : type(Type::number), primVal(numberTo<std::string>(num)) {} + explicit JsonValue(double num) : type(Type::number), primVal(numberTo<std::string>(num)) {} + explicit JsonValue(const std::string& str) : type(Type::string), primVal(str) {} + + Type type = Type::null; + std::string primVal; //for primitive types + std::map<std::string, std::unique_ptr<JsonValue>> objectVal; //"[...] most implementations of JSON libraries do not accept duplicate keys [...]" => fine! + std::vector<std::unique_ptr<JsonValue>> arrayVal; +}; + + +std::string serializeJson(const JsonValue& jval, + const std::string& lineBreak = "\r\n", + const std::string& indent = " "); //noexcept + + +struct JsonParsingError +{ + JsonParsingError(size_t rowNo, size_t colNo) : row(rowNo), col(colNo) {} + const size_t row; //beginning with 0 + const size_t col; // +}; +JsonValue parseJson(const std::string& stream); //throw JsonParsingError + + + +//helper functions for JsonValue access: +inline +const JsonValue* getChildFromJsonObject(const JsonValue& jvalue, const std::string& name) +{ + if (jvalue.type != JsonValue::Type::object) + return nullptr; + + auto it = jvalue.objectVal.find(name); + if (it == jvalue.objectVal.end()) + return nullptr; + + return it->second.get(); +} + + +inline +std::optional<std::string> getPrimitiveFromJsonObject(const JsonValue& jvalue, const std::string& name) +{ + if (const JsonValue* childValue = getChildFromJsonObject(jvalue, name)) + if (childValue->type != JsonValue::Type::object && + childValue->type != JsonValue::Type::array) + return childValue->primVal; + return std::nullopt; +} + + + + + +//---------------------- implementation ---------------------- +namespace json_impl +{ +namespace +{ +std::string jsonEscape(const std::string& str) +{ + std::string output; + for (const char c : str) + { + if (c == '"') output += "\\\""; //escaping mandatory + else if (c == '\\') output += "\\\\"; // + + else if (c == '\b') output += "\\b"; // + else if (c == '\f') output += "\\f"; // + else if (c == '\n') output += "\\n"; //prefer compact escaping + else if (c == '\r') output += "\\r"; // + else if (c == '\t') output += "\\t"; // + + else if (static_cast<unsigned char>(c) < 32) + { + const auto hexDigits = hexify(c); + output += "\\u00"; + output += hexDigits.first; + output += hexDigits.second; + } + else + output += c; + } + return output; +} + + +std::string jsonUnescape(const std::string& str) +{ + std::string output; + std::basic_string<impl::Char16> utf16Buf; + + auto flushUtf16 = [&] + { + if (!utf16Buf.empty()) + { + impl::UtfDecoder<impl::Char16> decoder(utf16Buf.c_str(), utf16Buf.size()); + while (std::optional<impl::CodePoint> cp = decoder.getNext()) + impl::codePointToUtf<char>(*cp, [&](char c) { output += c; }); + utf16Buf.clear(); + } + }; + auto writeOut = [&](char c) + { + flushUtf16(); + output += c; + }; + + for (auto it = str.begin(); it != str.end(); ++it) + { + const char c = *it; + + if (c == '\\') + { + ++it; + if (it == str.end()) //unexpected end! + { + writeOut(c); + break; + } + + const char c2 = *it; + if (c2 == '"' || + c2 == '\\' || + c2 == '/') + writeOut(c2); + else if (c2 == 'b') writeOut('\b'); + else if (c2 == 'f') writeOut('\f'); + else if (c2 == 'n') writeOut('\n'); + else if (c2 == 'r') writeOut('\r'); + else if (c2 == 't') writeOut('\t'); + + else if (c2 == 'u' && + str.end() - it >= 5 && + isHexDigit(it[1]) && + isHexDigit(it[2]) && + isHexDigit(it[3]) && + isHexDigit(it[4])) + { + utf16Buf += static_cast<impl::Char16>(static_cast<unsigned char>(unhexify(it[1], it[2])) * 256 + + static_cast<unsigned char>(unhexify(it[3], it[4]))); + it += 4; + } + else //unknown escape sequence! + { + writeOut(c); + writeOut(c2); + } + } + else + writeOut(c); + } + flushUtf16(); + return output; +} + + +void serialize(const JsonValue& jval, std::string& stream, + const std::string& lineBreak, + const std::string& indent, + size_t indentLevel) +{ + //unlike our XML serialization the caller is repsonsible for line breaks and indentation of *first* line + auto writeIndent = [&](size_t level) + { + for (size_t i = 0; i < level; ++i) + stream += indent; + }; + + switch (jval.type) + { + case JsonValue::Type::null: + stream += "null"; + break; + + case JsonValue::Type::boolean: + case JsonValue::Type::number: + stream += jval.primVal; + break; + + case JsonValue::Type::string: + stream += '"' + jsonEscape(jval.primVal) + '"'; + break; + + case JsonValue::Type::object: + stream += '{'; + if (!jval.objectVal.empty()) + { + for (auto it = jval.objectVal.begin(); it != jval.objectVal.end(); ++it) + { + const auto& [childName, childValue] = *it; + + if (it != jval.objectVal.begin()) + stream += ','; + + stream += lineBreak; + writeIndent(indentLevel + 1); + + stream += '"' + jsonEscape(childName) + "\":"; + + if ((childValue->type == JsonValue::Type::object && !childValue->objectVal.empty()) || + (childValue->type == JsonValue::Type::array && !childValue->arrayVal .empty())) + { + stream += lineBreak; + writeIndent(indentLevel + 1); + } + else if (!indent.empty()) + stream += ' '; + + serialize(*childValue, stream, lineBreak, indent, indentLevel + 1); + } + stream += lineBreak; + writeIndent(indentLevel); + } + stream += '}'; + break; + + case JsonValue::Type::array: + stream += '['; + if (!jval.arrayVal.empty()) + { + for (auto it = jval.arrayVal.begin(); it != jval.arrayVal.end(); ++it) + { + const auto& childValue = **it; + + if (it != jval.arrayVal.begin()) + stream += ','; + + stream += lineBreak; + writeIndent(indentLevel + 1); + + serialize(childValue, stream, lineBreak, indent, indentLevel + 1); + } + stream += lineBreak; + writeIndent(indentLevel); + } + stream += ']'; + break; + } +} +} +} + + +inline +std::string serializeJson(const JsonValue& jval, + const std::string& lineBreak, + const std::string& indent) //noexcept +{ + std::string output; + json_impl::serialize(jval, output, lineBreak, indent, 0); + output += lineBreak; + return output; +} + + +namespace json_impl +{ +struct Token +{ + enum class Type + { + eof, + curlyOpen, + curlyClose, + squareOpen, + squareClose, + colon, + comma, + string, // + number, //primitive types + boolean, // + null, // + }; + + Token(Type t) : type(t) {} + + Type type; + std::string primVal; //for primitive types +}; + +class Scanner +{ +public: + Scanner(const std::string& stream) : stream_(stream), pos_(stream_.begin()) + { + if (zen::startsWith(stream_, BYTE_ORDER_MARK_UTF8)) + pos_ += strLength(BYTE_ORDER_MARK_UTF8); + } + + Token getNextToken() //throw JsonParsingError + { + //skip whitespace + pos_ = std::find_if(pos_, stream_.end(), std::not_fn(isJsonWhiteSpace)); + + if (pos_ == stream_.end()) + return Token::Type::eof; + + if (*pos_ == '{') return ++pos_, Token::Type::curlyOpen; + if (*pos_ == '}') return ++pos_, Token::Type::curlyClose; + if (*pos_ == '[') return ++pos_, Token::Type::squareOpen; + if (*pos_ == ']') return ++pos_, Token::Type::squareClose; + if (*pos_ == ':') return ++pos_, Token::Type::colon; + if (*pos_ == ',') return ++pos_, Token::Type::comma; + if (startsWith("null")) return pos_ += 4, Token(Token::Type::null); + + if (startsWith("true")) + { + pos_ += 4; + Token tk(Token::Type::boolean); + tk.primVal = "true"; + return tk; + } + if (startsWith("false")) + { + pos_ += 5; + Token tk(Token::Type::boolean); + tk.primVal = "false"; + return tk; + } + + if (*pos_ == '"') + { + for (auto it = ++pos_; it != stream_.end(); ++it) + if (*it == '"') + { + Token tk(Token::Type::string); + tk.primVal = jsonUnescape({ pos_, it }); + pos_ = ++it; + return tk; + } + else if (*it == '\\') //skip next char + if (++it == stream_.end()) + break; + + throw JsonParsingError(posRow(), posCol()); + } + + //expect a number: + const auto itNumEnd = std::find_if(pos_, stream_.end(), std::not_fn(isJsonNumDigit)); + if (itNumEnd == pos_) + throw JsonParsingError(posRow(), posCol()); + + Token tk(Token::Type::number); + tk.primVal.assign(pos_, itNumEnd); + pos_ = itNumEnd; + return tk; + } + + size_t posRow() const //current row beginning with 0 + { + const size_t crSum = std::count(stream_.begin(), pos_, '\r'); //carriage returns + const size_t nlSum = std::count(stream_.begin(), pos_, '\n'); //new lines + assert(crSum == 0 || nlSum == 0 || crSum == nlSum); + return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win + } + + size_t posCol() const //current col beginning with 0 + { + //seek beginning of line + for (auto it = pos_; it != stream_.begin(); ) + { + --it; + if (*it == '\r' || *it == '\n') + return pos_ - it - 1; + } + return pos_ - stream_.begin(); + } + +private: + Scanner (const Scanner&) = delete; + Scanner& operator=(const Scanner&) = delete; + + static bool isJsonWhiteSpace(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } + static bool isJsonNumDigit (char c) { return ('0' <= c && c <= '9') || c == '-' || c == '+' || c == '.' || c == 'e'|| c == 'E'; } + + bool startsWith(const std::string& prefix) const + { + return zen::startsWith(StringRef<const char>(pos_, stream_.end()), prefix); + } + + const std::string stream_; + std::string::const_iterator pos_; +}; + + +class JsonParser +{ +public: + JsonParser(const std::string& stream) : + scn_(stream), + tk_(scn_.getNextToken()) {} //throw JsonParsingError + + JsonValue parse() //throw JsonParsingError + { + JsonValue jval = parseValue(); //throw JsonParsingError + expectToken(Token::Type::eof); // + return jval; + } + +private: + JsonParser (const JsonParser&) = delete; + JsonParser& operator=(const JsonParser&) = delete; + + JsonValue parseValue() //throw JsonParsingError + { + if (token().type == Token::Type::curlyOpen) + { + nextToken(); //throw JsonParsingError + + JsonValue jval(JsonValue::Type::object); + + if (token().type != Token::Type::curlyClose) + for (;;) + { + expectToken(Token::Type::string); //throw JsonParsingError + std::string name = token().primVal; + nextToken(); //throw JsonParsingError + + consumeToken(Token::Type::colon); //throw JsonParsingError + + JsonValue value = parseValue(); //throw JsonParsingError + jval.objectVal.emplace(std::move(name), std::make_unique<JsonValue>(std::move(value))); + + if (token().type != Token::Type::comma) + break; + nextToken(); //throw JsonParsingError + } + + consumeToken(Token::Type::curlyClose); //throw JsonParsingError + return jval; + } + else if (token().type == Token::Type::squareOpen) + { + nextToken(); //throw JsonParsingError + + JsonValue jval(JsonValue::Type::array); + + if (token().type != Token::Type::squareClose) + for (;;) + { + JsonValue value = parseValue(); //throw JsonParsingError + jval.arrayVal.emplace_back(std::make_unique<JsonValue>(std::move(value))); + + if (token().type != Token::Type::comma) + break; + nextToken(); //throw JsonParsingError + } + + consumeToken(Token::Type::squareClose); //throw JsonParsingError + return jval; + } + else if (token().type == Token::Type::string) + { + JsonValue jval(token().primVal); + nextToken(); //throw JsonParsingError + return jval; + } + else if (token().type == Token::Type::number) + { + JsonValue jval(JsonValue::Type::number); + jval.primVal = token().primVal; + nextToken(); //throw JsonParsingError + return jval; + } + else if (token().type == Token::Type::boolean) + { + JsonValue jval(JsonValue::Type::boolean); + jval.primVal = token().primVal; + nextToken(); //throw JsonParsingError + return jval; + } + else if (token().type == Token::Type::null) + { + nextToken(); //throw JsonParsingError + return JsonValue(); + } + else //unexpected token + throw JsonParsingError(scn_.posRow(), scn_.posCol()); + } + + const Token& token() const { return tk_; } + + void nextToken() { tk_ = scn_.getNextToken(); } //throw JsonParsingError + + void expectToken(Token::Type t) //throw JsonParsingError + { + if (token().type != t) + throw JsonParsingError(scn_.posRow(), scn_.posCol()); + } + + void consumeToken(Token::Type t) //throw JsonParsingError + { + expectToken(t); //throw JsonParsingError + nextToken(); // + } + + Scanner scn_; + Token tk_; +}; +} + +inline +JsonValue parseJson(const std::string& stream) //throw JsonParsingError +{ + return json_impl::JsonParser(stream).parse(); //throw JsonParsingError +} +} + +#endif //JSON_H_0187348321748321758934215734 diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index 54605945..54605945 100755..100644 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h diff --git a/zen/perf.h b/zen/perf.h index 77251f8c..77251f8c 100755..100644 --- a/zen/perf.h +++ b/zen/perf.h diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp index e925f142..e925f142 100755..100644 --- a/zen/process_priority.cpp +++ b/zen/process_priority.cpp diff --git a/zen/process_priority.h b/zen/process_priority.h index cfadfff1..cfadfff1 100755..100644 --- a/zen/process_priority.h +++ b/zen/process_priority.h diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 8d34f262..dc156a6f 100755..100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -45,7 +45,7 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError return true; } - throw FileError(errorMsg, formatSystemError(L"g_file_trash", L"Glib Error Code " + numberTo<std::wstring>(error->code), utfTo<std::wstring>(error->message))); + throw FileError(errorMsg, formatSystemError(L"g_file_trash", replaceCpy(_("Error Code %x"), L"%x", numberTo<std::wstring>(error->code)), utfTo<std::wstring>(error->message))); //g_quark_to_string(error->domain) } return true; diff --git a/zen/recycler.h b/zen/recycler.h index 0e5ebbb1..0e5ebbb1 100755..100644 --- a/zen/recycler.h +++ b/zen/recycler.h diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h index e3dbd55f..e3dbd55f 100755..100644 --- a/zen/ring_buffer.h +++ b/zen/ring_buffer.h diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 9eff6c1f..9eff6c1f 100755..100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h diff --git a/zen/serialize.h b/zen/serialize.h index 8b4c58ea..5a38303c 100755..100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -114,6 +114,7 @@ private: const IOCallback& notifyUnbufferedIO_; }; + //buffered input/output stream reference implementations: template <class BinContainer> struct MemoryStreamIn diff --git a/zen/shell_execute.h b/zen/shell_execute.h index 98824d70..98824d70 100755..100644 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h diff --git a/zen/shutdown.cpp b/zen/shutdown.cpp index a25e9c64..f09c4d07 100755..100644 --- a/zen/shutdown.cpp +++ b/zen/shutdown.cpp @@ -40,30 +40,10 @@ void zen::terminateProcess(int exitCode) } -/* -Command line alternatives: - -#ifdef ZEN_WIN -#ifdef ZEN_WIN_VISTA_AND_LATER - Shut down: shutdown /s /t 60 - Sleep: rundll32.exe powrprof.dll,SetSuspendState Sleep - Log off: shutdown /l -#else //XP - Shut down: shutdown -s -t 60 - Standby: rundll32.exe powrprof.dll,SetSuspendState //this triggers standby OR hibernate, depending on whether hibernate setting is active! no suspend on XP? - Log off: shutdown -l -#endif - -#elif defined ZEN_LINUX - Shut down: systemctl poweroff //alternative requiring admin: sudo shutdown -h 1 - Sleep: systemctl suspend //alternative requiring admin: sudo pm-suspend - Log off: gnome-session-quit --no-prompt - //alternative requiring admin: sudo killall Xorg - //alternative without admin: dbus-send --session --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.Logout uint32:1 - -#elif defined ZEN_MAC - Shut down: osascript -e 'tell application "System Events" to shut down' - Sleep: osascript -e 'tell application "System Events" to sleep' - Log off: osascript -e 'tell application "System Events" to log out' -#endif -*/ +//Command line alternatives: + //Shut down: systemctl poweroff //alternative requiring admin: sudo shutdown -h 1 + //Sleep: systemctl suspend //alternative requiring admin: sudo pm-suspend + //Log off: gnome-session-quit --no-prompt + // alternative requiring admin: sudo killall Xorg + // alternative without admin: dbus-send --session --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.Logout uint32:1 + diff --git a/zen/shutdown.h b/zen/shutdown.h index 2d66d1e8..2d66d1e8 100755..100644 --- a/zen/shutdown.h +++ b/zen/shutdown.h diff --git a/zen/socket.h b/zen/socket.h index 33ac2e50..c77a4084 100755..100644 --- a/zen/socket.h +++ b/zen/socket.h @@ -119,7 +119,7 @@ size_t tryWriteSocket(SocketType socket, const void* buffer, size_t bytesToWrite int bytesWritten = 0; for (;;) { - bytesWritten = ::send(socket, //_In_ SOCKET s, + bytesWritten = ::send(socket, //_In_ SOCKET s, static_cast<const char*>(buffer), //_In_ const char *buf, static_cast<int>(bytesToWrite), //_In_ int len, 0); //_In_ int flags diff --git a/zen/stl_tools.h b/zen/stl_tools.h index f1ab7c16..f1ab7c16 100755..100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h diff --git a/zen/string_base.h b/zen/string_base.h index 2247f93a..2247f93a 100755..100644 --- a/zen/string_base.h +++ b/zen/string_base.h diff --git a/zen/string_tools.h b/zen/string_tools.h index c3970d05..c3970d05 100755..100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h diff --git a/zen/string_traits.h b/zen/string_traits.h index 93cfd81c..93cfd81c 100755..100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 2393013e..2393013e 100755..100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h diff --git a/zen/sys_error.cpp b/zen/sys_error.cpp index 2acaca1d..2acaca1d 100755..100644 --- a/zen/sys_error.cpp +++ b/zen/sys_error.cpp diff --git a/zen/sys_error.h b/zen/sys_error.h index a087172f..a087172f 100755..100644 --- a/zen/sys_error.h +++ b/zen/sys_error.h diff --git a/zen/thread.cpp b/zen/thread.cpp index 08bfaa25..08bfaa25 100755..100644 --- a/zen/thread.cpp +++ b/zen/thread.cpp diff --git a/zen/thread.h b/zen/thread.h index 5e298ba1..52b85d7f 100755..100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -186,7 +186,7 @@ public: std::future<void> allDone = promiseDone->get_future(); notifyWhenDone([promiseDone] { promiseDone->set_value(); }); //std::function doesn't support construction involving move-only types! - //use reference? => not guaranteed safe, e.g. promise object theoretically might be accessed inside set_value() after future gets signalled + //use reference? => potential lifetime issue, e.g. promise object theoretically might be accessed inside set_value() after future gets signalled allDone.get(); } diff --git a/zen/time.h b/zen/time.h index a32e28e3..a32e28e3 100755..100644 --- a/zen/time.h +++ b/zen/time.h diff --git a/zen/type_traits.h b/zen/type_traits.h index 8783cb6a..8783cb6a 100755..100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h diff --git a/zen/utf.h b/zen/utf.h index 5a095874..5a095874 100755..100644 --- a/zen/utf.h +++ b/zen/utf.h diff --git a/zen/warn_static.h b/zen/warn_static.h index fb8fbb95..fb8fbb95 100755..100644 --- a/zen/warn_static.h +++ b/zen/warn_static.h diff --git a/zen/zlib_wrap.cpp b/zen/zlib_wrap.cpp index fbbe2f09..9eb4302f 100755..100644 --- a/zen/zlib_wrap.cpp +++ b/zen/zlib_wrap.cpp @@ -6,9 +6,9 @@ #include "zlib_wrap.h" //Windows: use the SAME zlib version that wxWidgets is linking against! //C:\Data\Projects\wxWidgets\Source\src\zlib\zlib.h -//Linux/macOS: use zlib system header for both wxWidgets and Curl (zlib is required for HTTP) +//Linux/macOS: use zlib system header for both wxWidgets and libcurl (zlib is required for HTTP) // => don't compile wxWidgets with: --with-zlib=builtin -#include <zlib.h> +#include <zlib.h> //https://www.zlib.net/manual.html using namespace zen; @@ -51,3 +51,78 @@ size_t zen::impl::zlib_decompress(const void* src, size_t srcLen, void* trg, siz throw ZlibInternalError(); return bufferSize; } + + +class InputStreamAsGzip::Impl +{ +public: + Impl(const std::function<size_t(void* buffer, size_t bytesToRead)>& readBlock /*throw X*/) : //throw ZlibInternalError; returning 0 signals EOF: Posix read() semantics + readBlock_(readBlock) + { + const int windowBits = MAX_WBITS + 16; //"add 16 to windowBits to write a simple gzip header" + + //"memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. + const int memLevel = 9; //test; 280 MB installer file: level 9 shrinks runtime by ~8% compared to level 8 (==DEF_MEM_LEVEL) at the cost of 128 KB extra memory + static_assert(memLevel <= MAX_MEM_LEVEL); + + const int rv = ::deflateInit2(&gzipStream_, //z_streamp strm + 3 /*see db_file.cpp*/, //int level + Z_DEFLATED, //int method + windowBits, //int windowBits + memLevel, //int memLevel + Z_DEFAULT_STRATEGY); //int strategy + if (rv != Z_OK) + throw ZlibInternalError(); + } + + ~Impl() + { + const int rv = ::deflateEnd(&gzipStream_); + assert(rv == Z_OK); + (void)rv; + } + + size_t read(void* buffer, size_t bytesToRead) //throw ZlibInternalError, X; return "bytesToRead" bytes unless end of stream! + { + if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check! + throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + + gzipStream_.next_out = static_cast<Bytef*>(buffer); + gzipStream_.avail_out = static_cast<uInt>(bytesToRead); + + for (;;) + { + if (gzipStream_.avail_in == 0 && !eof_) + { + if (bufIn_.size() < bytesToRead) + bufIn_.resize(bytesToRead); + + const size_t bytesRead = readBlock_(&bufIn_[0], bufIn_.size()); //throw X; returning 0 signals EOF: Posix read() semantics + gzipStream_.next_in = reinterpret_cast<z_const Bytef*>(&bufIn_[0]); + gzipStream_.avail_in = static_cast<uInt>(bytesRead); + if (bytesRead == 0) + eof_ = true; + } + + const int rv = ::deflate(&gzipStream_, eof_ ? Z_FINISH : Z_NO_FLUSH); + if (rv == Z_STREAM_END) + return bytesToRead - gzipStream_.avail_out; + if (rv != Z_OK) + throw ZlibInternalError(); + + if (gzipStream_.avail_out == 0) + return bytesToRead; + } + } + +private: + const std::function<size_t(void* buffer, size_t bytesToRead)> readBlock_; //throw X + bool eof_ = false; + std::vector<std::byte> bufIn_; + z_stream gzipStream_ = {}; +}; + + +zen::InputStreamAsGzip::InputStreamAsGzip(const std::function<size_t(void* buffer, size_t bytesToRead)>& readBlock /*throw X*/) : pimpl_(std::make_unique<Impl>(readBlock)) {} //throw ZlibInternalError +zen::InputStreamAsGzip::~InputStreamAsGzip() {} +size_t zen::InputStreamAsGzip::read(void* buffer, size_t bytesToRead) { return pimpl_->read(buffer, bytesToRead); } //throw ZlibInternalError, X diff --git a/zen/zlib_wrap.h b/zen/zlib_wrap.h index b92a8eba..c8647baf 100755..100644 --- a/zen/zlib_wrap.h +++ b/zen/zlib_wrap.h @@ -25,8 +25,19 @@ template <class BinContainer> BinContainer decompress(const BinContainer& stream); //throw ZlibInternalError +class InputStreamAsGzip //convert input stream into gzip on the fly +{ +public: + InputStreamAsGzip( //throw ZlibInternalError + const std::function<size_t(void* buffer, size_t bytesToRead)>& readBlock /*throw X*/); //returning 0 signals EOF: Posix read() semantics + ~InputStreamAsGzip(); + size_t read(void* buffer, size_t bytesToRead); //throw ZlibInternalError, X; return "bytesToRead" bytes unless end of stream! +private: + class Impl; + const std::unique_ptr<Impl> pimpl_; +}; @@ -52,9 +63,7 @@ BinContainer compress(const BinContainer& stream, int level) //throw ZlibInterna //save uncompressed stream size for decompression const uint64_t uncompressedSize = stream.size(); //use portable number type! contOut.resize(sizeof(uncompressedSize)); - std::copy(reinterpret_cast<const std::byte*>(&uncompressedSize), - reinterpret_cast<const std::byte*>(&uncompressedSize) + sizeof(uncompressedSize), - &*contOut.begin()); + std::memcpy(&*contOut.begin(), &uncompressedSize, sizeof(uncompressedSize)); const size_t bufferEstimate = impl::zlib_compressBound(stream.size()); //upper limit for buffer size, larger than input size!!! @@ -83,9 +92,9 @@ BinContainer decompress(const BinContainer& stream) //throw ZlibInternalError uint64_t uncompressedSize = 0; //use portable number type! if (stream.size() < sizeof(uncompressedSize)) throw ZlibInternalError(); - std::copy(&*stream.begin(), - &*stream.begin() + sizeof(uncompressedSize), - reinterpret_cast<std::byte*>(&uncompressedSize)); + + std::memcpy(&uncompressedSize, &*stream.begin(), sizeof(uncompressedSize)); + //attention: contOut MUST NOT be empty! Else it will pass a nullptr to zlib_decompress() => Z_STREAM_ERROR although "uncompressedSize == 0"!!! //secondary bug: don't dereference iterator into empty container! if (uncompressedSize == 0) //cannot be 0: compress() directly maps empty -> empty container skipping zlib! diff --git a/zen/zstring.cpp b/zen/zstring.cpp index f8a34045..62a0caef 100755..100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -57,7 +57,7 @@ Zstring getUnicodeNormalForm(const Zstring& str) // const char* precomposed = "\xc3\xb3"; try { - gchar* outStr = ::g_utf8_normalize (str.c_str(), str.length(), G_NORMALIZE_DEFAULT_COMPOSE); + gchar* outStr = ::g_utf8_normalize(str.c_str(), str.length(), G_NORMALIZE_DEFAULT_COMPOSE); if (!outStr) throw SysError(L"g_utf8_normalize: conversion failed. (" + utfTo<std::wstring>(str) + L")"); ZEN_ON_SCOPE_EXIT(::g_free(outStr)); diff --git a/zen/zstring.h b/zen/zstring.h index 6727253b..6727253b 100755..100644 --- a/zen/zstring.h +++ b/zen/zstring.h |