summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2018-05-09 00:01:58 +0200
committerDaniel Wilhelm <shieldwed@outlook.com>2018-05-09 00:01:58 +0200
commit725e6e3c8e52ede0edec0fa15da3f148bb2f7d74 (patch)
tree739061fe661dc0bd1d200b0378365f3f47ff78be /zen
parent9.3 (diff)
downloadFreeFileSync-725e6e3c8e52ede0edec0fa15da3f148bb2f7d74.tar.gz
FreeFileSync-725e6e3c8e52ede0edec0fa15da3f148bb2f7d74.tar.bz2
FreeFileSync-725e6e3c8e52ede0edec0fa15da3f148bb2f7d74.zip
9.4
Diffstat (limited to 'zen')
-rwxr-xr-xzen/basic_math.h53
-rwxr-xr-xzen/file_access.cpp11
-rwxr-xr-xzen/file_io.cpp6
-rwxr-xr-xzen/file_io.h5
-rwxr-xr-xzen/i18n.h68
-rwxr-xr-xzen/scope_guard.h2
-rwxr-xr-xzen/stl_tools.h17
-rwxr-xr-xzen/string_tools.h48
-rwxr-xr-xzen/time.h43
-rwxr-xr-xzen/xml_io.cpp2
10 files changed, 139 insertions, 116 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 722722a5..22f29ca7 100755
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -24,10 +24,9 @@ template <class T> T min(T a, T b, T c);
template <class T> T max(T a, T b, T c);
template <class T> bool isNull(T value);
-template <class T>
-void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal
-template <class T>
-T clampCpy(T val, T minVal, T maxVal);
+template <class T> void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal
+template <class T> T clampCpy(T val, T minVal, T maxVal);
+//std::clamp() available with C++17
template <class T, class InputIterator> //precondition: range must be sorted!
auto nearMatch(const T& val, InputIterator first, InputIterator last);
@@ -64,6 +63,8 @@ const double pi = 3.14159265358979323846;
const double e = 2.71828182845904523536;
const double sqrt2 = 1.41421356237309504880;
const double ln2 = 0.693147180559945309417;
+
+//static_assert(pi + e + sqrt2 + ln2 == 7.9672352249818781, "whoopsie");
//----------------------------------------------------------------------------------
@@ -286,17 +287,16 @@ template <class RandomAccessIterator> inline
double median(RandomAccessIterator first, RandomAccessIterator last) //note: invalidates input range!
{
const size_t n = last - first;
- if (n > 0)
- {
- std::nth_element(first, first + n / 2, last); //complexity: O(n)
- const double midVal = *(first + n / 2);
+ if (n == 0)
+ return 0;
- if (n % 2 != 0)
- return midVal;
- else //n is even and >= 2 in this context: return mean of two middle values
- return 0.5 * (*std::max_element(first, first + n / 2) + midVal); //this operation is the reason why median() CANNOT support a comparison predicate!!!
- }
- return 0;
+ std::nth_element(first, first + n / 2, last); //complexity: O(n)
+ const double midVal = *(first + n / 2);
+
+ if (n % 2 != 0)
+ return midVal;
+ else //n is even and >= 2 in this context: return mean of two middle values
+ return 0.5 * (*std::max_element(first, first + n / 2) + midVal); //this operation is the reason why median() CANNOT support a comparison predicate!!!
}
@@ -304,25 +304,22 @@ template <class RandomAccessIterator> inline
double mad(RandomAccessIterator first, RandomAccessIterator last) //note: invalidates input range!
{
//http://en.wikipedia.org/wiki/Median_absolute_deviation
-
const size_t n = last - first;
- if (n > 0)
- {
- const double m = median(first, last);
+ if (n == 0)
+ return 0;
- //the second median needs to operate on absolute residuals => avoid transforming input range which may have less than double precision!
+ const double m = median(first, last);
- auto lessMedAbs = [m](double lhs, double rhs) { return abs(lhs - m) < abs(rhs - m); };
+ //the second median needs to operate on absolute residuals => avoid transforming input range which may have less than double precision!
+ auto lessMedAbs = [m](double lhs, double rhs) { return abs(lhs - m) < abs(rhs - m); };
- std::nth_element(first, first + n / 2, last, lessMedAbs); //complexity: O(n)
- const double midVal = abs(*(first + n / 2) - m);
+ std::nth_element(first, first + n / 2, last, lessMedAbs); //complexity: O(n)
+ const double midVal = abs(*(first + n / 2) - m);
- if (n % 2 != 0)
- return midVal;
- else //n is even and >= 2 in this context: return mean of two middle values
- return 0.5 * (abs(*std::max_element(first, first + n / 2, lessMedAbs) - m) + midVal);
- }
- return 0;
+ if (n % 2 != 0)
+ return midVal;
+ else //n is even and >= 2 in this context: return mean of two middle values
+ return 0.5 * (abs(*std::max_element(first, first + n / 2, lessMedAbs) - m) + midVal);
}
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 69b6c388..e7467325 100755
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -14,8 +14,8 @@
#include "symlink_target.h"
#include "file_id_def.h"
#include "file_io.h"
-#include "crc.h"
-#include "guid.h"
+#include "crc.h" //boost dependency!
+#include "guid.h" //
#include <sys/vfs.h> //statfs
#include <sys/time.h> //lutimes
@@ -627,10 +627,13 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError,
//=> don't delete file that existed previously!!!
FileOutput fileOut(fdTarget, targetFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //pass ownership
- bufferedStreamCopy(fileIn, fileOut); //throw FileError, X
+ //fileOut.preAllocateSpaceBestEffort(sourceInfo.st_size); //throw FileError
+ //=> perf: seems like no real benefit...
+
+ bufferedStreamCopy(fileIn, fileOut); //throw FileError, (ErrorFileLocked), X
//flush intermediate buffers before fiddling with the raw file handle
- fileOut.flushBuffers(); //throw FileError, X
+ fileOut.flushBuffers(); //throw FileError, X
struct ::stat targetInfo = {};
if (::fstat(fileOut.getHandle(), &targetInfo) != 0)
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 60023849..68b41bcb 100755
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -103,7 +103,7 @@ FileInput::FileInput(const Zstring& filePath, const IOCallback& notifyUnbuffered
}
-size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; may return short, only 0 means EOF!
+size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked; may return short, only 0 means EOF!
{
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__));
@@ -127,7 +127,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; m
}
-size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; return "bytesToRead" bytes unless end of stream!
+size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
{
const size_t blockSize = getBlockSize();
assert(memBuf_.size() <= blockSize);
@@ -144,7 +144,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; r
break;
//--------------------------------------------------------------------
memBuf_.resize(blockSize);
- const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
+ const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
memBuf_.resize(bytesRead);
if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X
diff --git a/zen/file_io.h b/zen/file_io.h
index 6b0665a1..c2071500 100755
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -33,6 +33,7 @@ public:
//Windows: use 64kB ?? https://technet.microsoft.com/en-us/library/cc938632
//Linux: use st_blksize?
+ //macOS: use f_iosize?
static size_t getBlockSize() { return 128 * 1024; };
protected:
@@ -58,10 +59,10 @@ public:
FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorFileLocked
FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership!
- size_t read(void* buffer, size_t bytesToRead); //throw FileError, X; return "bytesToRead" bytes unless end of stream!
+ size_t read(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
private:
- size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0!
+ size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0!
std::vector<char> memBuf_;
const IOCallback notifyUnbufferedIO_; //throw X
diff --git a/zen/i18n.h b/zen/i18n.h
index c7d1d5dc..ebe22459 100755
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -16,8 +16,8 @@
//minimal layer enabling text translation - without platform/library dependencies!
#define ZEN_TRANS_CONCAT_SUB(X, Y) X ## Y
-#define _(s) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s))
-#define _P(s, p, n) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s), ZEN_TRANS_CONCAT_SUB(L, p), n)
+#define _(s) zen::translate(ZEN_TRANS_CONCAT_SUB(L, s))
+#define _P(s, p, n) zen::translate(ZEN_TRANS_CONCAT_SUB(L, s), ZEN_TRANS_CONCAT_SUB(L, p), n)
//source and translation are required to use %x as number placeholder
//for plural form, which will be substituted automatically!!!
@@ -58,61 +58,55 @@ std::shared_ptr<const TranslationHandler> getTranslator();
namespace implementation
{
inline
-std::wstring translate(const std::wstring& text)
+Global<const TranslationHandler>& refGlobalTranslationHandler()
{
- if (std::shared_ptr<const TranslationHandler> t = getTranslator()) //std::shared_ptr => temporarily take (shared) ownership while using the interface!
- return t->translate(text);
- return text;
+ //getTranslator() may be called even after static objects of this translation unit are destroyed!
+ static Global<const TranslationHandler> inst; //external linkage even in header!
+ return inst;
+}
}
-
-//translate plural forms: "%x day" "%x days"
-//returns "1 day" if n == 1; "123 days" if n == 123 for english language
inline
-std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n)
+std::shared_ptr<const TranslationHandler> getTranslator()
{
- assert(contains(plural, L"%x"));
-
- if (std::shared_ptr<const TranslationHandler> t = getTranslator())
- {
- std::wstring translation = t->translate(singular, plural, n);
- assert(!contains(translation, L"%x"));
- return translation;
- }
-
- return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n));
+ return implementation::refGlobalTranslationHandler().get();
}
-template <class T> inline
-std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n)
+inline
+void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler)
{
- static_assert(sizeof(n) <= sizeof(int64_t), "");
- return translate(singular, plural, static_cast<int64_t>(n));
+ implementation::refGlobalTranslationHandler().set(std::move(newHandler));
}
inline
-Global<const TranslationHandler>& refGlobalTranslationHandler()
+std::wstring translate(const std::wstring& text)
{
- //getTranslator() may be called even after static objects of this translation unit are destroyed!
- static Global<const TranslationHandler> inst; //external linkage even in header!
- return inst;
-}
+ if (std::shared_ptr<const TranslationHandler> t = getTranslator()) //std::shared_ptr => temporarily take (shared) ownership while using the interface!
+ return t->translate(text);
+ return text;
}
-inline
-void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler)
+//translate plural forms: "%x day" "%x days"
+//returns "1 day" if n == 1; "123 days" if n == 123 for english language
+template <class T> inline
+std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n)
{
- implementation::refGlobalTranslationHandler().set(std::move(newHandler));
-}
+ static_assert(sizeof(n) <= sizeof(int64_t), "");
+ const auto n64 = static_cast<int64_t>(n);
+ assert(contains(plural, L"%x"));
-inline
-std::shared_ptr<const TranslationHandler> getTranslator()
-{
- return implementation::refGlobalTranslationHandler().get();
+ if (std::shared_ptr<const TranslationHandler> t = getTranslator())
+ {
+ std::wstring translation = t->translate(singular, plural, n64);
+ assert(!contains(translation, L"%x"));
+ return translation;
+ }
+ //fallback:
+ return replaceCpy(std::abs(n64) == 1 ? singular : plural, L"%x", toGuiString(n));
}
}
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index f4ffc92b..ccc6db0c 100755
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -13,7 +13,7 @@
//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP
- static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
+ static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
namespace __cxxabiv1
{
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index b03d7533..2fcecd11 100755
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -39,9 +39,6 @@ void append(std::set<T, LessType, Alloc>& s, const C& c);
template <class KeyType, class ValueType, class LessType, class Alloc, class C>
void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c);
-template <class M, class K, class V>
-V& map_add_or_update(M& map, const K& key, const V& value); //efficient add or update without "default-constructible" requirement (Effective STL, item 24)
-
template <class T, class Alloc>
void removeDuplicates(std::vector<T, Alloc>& v);
@@ -125,20 +122,6 @@ template <class KeyType, class ValueType, class LessType, class Alloc, class C>
void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c) { m.insert(c.begin(), c.end()); }
-template <class M, class K, class V> inline
-V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or update without "default-constructible" requirement (Effective STL, item 24)
-{
- auto it = map.lower_bound(key);
- if (it != map.end() && !(map.key_comp()(key, it->first)))
- {
- it->second = value;
- return it->second;
- }
- else
- return map.insert(it, typename M::value_type(key, value))->second;
-}
-
-
template <class T, class Alloc> inline
void removeDuplicates(std::vector<T, Alloc>& v)
{
diff --git a/zen/string_tools.h b/zen/string_tools.h
index bfa14257..0a24ab2a 100755
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -22,10 +22,11 @@
//enhance arbitray string class with useful non-member functions:
namespace zen
{
-template <class Char> bool isWhiteSpace(Char ch);
-template <class Char> bool isDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only!
-template <class Char> bool isHexDigit (Char ch);
-template <class Char> bool isAsciiAlpha(Char ch);
+template <class Char> bool isWhiteSpace(Char c);
+template <class Char> bool isDigit (Char c); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only!
+template <class Char> bool isHexDigit (Char c);
+template <class Char> bool isAsciiAlpha(Char c);
+template <class Char> Char asciiToLower(Char c);
//case-sensitive comparison (compile-time correctness: use different number of arguments as STL comparison predicates!)
struct CmpBinary { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; };
@@ -105,28 +106,28 @@ template <class T, class S> T copyStringTo(S&& str);
//---------------------- implementation ----------------------
template <> inline
-bool isWhiteSpace(char ch)
+bool isWhiteSpace(char c)
{
- assert(ch != 0); //std C++ does not consider 0 as white space
+ assert(c != 0); //std C++ does not consider 0 as white space
//caveat 1: std::isspace() takes an int, but expects an unsigned char
//caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
- return static_cast<unsigned char>(ch) < 128 &&
- std::isspace(static_cast<unsigned char>(ch)) != 0;
+ return static_cast<unsigned char>(c) < 128 &&
+ std::isspace(static_cast<unsigned char>(c)) != 0;
}
template <> inline
-bool isWhiteSpace(wchar_t ch)
+bool isWhiteSpace(wchar_t c)
{
- assert(ch != 0); //std C++ does not consider 0 as white space
- return std::iswspace(ch) != 0;
+ assert(c != 0); //std C++ does not consider 0 as white space
+ return std::iswspace(c) != 0;
}
template <class Char> inline
-bool isDigit(Char ch) //similar to implmenetation of std::isdigit()!
+bool isDigit(Char c) //similar to implmenetation of std::isdigit()!
{
static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
- return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9');
+ return static_cast<Char>('0') <= c && c <= static_cast<Char>('9');
}
@@ -149,6 +150,15 @@ bool isAsciiAlpha(Char c)
}
+template <class Char> inline
+Char asciiToLower(Char c)
+{
+ if (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z'))
+ return static_cast<Char>(c - static_cast<Char>('A') + static_cast<Char>('a'));
+ return c;
+}
+
+
template <class S, class T, class Function> inline
bool startsWith(const S& str, const T& prefix, Function cmpStringFun)
{
@@ -458,19 +468,11 @@ int CmpBinary::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_
template <class Char> inline
int CmpAsciiNoCase::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const
{
- auto asciiToLower = [](Char c) //ordering: lower-case chars have higher code points than uppper-case
- {
- if (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z'))
- return static_cast<Char>(c - static_cast<Char>('A') + static_cast<Char>('a'));
- return c;
- };
-
const auto* const lhsLast = lhs + std::min(lhsLen, rhsLen);
-
while (lhs != lhsLast)
{
- const Char charL = asciiToLower(*lhs++);
- const Char charR = asciiToLower(*rhs++);
+ const Char charL = asciiToLower(*lhs++); //ordering: lower-case chars have higher code points than uppper-case
+ const Char charR = asciiToLower(*rhs++); //
if (charL != charR)
return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention!
//unsigned underflow is well-defined!
diff --git a/zen/time.h b/zen/time.h
index ace87024..3323a318 100755
--- a/zen/time.h
+++ b/zen/time.h
@@ -23,12 +23,19 @@ struct TimeComp //replaces std::tm and SYSTEMTIME
int second = 0; //0-60 (including leap second)
};
+inline bool operator==(const TimeComp& lhs, const TimeComp& rhs)
+{
+ return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day && lhs.hour == rhs.hour && lhs.minute == rhs.minute && lhs.second == rhs.second;
+}
+
TimeComp getLocalTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components
time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error
TimeComp getUtcTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to UTC time components
time_t utcToTimeT(const TimeComp& comp); //convert UTC time components to time_t (UTC), returns -1 on error
+TimeComp getCompileTime();
+
//----------------------------------------------------------------------------------------------------------------------------------
/*
@@ -268,6 +275,23 @@ time_t utcToTimeT(const TimeComp& comp) //returns -1 on error
}
+inline
+TimeComp getCompileTime()
+{
+ //https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
+ char compileTime[] = __DATE__ " " __TIME__; //e.g. "Aug 1 2017 01:32:26"
+ if (compileTime[4] == ' ') //day is space-padded, but %d expects zero-padding
+ compileTime[4] = '0';
+
+ TimeComp tc = {};
+ if (parseTime("%b %d %Y %H:%M:%S", compileTime, tc))
+ return tc;
+
+ assert(false);
+ return TimeComp();
+}
+
+
template <class String, class String2> inline
String formatTime(const String2& format, const TimeComp& comp)
{
@@ -328,6 +352,25 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur
if (!extractNumber(comp.month, 2))
return false;
break;
+ case 'b': //abbreviated month name: Jan-Dec
+ {
+ if (strLast - itStr < 3)
+ return false;
+
+ const char* months[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" };
+ auto itMonth = std::find_if(std::begin(months), std::end(months), [&](const char* name)
+ {
+ return asciiToLower(itStr[0]) == name[0] &&
+ asciiToLower(itStr[1]) == name[1] &&
+ asciiToLower(itStr[2]) == name[2];
+ });
+ if (itMonth == std::end(months))
+ return false;
+
+ comp.month = 1 + static_cast<int>(itMonth - std::begin(months));
+ itStr += 3;
+ }
+ break;
case 'd':
if (!extractNumber(comp.day, 2))
return false;
diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp
index 0a1c8505..8192d6e6 100755
--- a/zen/xml_io.cpp
+++ b/zen/xml_io.cpp
@@ -22,7 +22,7 @@ XmlDoc zen::loadXmlDocument(const Zstring& filePath) //throw FileError
for (;;)
{
buffer.resize(buffer.size() + blockSize);
- const size_t bytesRead = fileIn.read(&*(buffer.end() - blockSize), blockSize); //throw FileError, (X); return "bytesToRead" bytes unless end of stream!
+ const size_t bytesRead = fileIn.read(&*(buffer.end() - blockSize), blockSize); //throw FileError, ErrorFileLocked, (X); return "bytesToRead" bytes unless end of stream!
buffer.resize(buffer.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
//quick test whether input is an XML: avoid loading large binary files up front!
bgstack15