summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2019-02-10 16:47:23 -0500
committerB Stack <bgstack15@gmail.com>2019-02-10 16:47:23 -0500
commita48439992d4b1c896dd0beaff91d0a14361032b9 (patch)
tree475a67b0b138f2b1cd5f02eaab8e413f7eee62c6 /zen
parentMerge branch '10.8' into 'master' (diff)
downloadFreeFileSync-a48439992d4b1c896dd0beaff91d0a14361032b9.tar.gz
FreeFileSync-a48439992d4b1c896dd0beaff91d0a14361032b9.tar.bz2
FreeFileSync-a48439992d4b1c896dd0beaff91d0a14361032b9.zip
10.9
Diffstat (limited to 'zen')
-rw-r--r--zen/base64.h175
-rw-r--r--[-rwxr-xr-x]zen/basic_math.h0
-rw-r--r--[-rwxr-xr-x]zen/build_info.h0
-rw-r--r--[-rwxr-xr-x]zen/crc.h0
-rw-r--r--[-rwxr-xr-x]zen/dir_watcher.cpp0
-rw-r--r--[-rwxr-xr-x]zen/dir_watcher.h0
-rw-r--r--[-rwxr-xr-x]zen/error_log.h0
-rw-r--r--[-rwxr-xr-x]zen/file_access.cpp25
-rw-r--r--[-rwxr-xr-x]zen/file_access.h2
-rw-r--r--[-rwxr-xr-x]zen/file_error.h0
-rw-r--r--[-rwxr-xr-x]zen/file_id_def.h0
-rw-r--r--[-rwxr-xr-x]zen/file_io.cpp0
-rw-r--r--[-rwxr-xr-x]zen/file_io.h0
-rw-r--r--[-rwxr-xr-x]zen/file_traverser.cpp0
-rw-r--r--[-rwxr-xr-x]zen/file_traverser.h0
-rw-r--r--[-rwxr-xr-x]zen/format_unit.cpp0
-rw-r--r--[-rwxr-xr-x]zen/format_unit.h0
-rw-r--r--[-rwxr-xr-x]zen/globals.h0
-rw-r--r--[-rwxr-xr-x]zen/guid.h11
-rw-r--r--[-rwxr-xr-x]zen/http.cpp0
-rw-r--r--[-rwxr-xr-x]zen/http.h0
-rw-r--r--[-rwxr-xr-x]zen/i18n.h0
-rw-r--r--zen/json.h541
-rw-r--r--[-rwxr-xr-x]zen/legacy_compiler.h0
-rw-r--r--[-rwxr-xr-x]zen/perf.h0
-rw-r--r--[-rwxr-xr-x]zen/process_priority.cpp0
-rw-r--r--[-rwxr-xr-x]zen/process_priority.h0
-rw-r--r--[-rwxr-xr-x]zen/recycler.cpp2
-rw-r--r--[-rwxr-xr-x]zen/recycler.h0
-rw-r--r--[-rwxr-xr-x]zen/ring_buffer.h0
-rw-r--r--[-rwxr-xr-x]zen/scope_guard.h0
-rw-r--r--[-rwxr-xr-x]zen/serialize.h1
-rw-r--r--[-rwxr-xr-x]zen/shell_execute.h0
-rw-r--r--[-rwxr-xr-x]zen/shutdown.cpp34
-rw-r--r--[-rwxr-xr-x]zen/shutdown.h0
-rw-r--r--[-rwxr-xr-x]zen/socket.h2
-rw-r--r--[-rwxr-xr-x]zen/stl_tools.h0
-rw-r--r--[-rwxr-xr-x]zen/string_base.h0
-rw-r--r--[-rwxr-xr-x]zen/string_tools.h0
-rw-r--r--[-rwxr-xr-x]zen/string_traits.h0
-rw-r--r--[-rwxr-xr-x]zen/symlink_target.h0
-rw-r--r--[-rwxr-xr-x]zen/sys_error.cpp0
-rw-r--r--[-rwxr-xr-x]zen/sys_error.h0
-rw-r--r--[-rwxr-xr-x]zen/thread.cpp0
-rw-r--r--[-rwxr-xr-x]zen/thread.h2
-rw-r--r--[-rwxr-xr-x]zen/time.h0
-rw-r--r--[-rwxr-xr-x]zen/type_traits.h0
-rw-r--r--[-rwxr-xr-x]zen/utf.h0
-rw-r--r--[-rwxr-xr-x]zen/warn_static.h0
-rw-r--r--[-rwxr-xr-x]zen/zlib_wrap.cpp79
-rw-r--r--[-rwxr-xr-x]zen/zlib_wrap.h21
-rw-r--r--[-rwxr-xr-x]zen/zstring.cpp2
-rw-r--r--[-rwxr-xr-x]zen/zstring.h0
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
bgstack15