From fd00d1afae3831a44496a3beb708be3bad780e7d Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Mon, 6 Jul 2020 10:59:16 +1000 Subject: [PATCH 01/91] Debian packaging, manpage, fixed static build --- buildscripts/linux-static/build-deps.sh | 87 ++++++++++---------- debian/changelog | 5 ++ debian/compat | 1 + debian/control | 19 +++++ debian/copyright | 2 + debian/icons/1024x1024/ricochet-refresh.png | Bin 0 -> 29835 bytes debian/icons/128x128/ricochet-refresh.png | Bin 0 -> 3403 bytes debian/icons/16x16/ricochet-refresh.png | Bin 0 -> 991 bytes debian/icons/192x192/ricochet-refresh.png | Bin 0 -> 4862 bytes debian/icons/24x24/ricochet-refresh.png | Bin 0 -> 1261 bytes debian/icons/256x256/ricochet-refresh.png | Bin 0 -> 6432 bytes debian/icons/32x32/ricochet-refresh.png | Bin 0 -> 1526 bytes debian/icons/48x48/ricochet-refresh.png | Bin 0 -> 1952 bytes debian/icons/512x512/ricochet-refresh.png | Bin 0 -> 13373 bytes debian/icons/64x64/ricochet-refresh.png | Bin 0 -> 1934 bytes debian/icons/96x96/ricochet-refresh.png | Bin 0 -> 2614 bytes debian/install | 2 + debian/manpages | 1 + debian/rules | 11 +++ debian/shlibs.local | 1 + doc/manpage | 28 +++++++ src/ricochet-refresh.desktop | 2 +- 22 files changed, 116 insertions(+), 43 deletions(-) create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/icons/1024x1024/ricochet-refresh.png create mode 100644 debian/icons/128x128/ricochet-refresh.png create mode 100644 debian/icons/16x16/ricochet-refresh.png create mode 100644 debian/icons/192x192/ricochet-refresh.png create mode 100644 debian/icons/24x24/ricochet-refresh.png create mode 100644 debian/icons/256x256/ricochet-refresh.png create mode 100644 debian/icons/32x32/ricochet-refresh.png create mode 100644 debian/icons/48x48/ricochet-refresh.png create mode 100644 debian/icons/512x512/ricochet-refresh.png create mode 100644 debian/icons/64x64/ricochet-refresh.png create mode 100644 debian/icons/96x96/ricochet-refresh.png create mode 100644 debian/install create mode 100644 debian/manpages create mode 100755 debian/rules create mode 100644 debian/shlibs.local create mode 100644 doc/manpage diff --git a/buildscripts/linux-static/build-deps.sh b/buildscripts/linux-static/build-deps.sh index 3804e3b..8fd19bb 100755 --- a/buildscripts/linux-static/build-deps.sh +++ b/buildscripts/linux-static/build-deps.sh @@ -22,12 +22,36 @@ pushd "$ROOT_SRC" git submodule update --init qtbase qtdeclarative qtgraphicaleffects qtimageformats qtquickcontrols qtsvg qtx11extras qttools qtmultimedia git submodule foreach --recursive git reset --hard git reset --hard + # TODO: + # Find which configure options can be included to compile QT without breaking the compilation + # Missing configure options + # -no-feature-bearermanagement \ + # -no-feature-big_codecs \ + # -no-feature-calendarwidget \ + # -no-feature-codecs \ + # -no-feature-colordialog \ + # -no-feature-colornames \ # -no-feature-concurrent \ + # -no-feature-completer \ + # -no-feature-cups \ + # -no-feature-datawidgetmapper \ # -no-feature-dbus \ + # -no-feature-desktopservices \ # -no-feature-dom \ + # -no-feature-effects \ # -no-feature-errormessage \ + # -no-feature-filedialog \ + # -no-feature-filesystemmodel \ + # -no-feature-filesystemwatcher \ + # -no-feature-fontdialog \ # -no-feature-freetype \ + # -no-feature-fscompleter \ + # -no-feature-ftp \ + # -no-feature-gestures \ + # -no-feature-graphicseffect \ + # -no-feature-graphicsview \ + # -no-feature-iconv \ # -no-feature-im \ # -no-feature-image_heuristic_mask \ # -no-feature-image_text \ @@ -36,10 +60,31 @@ pushd "$ROOT_SRC" # -no-feature-imageformat_png \ # -no-feature-imageformat_ppm \ # -no-feature-imageformat_xbm \ + # -no-feature-inputdialog \ + # -no-feature-keysequenceedit \ # -no-feature-mimetype \ + # -no-feature-networkdiskcache \ + # -no-feature-networkproxy \ + # -no-feature-paint_debug \ + # -no-feature-printpreviewdialog \ + # -no-feature-printpreviewwidget \ # -no-feature-process \ + # -no-feature-progressdialog \ # -no-feature-sharedmemory \ + # -no-feature-sizegrip \ + # -no-feature-socks5 \ + # -no-feature-statemachine \ # -no-feature-systemsemaphore \ + # -no-feature-systemtrayicon \ + # -no-feature-texthtmlparser \ + # -no-feature-textodfwriter \ + # -no-feature-translation \ + # -no-feature-udpsocket \ + # -no-feature-undocommand \ + # -no-feature-undogroup \ + # -no-feature-undostack \ + # -no-feature-undoview \ + # -no-feature-wizard \ # -skip qtdeclarative \ # -skip qtimageformats \ # -skip qtmultimedia \ @@ -55,48 +100,6 @@ pushd "$ROOT_SRC" -fontconfig \ -no-compile-examples \ -no-cups \ - -no-feature-bearermanagement \ - -no-feature-big_codecs \ - -no-feature-calendarwidget \ - -no-feature-codecs \ - -no-feature-colordialog \ - -no-feature-colornames \ - -no-feature-completer \ - -no-feature-cups \ - -no-feature-datawidgetmapper \ - -no-feature-desktopservices \ - -no-feature-effects \ - -no-feature-filedialog \ - -no-feature-filesystemmodel \ - -no-feature-filesystemwatcher \ - -no-feature-fontdialog \ - -no-feature-fscompleter \ - -no-feature-ftp \ - -no-feature-gestures \ - -no-feature-graphicseffect \ - -no-feature-graphicsview \ - -no-feature-iconv \ - -no-feature-inputdialog \ - -no-feature-keysequenceedit \ - -no-feature-networkdiskcache \ - -no-feature-networkproxy \ - -no-feature-paint_debug \ - -no-feature-printpreviewdialog \ - -no-feature-printpreviewwidget \ - -no-feature-progressdialog \ - -no-feature-sizegrip \ - -no-feature-socks5 \ - -no-feature-statemachine \ - -no-feature-systemtrayicon \ - -no-feature-texthtmlparser \ - -no-feature-textodfwriter \ - -no-feature-translation \ - -no-feature-udpsocket \ - -no-feature-undocommand \ - -no-feature-undogroup \ - -no-feature-undostack \ - -no-feature-undoview \ - -no-feature-wizard \ -no-openssl \ -no-qml-debug \ -nomake examples \ diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..8e54d58 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +ricochet-refresh (1.1.4) focal; urgency=medium + + * debian packaging + + -- Marco Simonelli Wed, 01 Jul 2020 16:14:29 +1000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +10 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..c18db1f --- /dev/null +++ b/debian/control @@ -0,0 +1,19 @@ +Source: ricochet-refresh +Section: net +Priority: optional +Maintainer: Marco Simonelli +Build-Depends: debhelper (>= 9), pkg-config, libevent-dev, libssl-dev, libprotobuf-dev, protobuf-compiler, qtbase5-dev, qtdeclarative5-dev, qttools5-dev-tools, tor, make +Standards-Version: 4.4.1 +Homepage: https://ricochetrefresh.net/ + +Package: ricochet-refresh +Architecture: any +Depends: tor, ${shlibs:Depends}, ${misc:Depends}, + qml-module-qtgraphicaleffects, + qml-module-qtquick-controls, + qml-module-qtquick-layouts, + qml-module-qtquick-dialogs +Description: Anonymous metadata-resistant instant messaging + Ricochet refresh is an open source project to allow private and anonymous + instant messaging. It is metadata-resistant, anonymous, decentralised and + easy to use. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..b51a5fa --- /dev/null +++ b/debian/copyright @@ -0,0 +1,2 @@ +Copyright (C) 2019-2020, Blueprint For Free Speech +Copyright (C) 2014-2016, John Brooks \ No newline at end of file diff --git a/debian/icons/1024x1024/ricochet-refresh.png b/debian/icons/1024x1024/ricochet-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..4c98412537ce3cf3af166532a6290a1dc4a17142 GIT binary patch literal 29835 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 &PLy5Hy-2&bpw;)g{^ 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 ZIrRVj!1to#2&wow+fBdctYQA~e*gn}F5UnD literal 0 HcmV?d00001 diff --git a/debian/icons/128x128/ricochet-refresh.png b/debian/icons/128x128/ricochet-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..e0a0a31254b337a44fdc45b3b2ccc91f229871e0 GIT binary patch literal 3403 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 M>FVdQ&MBb@0C$;-F#rGn literal 0 HcmV?d00001 diff --git a/debian/icons/192x192/ricochet-refresh.png b/debian/icons/192x192/ricochet-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..5790df2830df87ed7e3a553b82a0ce510cea2b1c GIT binary patch literal 4862 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 literal 0 HcmV?d00001 diff --git a/debian/icons/256x256/ricochet-refresh.png b/debian/icons/256x256/ricochet-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..18931de4b29f5e0989e1bc9e840a56ff8433e432 GIT binary patch literal 6432 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 literal 0 HcmV?d00001 diff --git a/debian/icons/32x32/ricochet-refresh.png b/debian/icons/32x32/ricochet-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..20feb00d313a551e769810edcedfb6689442bd8b GIT binary patch literal 1526 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

7Z;>0S3|c^3Eb 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 literal 0 HcmV?d00001 diff --git a/debian/icons/64x64/ricochet-refresh.png b/debian/icons/64x64/ricochet-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..04b7ad70e5555b88f2b290a4c68d72faaa93351d GIT binary patch literal 1934 zcmV;92XXj`P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 literal 0 HcmV?d00001 diff --git a/debian/install b/debian/install new file mode 100644 index 0000000..6740424 --- /dev/null +++ b/debian/install @@ -0,0 +1,2 @@ +src/ricochet-refresh.desktop usr/share/applications +ricochet-refresh usr/bin \ No newline at end of file diff --git a/debian/manpages b/debian/manpages new file mode 100644 index 0000000..49f06bf --- /dev/null +++ b/debian/manpages @@ -0,0 +1 @@ +doc/manpage \ No newline at end of file diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..8763c0c --- /dev/null +++ b/debian/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f +export QT_SELECT=qt5 + +%: + dh $@ --buildsystem=qmake --parallel + +override_dh_auto_configure: + dh_auto_configure $(AC_OPTS) + +override_dh_auto_install: + dh_auto_install \ No newline at end of file diff --git a/debian/shlibs.local b/debian/shlibs.local new file mode 100644 index 0000000..40d74e8 --- /dev/null +++ b/debian/shlibs.local @@ -0,0 +1 @@ +libprotobuf 17 diff --git a/doc/manpage b/doc/manpage new file mode 100644 index 0000000..4093c40 --- /dev/null +++ b/doc/manpage @@ -0,0 +1,28 @@ +.\" Manpage for Ricochet Refresh +.\" Submit a PR on the github repository for typos/errors (https://github.com/blueprint-freespeech/ricochet-refresh.git) +.Dd $Mdocdate$ +.Dt ricochet-refresh 1 +.Os +.Sh NAME +.Nm ricochet-refresh +.Nd Anonymous metadata-resistant instant messaging +.Sh SYNOPSIS +.Nm ricochet-refresh +.Op options +.Sh DESCRIPTION +.Nm +is an open source project to allow private and anonymous +instant messaging. It is metadata-resistant, anonymous and decentralised. +.Sh OPTIONS +.Nm +takes up to 1 argument, the ricochet folder path (relative to the current +working directory). If this folder does not exist, ricochet will create the +folder and inside that folder will create files containing tor keys and +contacts. If no argument is given, ricochet will create a config folder at +QStandardPaths::DataLocation (see +https://doc.qt.io/qt-5/qstandardpaths.html#StandardLocation-enum). +.Sh AUTHOR +.Nm +has been written by John Brooks and is +currently maintained by Blueprint For Free Speech + \ No newline at end of file diff --git a/src/ricochet-refresh.desktop b/src/ricochet-refresh.desktop index 55b3890..98e0802 100644 --- a/src/ricochet-refresh.desktop +++ b/src/ricochet-refresh.desktop @@ -5,4 +5,4 @@ Name=Ricochet Refresh Categories=Network;InstantMessaging;Chat;FileTransfer;Qt Comment=Anonymous instant messaging Icon=ricochet_refresh -Exec=ricochet +Exec=ricochet-refresh From e2e359b8302e7096eab8bdb9e684e01dc78ff588 Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Mon, 6 Jul 2020 11:17:38 +1000 Subject: [PATCH 02/91] Fix version number for debian packages Version 1.x.x is already in use by ricochet legacy. Version 2.x.x should be left in case legacy is updated (+major revision no corresponds to tor version) Ricochet refresh should instead use version 3.x.x --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 8e54d58..a35590a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -ricochet-refresh (1.1.4) focal; urgency=medium +ricochet-refresh (3.0.0) focal; urgency=medium * debian packaging From d6a2dc81e1d7faa9c33086bb69afe6500c23cb66 Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Mon, 6 Jul 2020 11:33:10 +1000 Subject: [PATCH 03/91] Bump version number to 3.0.0 in source, update deprecated QT usage QStandardPaths::DataLocation is deprecated, QStandardPaths::AppDataLocation should be used instead --- debian/changelog | 2 +- doc/manpage | 2 +- ricochet.pro | 2 +- src/main.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/debian/changelog b/debian/changelog index 8e54d58..a35590a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -ricochet-refresh (1.1.4) focal; urgency=medium +ricochet-refresh (3.0.0) focal; urgency=medium * debian packaging diff --git a/doc/manpage b/doc/manpage index 4093c40..b7090b1 100644 --- a/doc/manpage +++ b/doc/manpage @@ -19,7 +19,7 @@ takes up to 1 argument, the ricochet folder path (relative to the current working directory). If this folder does not exist, ricochet will create the folder and inside that folder will create files containing tor keys and contacts. If no argument is given, ricochet will create a config folder at -QStandardPaths::DataLocation (see +QStandardPaths::AppDataLocation (see https://doc.qt.io/qt-5/qstandardpaths.html#StandardLocation-enum). .Sh AUTHOR .Nm diff --git a/ricochet.pro b/ricochet.pro index 381dcef..1d19691 100644 --- a/ricochet.pro +++ b/ricochet.pro @@ -38,7 +38,7 @@ TEMPLATE = app QT += core gui network quick widgets CONFIG += c++11 -VERSION = 1.1.4 +VERSION = 3.0.0 # Use CONFIG+=no-hardened to disable compiler hardening options !CONFIG(no-hardened) { diff --git a/src/main.cpp b/src/main.cpp index 45a1bb5..554c599 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) qputenv("QMLSCENE_DEVICE", "softwarecontext"); QApplication a(argc, argv); - a.setApplicationVersion(QLatin1String("1.1.4")); + a.setApplicationVersion(QLatin1String("3.0.0")); a.setOrganizationName(QStringLiteral("Ricochet")); #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) @@ -118,7 +118,7 @@ int main(int argc, char *argv[]) static QString userConfigPath() { - QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString oldPath = path; oldPath.replace(QStringLiteral("Ricochet"), QStringLiteral("Torsion"), Qt::CaseInsensitive); if (QFile::exists(oldPath)) From cac4cd19f8af2655c955ca8da56c2317e30c1250 Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Mon, 6 Jul 2020 11:37:57 +1000 Subject: [PATCH 04/91] Add ricochet-refresh to the gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fbd81fc..d4b120a 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ moc_predefs.h \.vscode/ notes/* + +ricochet-refresh From 84c5cc48485905fd673b72b20f8cc509dc1cff26 Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Mon, 6 Jul 2020 11:43:17 +1000 Subject: [PATCH 05/91] Update ricochet refresh to have a new CFBundleIdentifier CFBundleSignature does not need to be updated as versions of macOS >10.7 ignore this field --- src/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Info.plist b/src/Info.plist index 1e90fbd..802c9ad 100644 --- a/src/Info.plist +++ b/src/Info.plist @@ -13,7 +13,7 @@ CFBundleExecutable ricochet-refresh CFBundleIdentifier - im.ricochet + net.ricochetrefresh CFBundleName Ricochet-Refresh NSSupportsAutomaticGraphicsSwitching From 37c36d9ff82cadc7f648b5a3b05ab384beb7a108 Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Tue, 7 Jul 2020 11:05:14 +1000 Subject: [PATCH 06/91] Base32 tests --- src/utils/CryptoKey.cpp | 48 ++++++++++--------- tests/tst_cryptokey/tst_cryptokey.cpp | 68 +++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 21 deletions(-) diff --git a/src/utils/CryptoKey.cpp b/src/utils/CryptoKey.cpp index 8aa0357..b7b2599 100644 --- a/src/utils/CryptoKey.cpp +++ b/src/utils/CryptoKey.cpp @@ -389,27 +389,28 @@ QByteArray torControlHashedPassword(const QByteArray &password) #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" -/* Implements base32 encoding as in rfc3548. Requires that srclen*8 is a multiple of 5. */ +/* Implements base32 encoding as in rfc3548. */ void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen) { unsigned i, bit, v, u; unsigned nbits = srclen * 8; - /* We need an even multiple of 5 bits, and enough space */ - if ((nbits%5) != 0 || destlen > (nbits/5)+1) { + /* We need enough space */ + if(!(((nbits + 5 - 1) / 5) + 1 <= destlen)) { Q_ASSERT(false); - memset(dest, 0, destlen); return; } + memset(dest, 0, destlen); + for (i = 0, bit = 0; bit < nbits; ++i, bit += 5) { /* set v to the 16-bit value starting at src[bits/8], 0-padded. */ - v = ((quint8) src[bit / 8]) << 8; - if (bit + 5 < nbits) - v += (quint8) src[(bit/8)+1]; - - /* set u to the 5-bit value at the bit'th bit of src. */ + size_t idx = bit / 8; + v = ((uint8_t)src[idx]) << 8; + if (idx + 1 < srclen) + v += (uint8_t)src[idx + 1]; + /* set u to the 5-bit value at the bit'th bit of buf. */ u = (v >> (11 - (bit % 8))) & 0x1F; dest[i] = BASE32_CHARS[u]; } @@ -417,19 +418,19 @@ void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srcle dest[i] = '\0'; } -/* Implements base32 decoding as in rfc3548. Requires that srclen*5 is a multiple of 8. */ +/* Implements base32 decoding as in rfc3548. */ bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen) { unsigned int i, j, bit; - unsigned nbits = srclen * 5; + unsigned nbits = ((srclen * 5) / 8) * 8; - /* We need an even multiple of 8 bits, and enough space */ - if ((nbits%8) != 0 || (nbits/8)+1 > destlen) { + /* We need enough space */ + if(!(nbits/8 <= destlen)) { Q_ASSERT(false); return false; } - char *tmp = new char[srclen]; + char *tmp = new char[srclen](); /* Convert base32 encoded chars to the 5-bit values that they represent. */ for (j = 0; j < srclen; ++j) @@ -453,21 +454,26 @@ bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srcle switch (bit % 40) { case 0: - dest[i] = (((quint8)tmp[(bit/5)]) << 3) + (((quint8)tmp[(bit/5)+1]) >> 2); + dest[i] = (((quint8)tmp[(bit/5)]) << 3) + + (((quint8)tmp[(bit/5)+1]) >> 2); break; case 8: - dest[i] = (((quint8)tmp[(bit/5)]) << 6) + (((quint8)tmp[(bit/5)+1]) << 1) - + (((quint8)tmp[(bit/5)+2]) >> 4); + dest[i] = (((quint8)tmp[(bit/5)]) << 6) + + (((quint8)tmp[(bit/5)+1]) << 1) + + (((quint8)tmp[(bit/5)+2]) >> 4); break; case 16: - dest[i] = (((quint8)tmp[(bit/5)]) << 4) + (((quint8)tmp[(bit/5)+1]) >> 1); + dest[i] = (((quint8)tmp[(bit/5)]) << 4) + + (((quint8)tmp[(bit/5)+1]) >> 1); break; case 24: - dest[i] = (((quint8)tmp[(bit/5)]) << 7) + (((quint8)tmp[(bit/5)+1]) << 2) - + (((quint8)tmp[(bit/5)+2]) >> 3); + dest[i] = (((quint8)tmp[(bit/5)]) << 7) + + (((quint8)tmp[(bit/5)+1]) << 2) + + (((quint8)tmp[(bit/5)+2]) >> 3); break; case 32: - dest[i] = (((quint8)tmp[(bit/5)]) << 5) + ((quint8)tmp[(bit/5)+1]); + dest[i] = (((quint8)tmp[(bit/5)]) << 5) + + ((quint8)tmp[(bit/5)+1]); break; } } diff --git a/tests/tst_cryptokey/tst_cryptokey.cpp b/tests/tst_cryptokey/tst_cryptokey.cpp index 1b2f070..79c5c04 100644 --- a/tests/tst_cryptokey/tst_cryptokey.cpp +++ b/tests/tst_cryptokey/tst_cryptokey.cpp @@ -31,7 +31,14 @@ */ #include +#include #include "utils/CryptoKey.h" +#include "utils/SecureRNG.h" + +#include + +void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen); +bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen); class TestCryptoKey : public QObject { @@ -44,6 +51,7 @@ private slots: void encodedPrivateKey(); void torServiceID(); void sign(); + void testBase32(); }; const char *alice = @@ -224,5 +232,65 @@ void TestCryptoKey::sign() QVERIFY(key.verifySHA256(dataDigest, signaturep)); } +void TestCryptoKey::testBase32() +{ + /* Base32 encode */ + + char *data1 = new char[64](); + char *data2 = new char[64](); + + /* test different length inputs */ + strcpy(data1, "test0"); + base32_encode(data2, 9, data1, 5); + QCOMPARE(QString::fromLocal8Bit(data2), QString("orsxg5bq")); + + strcpy(data1, "\xff\xf5\x6d\x44\xae\x0d\x5c\xc9\x62\xc4"); + base32_encode(data2, 17, data1, 10); + QCOMPARE(QString::fromLocal8Bit(data2), QString("772w2rfobvomsywe")); + + strcpy(data1, "\x18\x78\xa6\xd3\x32\xc8\xce\x91\x59\xaa\xc7\x38\x87\x37\x84"); + base32_encode(data2, 35, data1, 15); + QCOMPARE(QString::fromLocal8Bit(data2), QString("db4knuzszdhjcwnky44ion4e")); + + /* test padding */ + strcpy(data1, "\x2e\xd0\x63\xe2\x5b\x16\xdf\xc4\xfc\x01\x23\xc9\xeb\xf6\x83\x71\xe4\x8e\xa0\x1c\x08\x65\xab\xb2\x58\x3c\xd5\xd3\x60"); + base32_encode(data2, 49, data1, 30); + QCOMPARE(QString::fromLocal8Bit(data2), QString("f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhtk5gyaa")); + + /* test non multiple of 5 length input */ + strcpy(data1, "\x2e\xd0\x63\xe2\x5b\x16\xdf\xc4\xfc\x01\x23\xc9\xeb\xf6\x83\x71\xe4\x8e\xa0\x1c\x08\x65\xab\xb2\x58\x3c"); + base32_encode(data2, 49, data1, 27); + QCOMPARE(QString::fromLocal8Bit(data2), QString("f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhqaa")); + + delete[] data1; + delete[] data2; + + /* Base32 decode */ + char *rnd_bytes = new char[60](); + char *encoded = new char[97](); + char *decoded = new char[60](); + + /* encode and decode random bytes */ + SecureRNG::random(rnd_bytes, 60); + base32_encode(encoded, 97, rnd_bytes, 60); + QVERIFY(base32_decode(decoded, 60, encoded, 96)); + QCOMPARE(QString::fromLocal8Bit(rnd_bytes), QString::fromLocal8Bit(decoded)); + + /* test that decoding works on uppercase strings */ + for(char *c = encoded; *c != 0; c++) *c = std::toupper(*c); + QVERIFY(base32_decode(decoded, 60, encoded, 96)); + QCOMPARE(QString::fromLocal8Bit(rnd_bytes), QString::fromLocal8Bit(decoded)); + + /* change the encoded string and test again */ + if(encoded[0] == 'a' || encoded[0] == 'A') encoded[0] = 'B'; + else encoded[0] = 'A'; + QVERIFY(base32_decode(decoded, 60, encoded, 96)); + QVERIFY(QString::fromLocal8Bit(rnd_bytes).compare(QString::fromLocal8Bit(decoded)) != 0); + + /* bad base32 encoded string */ + encoded[0] = '@'; + QVERIFY(base32_decode(decoded, 60, encoded, 96) == false); +} + QTEST_MAIN(TestCryptoKey) #include "tst_cryptokey.moc" From 135b712741609bb792b89b6fdb7526c05b1a41fc Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Fri, 10 Jul 2020 15:26:26 +1000 Subject: [PATCH 07/91] Fix implicit operator declaration warning As of C++11, implicit declarations of operators are deprecated. Previous to this, implicit declarations of operators would function the same as: Class& operator(const Class&) = default; Explicitly declaring an operator as above will avoid this warning and maintain compatability with future C++ standards --- src/utils/CryptoKey.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/CryptoKey.h b/src/utils/CryptoKey.h index 70ada19..e6f16fc 100644 --- a/src/utils/CryptoKey.h +++ b/src/utils/CryptoKey.h @@ -54,6 +54,8 @@ class CryptoKey CryptoKey(const CryptoKey &other) : d(other.d) { } ~CryptoKey(); + CryptoKey& operator=(const CryptoKey&) = default; + bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM); bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM); void clear(); From d69de66fa4a32339a0e68c3002e92740f7fd7e84 Mon Sep 17 00:00:00 2001 From: m-simonelli <60584156+m-simonelli@users.noreply.github.com> Date: Sun, 12 Jul 2020 11:14:52 +1000 Subject: [PATCH 08/91] Update rules --- debian/rules | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/debian/rules b/debian/rules index 8763c0c..d440676 100755 --- a/debian/rules +++ b/debian/rules @@ -4,8 +4,8 @@ export QT_SELECT=qt5 %: dh $@ --buildsystem=qmake --parallel -override_dh_auto_configure: - dh_auto_configure $(AC_OPTS) +#override_dh_auto_configure: +# dh_auto_configure $(AC_OPTS) -override_dh_auto_install: - dh_auto_install \ No newline at end of file +#override_dh_auto_install: +# dh_auto_install From 7bf4157bf7083d4434bb361dbc95880ec6fdb0f8 Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Tue, 21 Jul 2020 11:54:44 +1000 Subject: [PATCH 09/91] Fix base32 + unit tests --- src/utils/CryptoKey.cpp | 14 ++++++++------ src/utils/CryptoKey.h | 7 +++++++ tests/tst_cryptokey/tst_cryptokey.cpp | 26 ++++++++++++++++++++++---- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/utils/CryptoKey.cpp b/src/utils/CryptoKey.cpp index b7b2599..c0feb8c 100644 --- a/src/utils/CryptoKey.cpp +++ b/src/utils/CryptoKey.cpp @@ -389,14 +389,14 @@ QByteArray torControlHashedPassword(const QByteArray &password) #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" -/* Implements base32 encoding as in rfc3548. */ +/* Implements base32 encoding as in rfc3548, except for padding. This implementation uses null bytes for padding */ void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen) { unsigned i, bit, v, u; unsigned nbits = srclen * 8; - /* We need enough space */ - if(!(((nbits + 5 - 1) / 5) + 1 <= destlen)) { + /* We need enough space, check that the encoded length of src is not greater than destlen*/ + if(CryptoKey::base32_encoded_size(srclen) > destlen) { Q_ASSERT(false); return; } @@ -415,7 +415,7 @@ void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srcle dest[i] = BASE32_CHARS[u]; } - dest[i] = '\0'; + dest[CryptoKey::base32_encoded_size(srclen) - 1] = '\0'; } /* Implements base32 decoding as in rfc3548. */ @@ -425,7 +425,7 @@ bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srcle unsigned nbits = ((srclen * 5) / 8) * 8; /* We need enough space */ - if(!(nbits/8 <= destlen)) { + if(nbits/8 > destlen) { Q_ASSERT(false); return false; } @@ -435,7 +435,9 @@ bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srcle /* Convert base32 encoded chars to the 5-bit values that they represent. */ for (j = 0; j < srclen; ++j) { - if (src[j] > 0x60 && src[j] < 0x7B) + if (src[j] == '=' || src[j] == '\0') + break; + else if (src[j] > 0x60 && src[j] < 0x7B) tmp[j] = src[j] - 0x61; else if (src[j] > 0x31 && src[j] < 0x38) tmp[j] = src[j] - 0x18; diff --git a/src/utils/CryptoKey.h b/src/utils/CryptoKey.h index 70ada19..f4abbc7 100644 --- a/src/utils/CryptoKey.h +++ b/src/utils/CryptoKey.h @@ -77,6 +77,13 @@ class CryptoKey // Verify a signature as per signSHA256 bool verifySHA256(const QByteArray &digest, QByteArray signature) const; + /* Given a source input length (srclen), calculate the size of the base32 encoded string for it */ + static inline unsigned base32_encoded_size(unsigned srclen) { + /* 1. Divide (srclen * 8) by five, rounding up - ((srclen * 8) + 4)/5 */ + /* 2. Add 1 byte for null terminator */ + return (((srclen * 8) + 4)/5) + 1; + } + private: struct Data : public QSharedData { diff --git a/tests/tst_cryptokey/tst_cryptokey.cpp b/tests/tst_cryptokey/tst_cryptokey.cpp index 79c5c04..4900018 100644 --- a/tests/tst_cryptokey/tst_cryptokey.cpp +++ b/tests/tst_cryptokey/tst_cryptokey.cpp @@ -254,13 +254,13 @@ void TestCryptoKey::testBase32() /* test padding */ strcpy(data1, "\x2e\xd0\x63\xe2\x5b\x16\xdf\xc4\xfc\x01\x23\xc9\xeb\xf6\x83\x71\xe4\x8e\xa0\x1c\x08\x65\xab\xb2\x58\x3c\xd5\xd3\x60"); - base32_encode(data2, 49, data1, 30); - QCOMPARE(QString::fromLocal8Bit(data2), QString("f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhtk5gyaa")); + base32_encode(data2, 49, data1, 29); + QCOMPARE(QString::fromLocal8Bit(data2), QString("f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhtk5gya")); /* test non multiple of 5 length input */ strcpy(data1, "\x2e\xd0\x63\xe2\x5b\x16\xdf\xc4\xfc\x01\x23\xc9\xeb\xf6\x83\x71\xe4\x8e\xa0\x1c\x08\x65\xab\xb2\x58\x3c"); - base32_encode(data2, 49, data1, 27); - QCOMPARE(QString::fromLocal8Bit(data2), QString("f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhqaa")); + base32_encode(data2, 49, data1, 26); + QCOMPARE(QString::fromLocal8Bit(data2), QString("f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhq")); delete[] data1; delete[] data2; @@ -270,6 +270,24 @@ void TestCryptoKey::testBase32() char *encoded = new char[97](); char *decoded = new char[60](); + /* Test known string */ + strcpy(encoded, "orsxg5bq"); + QVERIFY(base32_decode(decoded, 60, encoded, 8)); + QCOMPARE(QString::fromLocal8Bit(decoded), QString("test0")); + + /* Test padding */ + strcpy(encoded, "orsxg5bq==="); + QVERIFY(base32_decode(decoded, 60, encoded, 11)); + QCOMPARE(QString::fromLocal8Bit(decoded), QString("test0")); + + strcpy(encoded, "orsxg5bq======"); + QVERIFY(base32_decode(decoded, 60, encoded, 14)); + QCOMPARE(QString::fromLocal8Bit(decoded), QString("test0")); + + strcpy(encoded, "orsxg5bq\0\0\0\0\0"); + QVERIFY(base32_decode(decoded, 60, encoded, 13)); + QCOMPARE(QString::fromLocal8Bit(decoded), QString("test0")); + /* encode and decode random bytes */ SecureRNG::random(rnd_bytes, 60); base32_encode(encoded, 97, rnd_bytes, 60); From 7beb6dec76738ca0e3ff7a31f4340e0db4c16771 Mon Sep 17 00:00:00 2001 From: m-simonelli <60584156+m-simonelli@users.noreply.github.com> Date: Tue, 21 Jul 2020 11:57:34 +1000 Subject: [PATCH 10/91] Update CryptoKey.cpp --- src/utils/CryptoKey.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/CryptoKey.cpp b/src/utils/CryptoKey.cpp index c0feb8c..7ca8f1b 100644 --- a/src/utils/CryptoKey.cpp +++ b/src/utils/CryptoKey.cpp @@ -418,7 +418,7 @@ void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srcle dest[CryptoKey::base32_encoded_size(srclen) - 1] = '\0'; } -/* Implements base32 decoding as in rfc3548. */ +/* Implements base32 decoding as in rfc3548, except for padding. This implementation allows both '=' and null byte padding */ bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen) { unsigned int i, j, bit; From ce8aca2da098e8e5df6ceecfe6044f28f3bd05db Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Tue, 21 Jul 2020 12:27:28 +1000 Subject: [PATCH 11/91] Remove unneeded constructors/destructors/copy constructors for CryptoKey --- src/utils/CryptoKey.cpp | 9 --------- src/utils/CryptoKey.h | 4 ---- 2 files changed, 13 deletions(-) diff --git a/src/utils/CryptoKey.cpp b/src/utils/CryptoKey.cpp index 8aa0357..4bcbb0a 100644 --- a/src/utils/CryptoKey.cpp +++ b/src/utils/CryptoKey.cpp @@ -51,15 +51,6 @@ void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen); bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen); -CryptoKey::CryptoKey() -{ -} - -CryptoKey::~CryptoKey() -{ - clear(); -} - CryptoKey::Data::~Data() { if (key) diff --git a/src/utils/CryptoKey.h b/src/utils/CryptoKey.h index e6f16fc..8d4a3a3 100644 --- a/src/utils/CryptoKey.h +++ b/src/utils/CryptoKey.h @@ -50,10 +50,6 @@ class CryptoKey DER }; - CryptoKey(); - CryptoKey(const CryptoKey &other) : d(other.d) { } - ~CryptoKey(); - CryptoKey& operator=(const CryptoKey&) = default; bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM); From 416de45664ad2d83f6779b541e032551a6ce7951 Mon Sep 17 00:00:00 2001 From: m-simonelli <60584156+m-simonelli@users.noreply.github.com> Date: Tue, 21 Jul 2020 12:44:46 +1000 Subject: [PATCH 12/91] Update CryptoKey.h --- src/utils/CryptoKey.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/utils/CryptoKey.h b/src/utils/CryptoKey.h index 8d4a3a3..94a5287 100644 --- a/src/utils/CryptoKey.h +++ b/src/utils/CryptoKey.h @@ -50,8 +50,6 @@ class CryptoKey DER }; - CryptoKey& operator=(const CryptoKey&) = default; - bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM); bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM); void clear(); From 9bfa28b78297c1116569d042c18b983fde12fb84 Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Tue, 21 Jul 2020 13:23:20 +1000 Subject: [PATCH 13/91] Base32 is now RFC4648 compliant, tests rewritten to be cleaner --- src/utils/CryptoKey.cpp | 28 ++++++-- src/utils/CryptoKey.h | 7 -- tests/tst_cryptokey/tst_cryptokey.cpp | 96 ++++++++++++++++----------- 3 files changed, 81 insertions(+), 50 deletions(-) diff --git a/src/utils/CryptoKey.cpp b/src/utils/CryptoKey.cpp index 7ca8f1b..6877d76 100644 --- a/src/utils/CryptoKey.cpp +++ b/src/utils/CryptoKey.cpp @@ -387,16 +387,33 @@ QByteArray torControlHashedPassword(const QByteArray &password) * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* TODO: move this to a header file somewhere so it can be used elsewhere */ +#define CEIL_DIV(a, b) (((a) + ((b) - 1)) / (b)) + +/* Given a source input length (srclen), calculate the size of the + * base32 encoded string for it _without padding_, doesn't include null terminator */ +/* XXX: unused +static inline unsigned base32_encoded_size_no_pad(unsigned srclen) { + return CEIL_DIV(srclen * 8, 5); +}*/ + +/* Given a source input length (srclen), calculate the size of the + * base32 encoded string for it _with padding_, doesn't include null terminator */ +static inline unsigned base32_encoded_size(unsigned srclen) { + return (CEIL_DIV(srclen, 5) * 8); +} + #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" +#define BASE32_PAD '=' -/* Implements base32 encoding as in rfc3548, except for padding. This implementation uses null bytes for padding */ +/* Implements base32 encoding as in RFC4648 */ void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen) { unsigned i, bit, v, u; unsigned nbits = srclen * 8; /* We need enough space, check that the encoded length of src is not greater than destlen*/ - if(CryptoKey::base32_encoded_size(srclen) > destlen) { + if(base32_encoded_size(srclen) > destlen) { Q_ASSERT(false); return; } @@ -415,10 +432,11 @@ void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srcle dest[i] = BASE32_CHARS[u]; } - dest[CryptoKey::base32_encoded_size(srclen) - 1] = '\0'; + for(; i < base32_encoded_size(srclen); i++) dest[i] = BASE32_PAD; + dest[i] = '\0'; } -/* Implements base32 decoding as in rfc3548, except for padding. This implementation allows both '=' and null byte padding */ +/* Implements base32 decoding as in RFC4648 */ bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen) { unsigned int i, j, bit; @@ -435,7 +453,7 @@ bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srcle /* Convert base32 encoded chars to the 5-bit values that they represent. */ for (j = 0; j < srclen; ++j) { - if (src[j] == '=' || src[j] == '\0') + if (src[j] == '=') break; else if (src[j] > 0x60 && src[j] < 0x7B) tmp[j] = src[j] - 0x61; diff --git a/src/utils/CryptoKey.h b/src/utils/CryptoKey.h index f4abbc7..70ada19 100644 --- a/src/utils/CryptoKey.h +++ b/src/utils/CryptoKey.h @@ -77,13 +77,6 @@ class CryptoKey // Verify a signature as per signSHA256 bool verifySHA256(const QByteArray &digest, QByteArray signature) const; - /* Given a source input length (srclen), calculate the size of the base32 encoded string for it */ - static inline unsigned base32_encoded_size(unsigned srclen) { - /* 1. Divide (srclen * 8) by five, rounding up - ((srclen * 8) + 4)/5 */ - /* 2. Add 1 byte for null terminator */ - return (((srclen * 8) + 4)/5) + 1; - } - private: struct Data : public QSharedData { diff --git a/tests/tst_cryptokey/tst_cryptokey.cpp b/tests/tst_cryptokey/tst_cryptokey.cpp index 4900018..a93b633 100644 --- a/tests/tst_cryptokey/tst_cryptokey.cpp +++ b/tests/tst_cryptokey/tst_cryptokey.cpp @@ -239,28 +239,35 @@ void TestCryptoKey::testBase32() char *data1 = new char[64](); char *data2 = new char[64](); - /* test different length inputs */ - strcpy(data1, "test0"); - base32_encode(data2, 9, data1, 5); - QCOMPARE(QString::fromLocal8Bit(data2), QString("orsxg5bq")); - - strcpy(data1, "\xff\xf5\x6d\x44\xae\x0d\x5c\xc9\x62\xc4"); - base32_encode(data2, 17, data1, 10); - QCOMPARE(QString::fromLocal8Bit(data2), QString("772w2rfobvomsywe")); - - strcpy(data1, "\x18\x78\xa6\xd3\x32\xc8\xce\x91\x59\xaa\xc7\x38\x87\x37\x84"); - base32_encode(data2, 35, data1, 15); - QCOMPARE(QString::fromLocal8Bit(data2), QString("db4knuzszdhjcwnky44ion4e")); - - /* test padding */ - strcpy(data1, "\x2e\xd0\x63\xe2\x5b\x16\xdf\xc4\xfc\x01\x23\xc9\xeb\xf6\x83\x71\xe4\x8e\xa0\x1c\x08\x65\xab\xb2\x58\x3c\xd5\xd3\x60"); - base32_encode(data2, 49, data1, 29); - QCOMPARE(QString::fromLocal8Bit(data2), QString("f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhtk5gya")); +#define TEST_BASE_32_ENCODE(input, inlen, expected) \ + strcpy(data1, input); \ + base32_encode(data2, 64, data1, inlen); \ + QCOMPARE(QString::fromLocal8Bit(data2).toLower(), QString(expected).toLower()); + + /* test vectors from RFC4648 */ + /* + BASE32("") = "" + BASE32("f") = "MY======" + BASE32("fo") = "MZXQ====" + BASE32("foo") = "MZXW6===" + BASE32("foob") = "MZXW6YQ=" + BASE32("fooba") = "MZXW6YTB" + BASE32("foobar") = "MZXW6YTBOI======" + */ + TEST_BASE_32_ENCODE("", 0, ""); + TEST_BASE_32_ENCODE("f", 1, "MY======"); + TEST_BASE_32_ENCODE("fo", 2, "MZXQ===="); + TEST_BASE_32_ENCODE("foo", 3, "MZXW6==="); + TEST_BASE_32_ENCODE("foob", 4, "MZXW6YQ="); + TEST_BASE_32_ENCODE("fooba", 5, "MZXW6YTB"); + TEST_BASE_32_ENCODE("foobar", 6, "MZXW6YTBOI======"); - /* test non multiple of 5 length input */ - strcpy(data1, "\x2e\xd0\x63\xe2\x5b\x16\xdf\xc4\xfc\x01\x23\xc9\xeb\xf6\x83\x71\xe4\x8e\xa0\x1c\x08\x65\xab\xb2\x58\x3c"); - base32_encode(data2, 49, data1, 26); - QCOMPARE(QString::fromLocal8Bit(data2), QString("f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhq")); + /* test different length inputs */ + TEST_BASE_32_ENCODE("test0", 5, "orsxg5bq"); + TEST_BASE_32_ENCODE("\xff\xf5\x6d\x44\xae\x0d\x5c\xc9\x62\xc4", 10, "772w2rfobvomsywe"); + TEST_BASE_32_ENCODE("\x18\x78\xa6\xd3\x32\xc8\xce\x91\x59\xaa\xc7\x38\x87\x37\x84", 15, "db4knuzszdhjcwnky44ion4e"); + TEST_BASE_32_ENCODE("\x2e\xd0\x63\xe2\x5b\x16\xdf\xc4\xfc\x01\x23\xc9\xeb\xf6\x83\x71\xe4\x8e\xa0\x1c\x08\x65\xab\xb2\x58\x3c\xd5\xd3\x60", 29, "f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhtk5gya="); +#undef TEST_BASE_32_ENCODE delete[] data1; delete[] data2; @@ -270,23 +277,36 @@ void TestCryptoKey::testBase32() char *encoded = new char[97](); char *decoded = new char[60](); - /* Test known string */ - strcpy(encoded, "orsxg5bq"); - QVERIFY(base32_decode(decoded, 60, encoded, 8)); - QCOMPARE(QString::fromLocal8Bit(decoded), QString("test0")); - - /* Test padding */ - strcpy(encoded, "orsxg5bq==="); - QVERIFY(base32_decode(decoded, 60, encoded, 11)); - QCOMPARE(QString::fromLocal8Bit(decoded), QString("test0")); - - strcpy(encoded, "orsxg5bq======"); - QVERIFY(base32_decode(decoded, 60, encoded, 14)); - QCOMPARE(QString::fromLocal8Bit(decoded), QString("test0")); - - strcpy(encoded, "orsxg5bq\0\0\0\0\0"); - QVERIFY(base32_decode(decoded, 60, encoded, 13)); - QCOMPARE(QString::fromLocal8Bit(decoded), QString("test0")); +#define TEST_BASE_32_DECODE(input, inlen, expected) \ + strcpy(encoded, input); \ + QVERIFY(base32_decode(decoded, 60, encoded, inlen)); \ + QCOMPARE(QString::fromLocal8Bit(decoded).toLower(), QString(expected).toLower()); + + TEST_BASE_32_DECODE("orsxg5bq", 8, "test0"); + TEST_BASE_32_DECODE("orsxg5bq==", 10, "test0"); + TEST_BASE_32_DECODE("orsxg5bq====", 12, "test0"); + TEST_BASE_32_DECODE("orsxg5bq======", 14, "test0"); + + /* test vectors from RFC4648 */ + /* + BASE32("") = "" + BASE32("f") = "MY======" + BASE32("fo") = "MZXQ====" + BASE32("foo") = "MZXW6===" + BASE32("foob") = "MZXW6YQ=" + BASE32("fooba") = "MZXW6YTB" + BASE32("foobar") = "MZXW6YTBOI======" + */ + memset(decoded, 0, 60); + memset(encoded, 0, 97); + TEST_BASE_32_DECODE("", 0, ""); + TEST_BASE_32_DECODE("MY======", 8, "f"); + TEST_BASE_32_DECODE("MZXQ====", 8, "fo"); + TEST_BASE_32_DECODE("MZXW6===", 8, "foo"); + TEST_BASE_32_DECODE("MZXW6YQ=", 8, "foob"); + TEST_BASE_32_DECODE("MZXW6YTB", 8, "fooba"); + TEST_BASE_32_DECODE("MZXW6YTBOI======", 16, "foobar"); +#undef TEST_BASE_32_DECODE /* encode and decode random bytes */ SecureRNG::random(rnd_bytes, 60); From 5c15db19d683413aeb8f57f517b46719cb54829e Mon Sep 17 00:00:00 2001 From: m-simonelli <60584156+m-simonelli@users.noreply.github.com> Date: Wed, 22 Jul 2020 13:26:12 +1000 Subject: [PATCH 14/91] Remove non RFC4648 test cases If they pass the RFC4648 test vectors they should work with anything else, no need to have extra tests --- tests/tst_cryptokey/tst_cryptokey.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/tests/tst_cryptokey/tst_cryptokey.cpp b/tests/tst_cryptokey/tst_cryptokey.cpp index a93b633..275a286 100644 --- a/tests/tst_cryptokey/tst_cryptokey.cpp +++ b/tests/tst_cryptokey/tst_cryptokey.cpp @@ -261,12 +261,6 @@ void TestCryptoKey::testBase32() TEST_BASE_32_ENCODE("foob", 4, "MZXW6YQ="); TEST_BASE_32_ENCODE("fooba", 5, "MZXW6YTB"); TEST_BASE_32_ENCODE("foobar", 6, "MZXW6YTBOI======"); - - /* test different length inputs */ - TEST_BASE_32_ENCODE("test0", 5, "orsxg5bq"); - TEST_BASE_32_ENCODE("\xff\xf5\x6d\x44\xae\x0d\x5c\xc9\x62\xc4", 10, "772w2rfobvomsywe"); - TEST_BASE_32_ENCODE("\x18\x78\xa6\xd3\x32\xc8\xce\x91\x59\xaa\xc7\x38\x87\x37\x84", 15, "db4knuzszdhjcwnky44ion4e"); - TEST_BASE_32_ENCODE("\x2e\xd0\x63\xe2\x5b\x16\xdf\xc4\xfc\x01\x23\xc9\xeb\xf6\x83\x71\xe4\x8e\xa0\x1c\x08\x65\xab\xb2\x58\x3c\xd5\xd3\x60", 29, "f3ighys3c3p4j7abepe6x5udohsi5ia4bbs2xmsyhtk5gya="); #undef TEST_BASE_32_ENCODE delete[] data1; @@ -281,12 +275,7 @@ void TestCryptoKey::testBase32() strcpy(encoded, input); \ QVERIFY(base32_decode(decoded, 60, encoded, inlen)); \ QCOMPARE(QString::fromLocal8Bit(decoded).toLower(), QString(expected).toLower()); - - TEST_BASE_32_DECODE("orsxg5bq", 8, "test0"); - TEST_BASE_32_DECODE("orsxg5bq==", 10, "test0"); - TEST_BASE_32_DECODE("orsxg5bq====", 12, "test0"); - TEST_BASE_32_DECODE("orsxg5bq======", 14, "test0"); - + /* test vectors from RFC4648 */ /* BASE32("") = "" @@ -297,8 +286,6 @@ void TestCryptoKey::testBase32() BASE32("fooba") = "MZXW6YTB" BASE32("foobar") = "MZXW6YTBOI======" */ - memset(decoded, 0, 60); - memset(encoded, 0, 97); TEST_BASE_32_DECODE("", 0, ""); TEST_BASE_32_DECODE("MY======", 8, "f"); TEST_BASE_32_DECODE("MZXQ====", 8, "fo"); From e32e094a704f5450a44da64323ad11a8d832da89 Mon Sep 17 00:00:00 2001 From: Marco Simonelli Date: Tue, 4 Aug 2020 14:28:43 +1000 Subject: [PATCH 15/91] Scaling to higher DPI + updated .gitignore for new file tree --- .gitignore | 3 +++ src/main.cpp | 8 ++++++++ src/ui/qml/MainWindow.qml | 4 +--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d4b120a..5904fe7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ release *.o *.moc tests/*/tst_* +src/tests/*/tst_* !tst_*.pro !tst_*.cpp !tst_*.h @@ -23,6 +24,7 @@ tests/*/tst_* packaging/installer/Qt packaging/installer/Output translation/*.qm +src/tego-ui/translation/*.qm config config.ricochet config.log @@ -31,6 +33,7 @@ config.tests/*/* !config.tests/*/*.cpp !config.tests/*/*.h ricochet +src/ricochet.log build-tests* buildscripts/src/ricochet-refresh buildscripts/output diff --git a/src/main.cpp b/src/main.cpp index 45a1bb5..7f1361f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,6 +65,14 @@ int main(int argc, char *argv[]) if (qEnvironmentVariableIsEmpty("QMLSCENE_DEVICE")) qputenv("QMLSCENE_DEVICE", "softwarecontext"); + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + if (!qEnvironmentVariableIsSet("QT_DEVICE_PIXEL_RATIO") + && !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") + && !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") + && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) { + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + } + QApplication a(argc, argv); a.setApplicationVersion(QLatin1String("1.1.4")); a.setOrganizationName(QStringLiteral("Ricochet")); diff --git a/src/ui/qml/MainWindow.qml b/src/ui/qml/MainWindow.qml index b318995..1711690 100644 --- a/src/ui/qml/MainWindow.qml +++ b/src/ui/qml/MainWindow.qml @@ -10,14 +10,12 @@ ApplicationWindow { title: "Ricochet" visibility: Window.AutomaticVisibility - width: 250 + width: 650 height: 400 minimumHeight: 400 minimumWidth: uiSettings.data.combinedChatWindow ? 650 : 250 - maximumWidth: uiSettings.data.combinedChatWindow ? (1 << 24) - 1 : 250 onMinimumWidthChanged: width = Math.max(width, minimumWidth) - onMaximumWidthChanged: width = Math.min(width, maximumWidth) // OS X Menu Loader { From 7f177faf261eb5694ea41fe5d07fbb351a3a23ca Mon Sep 17 00:00:00 2001 From: m-simonelli <60584156+m-simonelli@users.noreply.github.com> Date: Tue, 4 Aug 2020 21:26:57 +1000 Subject: [PATCH 16/91] Update main.cpp --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cpp b/src/main.cpp index 7f1361f..c225df2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,6 +65,7 @@ int main(int argc, char *argv[]) if (qEnvironmentVariableIsEmpty("QMLSCENE_DEVICE")) qputenv("QMLSCENE_DEVICE", "softwarecontext"); + /* https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt */ QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); if (!qEnvironmentVariableIsSet("QT_DEVICE_PIXEL_RATIO") && !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") From 004702add170e2838639c7fc7f55b7e783ea98c6 Mon Sep 17 00:00:00 2001 From: Richard Pospesel Date: Wed, 15 Jul 2020 14:15:05 -0500 Subject: [PATCH 17/91] build: added ricochet-refresh binary .gitignore file --- .gitignore | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 5904fe7..fc5dd56 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,7 @@ config.tests/*/* !config.tests/*/*.pro !config.tests/*/*.cpp !config.tests/*/*.h -ricochet -src/ricochet.log +ricochet-refresh build-tests* buildscripts/src/ricochet-refresh buildscripts/output @@ -41,8 +40,5 @@ buildscripts/lib target_wrapper\.sh moc_predefs.h \.vscode/c_cpp_properties\.json - \.vscode/ notes/* - -ricochet-refresh From 2b5fc1b39fc66807889700f783859f4b1873e6fa Mon Sep 17 00:00:00 2001 From: Richard Pospesel Date: Thu, 16 Jul 2020 13:01:52 -0500 Subject: [PATCH 18/91] build: integrated fmt library for debug logging --- ricochet.pro | 25 +- src/fmt/core.h | 1301 +++++++++++++++ src/fmt/format-inl.h | 546 +++++++ src/fmt/format.cc | 47 + src/fmt/format.h | 3698 ++++++++++++++++++++++++++++++++++++++++++ src/fmt/ostream.h | 161 ++ src/fmt/posix.cc | 244 +++ src/fmt/posix.h | 417 +++++ src/fmt/printf.h | 713 ++++++++ src/fmt/ranges.h | 243 +++ src/fmt/time.h | 154 ++ src/logger.hpp | 60 + 12 files changed, 7608 insertions(+), 1 deletion(-) create mode 100644 src/fmt/core.h create mode 100644 src/fmt/format-inl.h create mode 100644 src/fmt/format.cc create mode 100644 src/fmt/format.h create mode 100644 src/fmt/ostream.h create mode 100644 src/fmt/posix.cc create mode 100644 src/fmt/posix.h create mode 100644 src/fmt/printf.h create mode 100644 src/fmt/ranges.h create mode 100644 src/fmt/time.h create mode 100644 src/logger.hpp diff --git a/ricochet.pro b/ricochet.pro index 381dcef..00822ae 100644 --- a/ricochet.pro +++ b/ricochet.pro @@ -36,7 +36,6 @@ lessThan(QT_MAJOR_VERSION,5)|lessThan(QT_MINOR_VERSION,1) { TARGET = ricochet-refresh TEMPLATE = app QT += core gui network quick widgets -CONFIG += c++11 VERSION = 1.1.4 @@ -129,6 +128,9 @@ win32|mac { } unix { + # get us onto the latest c++ + QMAKE_CXXFLAGS += --std=c++2a + !isEmpty(OPENSSLDIR) { INCLUDEPATH += $${OPENSSLDIR}/include LIBS += -L$${OPENSSLDIR}/lib -lcrypto @@ -240,6 +242,27 @@ HEADERS += src/protocol/Channel.h \ src/protocol/ChatChannel.h \ src/protocol/ContactRequestChannel.h +# fmt lib +SOURCE +=\ + src/fmt/format.cc \ + src/fmt/posix.cc + +HEADERS +=\ + src/fmt/core.h \ + src/fmt/format.h \ + src/fmt/format-inl.h \ + src/fmt/ostream.h \ + src/fmt/posix.h \ + src/fmt/printf.h \ + src/fmt/ranges.h \ + src/fmt/time.h + + + +# custom +HEADERS +=\ + src/logger.hpp + include(protobuf.pri) PROTOS += src/protocol/ControlChannel.proto \ src/protocol/AuthHiddenService.proto \ diff --git a/src/fmt/core.h b/src/fmt/core.h new file mode 100644 index 0000000..96bbf27 --- /dev/null +++ b/src/fmt/core.h @@ -0,0 +1,1301 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include +#include +#include +#include +#include +#include + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 50100 + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#ifdef __has_include +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION +#else +# define FMT_HAS_GXX_CXX11 0 +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VER _MSC_VER +#else +# define FMT_MSC_VER 0 +#endif + +// Check if relaxed c++14 constexpr is supported. +// GCC doesn't allow throw in constexpr until version 6 (bug 67371). +#ifndef FMT_USE_CONSTEXPR +# define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ + (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +# define FMT_CONSTEXPR_DECL constexpr +#else +# define FMT_CONSTEXPR inline +# define FMT_CONSTEXPR_DECL +#endif + +#ifndef FMT_OVERRIDE +# if FMT_HAS_FEATURE(cxx_override) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ + FMT_MSC_VER >= 1900 +# define FMT_OVERRIDE override +# else +# define FMT_OVERRIDE +# endif +#endif + +#if FMT_HAS_FEATURE(cxx_explicit_conversions) || \ + FMT_MSC_VER >= 1800 +# define FMT_EXPLICIT explicit +#else +# define FMT_EXPLICIT +#endif + +#ifndef FMT_NULL +# if FMT_HAS_FEATURE(cxx_nullptr) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ + FMT_MSC_VER >= 1600 +# define FMT_NULL nullptr +# define FMT_USE_NULLPTR 1 +# else +# define FMT_NULL NULL +# endif +#endif + +#ifndef FMT_USE_NULLPTR +# define FMT_USE_NULLPTR 0 +#endif + +// Check if exceptions are disabled. +#if defined(__GNUC__) && !defined(__EXCEPTIONS) +# define FMT_EXCEPTIONS 0 +#elif FMT_MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +#endif +#ifndef FMT_EXCEPTIONS +# define FMT_EXCEPTIONS 1 +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ + FMT_MSC_VER >= 1900 +# define FMT_DETECTED_NOEXCEPT noexcept +#else +# define FMT_DETECTED_NOEXCEPT throw() +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS +# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +# else +# define FMT_NOEXCEPT +# endif +#endif + +// This is needed because GCC still uses throw() in its headers when exceptions +// are disabled. +#if FMT_GCC_VERSION +# define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT +#else +# define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ + FMT_MSC_VER >= 1900 +# define FMT_INLINE_NAMESPACE inline namespace +# define FMT_END_NAMESPACE }} +# else +# define FMT_INLINE_NAMESPACE namespace +# define FMT_END_NAMESPACE } using namespace v5; } +# endif +# define FMT_BEGIN_NAMESPACE namespace fmt { FMT_INLINE_NAMESPACE v5 { +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#endif +#ifndef FMT_API +# define FMT_API +#endif + +#ifndef FMT_ASSERT +# define FMT_ASSERT(condition, message) assert((condition) && message) +#endif + +#define FMT_DELETED = delete + +// A macro to disallow the copy construction and assignment. +#define FMT_DISALLOW_COPY_AND_ASSIGN(Type) \ + Type(const Type &) FMT_DELETED; \ + void operator=(const Type &) FMT_DELETED + +// libc++ supports string_view in pre-c++17. +#if (FMT_HAS_INCLUDE() && \ + (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +# include +# define FMT_USE_STD_STRING_VIEW +#elif (FMT_HAS_INCLUDE() && \ + __cplusplus >= 201402L) +# include +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +// std::result_of is defined in in gcc 4.4. +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 404 +# include +#endif + +FMT_BEGIN_NAMESPACE + +// An implementation of declval for pre-C++11 compilers such as gcc 4. +namespace internal { +template +typename std::add_rvalue_reference::type declval() FMT_NOEXCEPT; +} + +/** + An implementation of ``std::basic_string_view`` for pre-C++17. It provides a + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). + */ +template +class basic_string_view { + private: + const Char *data_; + size_t size_; + + public: + typedef Char char_type; + typedef const Char *iterator; + + // Standard basic_string_view type. +#if defined(FMT_USE_STD_STRING_VIEW) + typedef std::basic_string_view type; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) + typedef std::experimental::basic_string_view type; +#else + struct type { + const char *data() const { return FMT_NULL; } + size_t size() const { return 0; }; + }; +#endif + + FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(FMT_NULL), size_(0) {} + + /** Constructs a string reference object from a C string and a size. */ + FMT_CONSTEXPR basic_string_view(const Char *s, size_t size) FMT_NOEXCEPT + : data_(s), size_(size) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ + basic_string_view(const Char *s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** Constructs a string reference from a ``std::basic_string`` object. */ + template + FMT_CONSTEXPR basic_string_view( + const std::basic_string &s) FMT_NOEXCEPT + : data_(s.c_str()), size_(s.size()) {} + + FMT_CONSTEXPR basic_string_view(type s) FMT_NOEXCEPT + : data_(s.data()), size_(s.size()) {} + + /** Returns a pointer to the string data. */ + const Char *data() const { return data_; } + + /** Returns the string size. */ + FMT_CONSTEXPR size_t size() const { return size_; } + + FMT_CONSTEXPR iterator begin() const { return data_; } + FMT_CONSTEXPR iterator end() const { return data_ + size_; } + + FMT_CONSTEXPR void remove_prefix(size_t n) { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + int compare(basic_string_view other) const { + size_t size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + friend bool operator==(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) != 0; + } + friend bool operator<(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) < 0; + } + friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) <= 0; + } + friend bool operator>(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) >= 0; + } +}; + +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; + +template +class basic_format_arg; + +template +class basic_format_args; + +// A formatter for objects of type T. +template +struct formatter; + +namespace internal { + +/** A contiguous memory buffer with an optional growing ability. */ +template +class basic_buffer { + private: + FMT_DISALLOW_COPY_AND_ASSIGN(basic_buffer); + + T *ptr_; + std::size_t size_; + std::size_t capacity_; + + protected: + basic_buffer(T *p = FMT_NULL, std::size_t size = 0, std::size_t capacity = 0) + FMT_NOEXCEPT: ptr_(p), size_(size), capacity_(capacity) {} + + /** Sets the buffer data and capacity. */ + void set(T *data, std::size_t capacity) FMT_NOEXCEPT { + ptr_ = data; + capacity_ = capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual void grow(std::size_t capacity) = 0; + + public: + typedef T value_type; + typedef const T &const_reference; + + virtual ~basic_buffer() {} + + T *begin() FMT_NOEXCEPT { return ptr_; } + T *end() FMT_NOEXCEPT { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + std::size_t size() const FMT_NOEXCEPT { return size_; } + + /** Returns the capacity of this buffer. */ + std::size_t capacity() const FMT_NOEXCEPT { return capacity_; } + + /** Returns a pointer to the buffer data. */ + T *data() FMT_NOEXCEPT { return ptr_; } + + /** Returns a pointer to the buffer data. */ + const T *data() const FMT_NOEXCEPT { return ptr_; } + + /** + Resizes the buffer. If T is a POD type new elements may not be initialized. + */ + void resize(std::size_t new_size) { + reserve(new_size); + size_ = new_size; + } + + /** Reserves space to store at least *capacity* elements. */ + void reserve(std::size_t capacity) { + if (capacity > capacity_) + grow(capacity); + } + + void push_back(const T &value) { + reserve(size_ + 1); + ptr_[size_++] = value; + } + + /** Appends data to the end of the buffer. */ + template + void append(const U *begin, const U *end); + + T &operator[](std::size_t index) { return ptr_[index]; } + const T &operator[](std::size_t index) const { return ptr_[index]; } +}; + +typedef basic_buffer buffer; +typedef basic_buffer wbuffer; + +// A container-backed buffer. +template +class container_buffer : public basic_buffer { + private: + Container &container_; + + protected: + void grow(std::size_t capacity) FMT_OVERRIDE { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit container_buffer(Container &c) + : basic_buffer(&c[0], c.size(), c.size()), + container_(c) {} +}; + +struct error_handler { + FMT_CONSTEXPR error_handler() {} + FMT_CONSTEXPR error_handler(const error_handler &) {} + + // This function is intentionally not constexpr to give a compile-time error. + FMT_API void on_error(const char *message); +}; + +// Formatting of wide characters and strings into a narrow output is disallowed: +// fmt::format("{}", L"test"); // error +// To fix this, use a wide format string: +// fmt::format(L"{}", L"test"); +template +inline void require_wchar() { + static_assert( + std::is_same::value, + "formatting of wide characters into a narrow output is disallowed"); +} + +template +struct named_arg_base; + +template +struct named_arg; + +template +struct is_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +enum type { + none_type, name_arg_type, + // Integer types should go first, + int_type, uint_type, long_long_type, ulong_long_type, bool_type, char_type, + last_integer_type = char_type, + // followed by floating-point types. + double_type, long_double_type, last_numeric_type = long_double_type, + cstring_type, string_type, pointer_type, custom_type +}; + +FMT_CONSTEXPR bool is_integral(type t) { + FMT_ASSERT(t != internal::name_arg_type, "invalid argument type"); + return t > internal::none_type && t <= internal::last_integer_type; +} + +FMT_CONSTEXPR bool is_arithmetic(type t) { + FMT_ASSERT(t != internal::name_arg_type, "invalid argument type"); + return t > internal::none_type && t <= internal::last_numeric_type; +} + +template +struct convert_to_int { + enum { + value = !std::is_arithmetic::value && std::is_convertible::value + }; +}; + +template +struct string_value { + const Char *value; + std::size_t size; +}; + +template +struct custom_value { + const void *value; + void (*format)(const void *arg, Context &ctx); +}; + +// A formatting argument value. +template +class value { + public: + typedef typename Context::char_type char_type; + + union { + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + double double_value; + long double long_double_value; + const void *pointer; + string_value string; + string_value sstring; + string_value ustring; + custom_value custom; + }; + + FMT_CONSTEXPR value(int val = 0) : int_value(val) {} + value(unsigned val) { uint_value = val; } + value(long long val) { long_long_value = val; } + value(unsigned long long val) { ulong_long_value = val; } + value(double val) { double_value = val; } + value(long double val) { long_double_value = val; } + value(const char_type *val) { string.value = val; } + value(const signed char *val) { + static_assert(std::is_same::value, + "incompatible string types"); + sstring.value = val; + } + value(const unsigned char *val) { + static_assert(std::is_same::value, + "incompatible string types"); + ustring.value = val; + } + value(basic_string_view val) { + string.value = val.data(); + string.size = val.size(); + } + value(const void *val) { pointer = val; } + + template + explicit value(const T &val) { + custom.value = &val; + custom.format = &format_custom_arg; + } + + const named_arg_base &as_named_arg() { + return *static_cast*>(pointer); + } + + private: + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg(const void *arg, Context &ctx) { + // Get the formatter type through the context to allow different contexts + // have different extension points, e.g. `formatter` for `format` and + // `printf_formatter` for `printf`. + typename Context::template formatter_type::type f; + auto &&parse_ctx = ctx.parse_context(); + parse_ctx.advance_to(f.parse(parse_ctx)); + ctx.advance_to(f.format(*static_cast(arg), ctx)); + } +}; + +template +struct typed_value : value { + static const type type_tag = TYPE; + + template + FMT_CONSTEXPR typed_value(const T &val) : value(val) {} +}; + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T &value); + +#define FMT_MAKE_VALUE(TAG, ArgType, ValueType) \ + template \ + FMT_CONSTEXPR typed_value make_value(ArgType val) { \ + return static_cast(val); \ + } + +FMT_MAKE_VALUE(bool_type, bool, int) +FMT_MAKE_VALUE(int_type, short, int) +FMT_MAKE_VALUE(uint_type, unsigned short, unsigned) +FMT_MAKE_VALUE(int_type, int, int) +FMT_MAKE_VALUE(uint_type, unsigned, unsigned) + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +typedef std::conditional::type + long_type; +FMT_MAKE_VALUE( + (sizeof(long) == sizeof(int) ? int_type : long_long_type), long, long_type) +typedef std::conditional::type ulong_type; +FMT_MAKE_VALUE( + (sizeof(unsigned long) == sizeof(unsigned) ? uint_type : ulong_long_type), + unsigned long, ulong_type) + +FMT_MAKE_VALUE(long_long_type, long long, long long) +FMT_MAKE_VALUE(ulong_long_type, unsigned long long, unsigned long long) +FMT_MAKE_VALUE(int_type, signed char, int) +FMT_MAKE_VALUE(uint_type, unsigned char, unsigned) +FMT_MAKE_VALUE(char_type, char, int) + +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +template +inline typed_value make_value(wchar_t val) { + require_wchar(); + return static_cast(val); +} +#endif + +FMT_MAKE_VALUE(double_type, float, double) +FMT_MAKE_VALUE(double_type, double, double) +FMT_MAKE_VALUE(long_double_type, long double, long double) + +// Formatting of wide strings into a narrow buffer and multibyte strings +// into a wide buffer is disallowed (https://github.com/fmtlib/fmt/pull/606). +FMT_MAKE_VALUE(cstring_type, typename C::char_type*, + const typename C::char_type*) +FMT_MAKE_VALUE(cstring_type, const typename C::char_type*, + const typename C::char_type*) + +FMT_MAKE_VALUE(cstring_type, signed char*, const signed char*) +FMT_MAKE_VALUE(cstring_type, const signed char*, const signed char*) +FMT_MAKE_VALUE(cstring_type, unsigned char*, const unsigned char*) +FMT_MAKE_VALUE(cstring_type, const unsigned char*, const unsigned char*) +FMT_MAKE_VALUE(string_type, basic_string_view, + basic_string_view) +FMT_MAKE_VALUE(string_type, + typename basic_string_view::type, + basic_string_view) +FMT_MAKE_VALUE(string_type, const std::basic_string&, + basic_string_view) +FMT_MAKE_VALUE(pointer_type, void*, const void*) +FMT_MAKE_VALUE(pointer_type, const void*, const void*) + +#if FMT_USE_NULLPTR +FMT_MAKE_VALUE(pointer_type, std::nullptr_t, const void*) +#endif + +// Formatting of arbitrary pointers is disallowed. If you want to output a +// pointer cast it to "void *" or "const void *". In particular, this forbids +// formatting of "[const] volatile char *" which is printed as bool by +// iostreams. +template +typename std::enable_if::value>::type + make_value(const T *) { + static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); +} + +template +inline typename std::enable_if< + std::is_enum::value && convert_to_int::value, + typed_value>::type + make_value(const T &val) { return static_cast(val); } + +template +inline typename std::enable_if< + !convert_to_int::value && + !std::is_convertible>::value, + // Implicit conversion to std::string is not handled here because it's + // unsafe: https://github.com/fmtlib/fmt/issues/729 + typed_value>::type + make_value(const T &val) { return val; } + +template +typed_value + make_value(const named_arg &val) { + basic_format_arg arg = make_arg(val.value); + std::memcpy(val.data, &arg, sizeof(arg)); + return static_cast(&val); +} + +// Maximum number of arguments with packed types. +enum { max_packed_args = 15 }; + +template +class arg_map; + +template +struct result_of; + +template +struct result_of { + // A workaround for gcc 4.4 that doesn't allow F to be a reference. + typedef typename std::result_of< + typename std::remove_reference::type(Args...)>::type type; +}; +} + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template +class basic_format_arg { + private: + internal::value value_; + internal::type type_; + + template + friend FMT_CONSTEXPR basic_format_arg + internal::make_arg(const T &value); + + template + friend FMT_CONSTEXPR typename internal::result_of::type + visit(Visitor &&vis, basic_format_arg arg); + + friend class basic_format_args; + friend class internal::arg_map; + + typedef typename Context::char_type char_type; + + public: + class handle { + public: + explicit handle(internal::custom_value custom): custom_(custom) {} + + void format(Context &ctx) const { custom_.format(custom_.value, ctx); } + + private: + internal::custom_value custom_; + }; + + FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {} + + FMT_EXPLICIT operator bool() const FMT_NOEXCEPT { + return type_ != internal::none_type; + } + + internal::type type() const { return type_; } + + bool is_integral() const { return internal::is_integral(type_); } + bool is_arithmetic() const { return internal::is_arithmetic(type_); } +}; + +// Parsing context consisting of a format string range being parsed and an +// argument counter for automatic indexing. +template +class basic_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + public: + typedef Char char_type; + typedef typename basic_string_view::iterator iterator; + + explicit FMT_CONSTEXPR basic_parse_context( + basic_string_view format_str, ErrorHandler eh = ErrorHandler()) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} + + // Returns an iterator to the beginning of the format string range being + // parsed. + FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT { + return format_str_.begin(); + } + + // Returns an iterator past the end of the format string range being parsed. + FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + + // Advances the begin iterator to ``it``. + FMT_CONSTEXPR void advance_to(iterator it) { + format_str_.remove_prefix(it - begin()); + } + + // Returns the next argument index. + FMT_CONSTEXPR unsigned next_arg_id(); + + FMT_CONSTEXPR bool check_arg_id(unsigned) { + if (next_arg_id_ > 0) { + on_error("cannot switch from automatic to manual argument indexing"); + return false; + } + next_arg_id_ = -1; + return true; + } + void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char *message) { + ErrorHandler::on_error(message); + } + + FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; } +}; + +typedef basic_parse_context parse_context; +typedef basic_parse_context wparse_context; + +namespace internal { +// A map from argument names to their values for named arguments. +template +class arg_map { + private: + FMT_DISALLOW_COPY_AND_ASSIGN(arg_map); + + typedef typename Context::char_type char_type; + + struct entry { + basic_string_view name; + basic_format_arg arg; + }; + + entry *map_; + unsigned size_; + + void push_back(value val) { + const internal::named_arg_base &named = val.as_named_arg(); + map_[size_] = entry{named.name, named.template deserialize()}; + ++size_; + } + + public: + arg_map() : map_(FMT_NULL), size_(0) {} + void init(const basic_format_args &args); + ~arg_map() { delete [] map_; } + + basic_format_arg find(basic_string_view name) const { + // The list is unsorted, so just return the first matching name. + for (auto it = map_, end = map_ + size_; it != end; ++it) { + if (it->name == name) + return it->arg; + } + return basic_format_arg(); + } +}; + +template +class context_base { + public: + typedef OutputIt iterator; + + private: + basic_parse_context parse_context_; + iterator out_; + basic_format_args args_; + + protected: + typedef Char char_type; + typedef basic_format_arg format_arg; + + context_base(OutputIt out, basic_string_view format_str, + basic_format_args args) + : parse_context_(format_str), out_(out), args_(args) {} + + // Returns the argument with specified index. + format_arg do_get_arg(unsigned arg_id) { + format_arg arg = args_.get(arg_id); + if (!arg) + parse_context_.on_error("argument index out of range"); + return arg; + } + + // Checks if manual indexing is used and returns the argument with + // specified index. + format_arg get_arg(unsigned arg_id) { + return this->parse_context().check_arg_id(arg_id) ? + this->do_get_arg(arg_id) : format_arg(); + } + + public: + basic_parse_context &parse_context() { + return parse_context_; + } + + internal::error_handler error_handler() { + return parse_context_.error_handler(); + } + + void on_error(const char *message) { parse_context_.on_error(message); } + + // Returns an iterator to the beginning of the output range. + iterator out() { return out_; } + iterator begin() { return out_; } // deprecated + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { out_ = it; } + + basic_format_args args() const { return args_; } +}; + +// Extracts a reference to the container from back_insert_iterator. +template +inline Container &get_container(std::back_insert_iterator it) { + typedef std::back_insert_iterator bi_iterator; + struct accessor: bi_iterator { + accessor(bi_iterator iter) : bi_iterator(iter) {} + using bi_iterator::container; + }; + return *accessor(it).container; +} +} // namespace internal + +// Formatting context. +template +class basic_format_context : + public internal::context_base< + OutputIt, basic_format_context, Char> { + public: + /** The character type for the output. */ + typedef Char char_type; + + // using formatter_type = formatter; + template + struct formatter_type { typedef formatter type; }; + + private: + internal::arg_map map_; + + FMT_DISALLOW_COPY_AND_ASSIGN(basic_format_context); + + typedef internal::context_base base; + typedef typename base::format_arg format_arg; + using base::get_arg; + + public: + using typename base::iterator; + + /** + Constructs a ``basic_format_context`` object. References to the arguments are + stored in the object so make sure they have appropriate lifetimes. + */ + basic_format_context(OutputIt out, basic_string_view format_str, + basic_format_args args) + : base(out, format_str, args) {} + + format_arg next_arg() { + return this->do_get_arg(this->parse_context().next_arg_id()); + } + format_arg get_arg(unsigned arg_id) { return this->do_get_arg(arg_id); } + + // Checks if manual indexing is used and returns the argument with the + // specified name. + format_arg get_arg(basic_string_view name); +}; + +template +struct buffer_context { + typedef basic_format_context< + std::back_insert_iterator>, Char> type; +}; +typedef buffer_context::type format_context; +typedef buffer_context::type wformat_context; + +namespace internal { +template +struct get_type { + typedef decltype(make_value( + declval::type&>())) value_type; + static const type value = value_type::type_tag; +}; + +template +FMT_CONSTEXPR uint64_t get_types() { return 0; } + +template +FMT_CONSTEXPR uint64_t get_types() { + return get_type::value | (get_types() << 4); +} + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T &value) { + basic_format_arg arg; + arg.type_ = get_type::value; + arg.value_ = make_value(value); + return arg; +} + +template +inline typename std::enable_if>::type + make_arg(const T &value) { + return make_value(value); +} + +template +inline typename std::enable_if>::type + make_arg(const T &value) { + return make_arg(value); +} +} + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store { + private: + static const size_t NUM_ARGS = sizeof...(Args); + + // Packed is a macro on MinGW so use IS_PACKED instead. + static const bool IS_PACKED = NUM_ARGS < internal::max_packed_args; + + typedef typename std::conditional, basic_format_arg>::type value_type; + + // If the arguments are not packed, add one more element to mark the end. + value_type data_[NUM_ARGS + (IS_PACKED && NUM_ARGS != 0 ? 0 : 1)]; + + friend class basic_format_args; + + static FMT_CONSTEXPR uint64_t get_types() { + return IS_PACKED ? internal::get_types() + : -static_cast(NUM_ARGS); + } + + public: +#if FMT_USE_CONSTEXPR + static constexpr uint64_t TYPES = get_types(); +#else + static const uint64_t TYPES; +#endif + +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 405 + // Workaround an array initialization bug in gcc 4.5 and earlier. + format_arg_store(const Args &... args) { + data_ = {internal::make_arg(args)...}; + } +#else + format_arg_store(const Args &... args) + : data_{internal::make_arg(args)...} {} +#endif +}; + +#if !FMT_USE_CONSTEXPR +template +const uint64_t format_arg_store::TYPES = get_types(); +#endif + +/** + \rst + Constructs an `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` can + be omitted in which case it defaults to `~fmt::context`. + \endrst + */ +template +inline format_arg_store + make_format_args(const Args & ... args) { + return format_arg_store(args...); +} + +template +inline format_arg_store + make_format_args(const Args & ... args) { + return format_arg_store(args...); +} + +/** Formatting arguments. */ +template +class basic_format_args { + public: + typedef unsigned size_type; + typedef basic_format_arg format_arg; + + private: + // To reduce compiled code size per formatting function call, types of first + // max_packed_args arguments are passed in the types_ field. + uint64_t types_; + union { + // If the number of arguments is less than max_packed_args, the argument + // values are stored in values_, otherwise they are stored in args_. + // This is done to reduce compiled code size as storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const internal::value *values_; + const format_arg *args_; + }; + + typename internal::type type(unsigned index) const { + unsigned shift = index * 4; + uint64_t mask = 0xf; + return static_cast( + (types_ & (mask << shift)) >> shift); + } + + friend class internal::arg_map; + + void set_data(const internal::value *values) { values_ = values; } + void set_data(const format_arg *args) { args_ = args; } + + format_arg do_get(size_type index) const { + int64_t signed_types = static_cast(types_); + if (signed_types < 0) { + uint64_t num_args = -signed_types; + return index < num_args ? args_[index] : format_arg(); + } + format_arg arg; + if (index > internal::max_packed_args) + return arg; + arg.type_ = type(index); + if (arg.type_ == internal::none_type) + return arg; + internal::value &val = arg.value_; + val = values_[index]; + return arg; + } + + public: + basic_format_args() : types_(0) {} + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template + basic_format_args(const format_arg_store &store) + : types_(store.TYPES) { + set_data(store.data_); + } + + /** Returns the argument at specified index. */ + format_arg get(size_type index) const { + format_arg arg = do_get(index); + return arg.type_ == internal::name_arg_type ? + arg.value_.as_named_arg().template deserialize() : arg; + } + + unsigned max_size() const { + int64_t signed_types = static_cast(types_); + return static_cast( + signed_types < 0 ? + -signed_types : static_cast(internal::max_packed_args)); + } +}; + +/** An alias to ``basic_format_args``. */ +// It is a separate type rather than a typedef to make symbols readable. +struct format_args: basic_format_args { + template + format_args(Args && ... arg) + : basic_format_args(std::forward(arg)...) {} +}; +struct wformat_args : basic_format_args { + template + wformat_args(Args && ... arg) + : basic_format_args(std::forward(arg)...) {} +}; + +namespace internal { +template +struct named_arg_base { + basic_string_view name; + + // Serialized value. + mutable char data[sizeof(basic_format_arg)]; + + named_arg_base(basic_string_view nm) : name(nm) {} + + template + basic_format_arg deserialize() const { + basic_format_arg arg; + std::memcpy(&arg, data, sizeof(basic_format_arg)); + return arg; + } +}; + +template +struct named_arg : named_arg_base { + const T &value; + + named_arg(basic_string_view name, const T &val) + : named_arg_base(name), value(val) {} +}; +} + +/** + \rst + Returns a named argument to be used in a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template +inline internal::named_arg arg(string_view name, const T &arg) { + return internal::named_arg(name, arg); +} + +template +inline internal::named_arg arg(wstring_view name, const T &arg) { + return internal::named_arg(name, arg); +} + +// This function template is deleted intentionally to disable nested named +// arguments as in ``format("{}", arg("a", arg("b", 42)))``. +template +void arg(S, internal::named_arg) FMT_DELETED; + +enum color { black, red, green, yellow, blue, magenta, cyan, white }; + +FMT_API void vprint_colored(color c, string_view format, format_args args); +FMT_API void vprint_colored(color c, wstring_view format, wformat_args args); + +/** + Formats a string and prints it to stdout using ANSI escape sequences to + specify color (experimental). + Example: + fmt::print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); + */ +template +inline void print_colored(color c, string_view format_str, + const Args & ... args) { + vprint_colored(c, format_str, make_format_args(args...)); +} + +template +inline void print_colored(color c, wstring_view format_str, + const Args & ... args) { + vprint_colored(c, format_str, make_format_args(args...)); +} + +format_context::iterator vformat_to( + internal::buffer &buf, string_view format_str, format_args args); +wformat_context::iterator vformat_to( + internal::wbuffer &buf, wstring_view format_str, wformat_args args); + +template +struct is_contiguous : std::false_type {}; + +template +struct is_contiguous> : std::true_type {}; + +template +struct is_contiguous> : std::true_type {}; + +/** Formats a string and writes the output to ``out``. */ +template +typename std::enable_if< + is_contiguous::value, std::back_insert_iterator>::type + vformat_to(std::back_insert_iterator out, + string_view format_str, format_args args) { + auto& container = internal::get_container(out); + internal::container_buffer buf(container); + vformat_to(buf, format_str, args); + return std::back_inserter(container); +} + +template +typename std::enable_if< + is_contiguous::value, std::back_insert_iterator>::type + vformat_to(std::back_insert_iterator out, + wstring_view format_str, wformat_args args) { + auto& container = internal::get_container(out); + internal::container_buffer buf(container); + vformat_to(buf, format_str, args); + return std::back_inserter(container); +} + +std::string vformat(string_view format_str, format_args args); +std::wstring vformat(wstring_view format_str, wformat_args args); + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}", 42); + \endrst +*/ +template +inline std::string format(string_view format_str, const Args & ... args) { + // This should be just + // return vformat(format_str, make_format_args(args...)); + // but gcc has trouble optimizing the latter, so break it down. + format_arg_store as{args...}; + return vformat(format_str, as); +} +template +inline std::wstring format(wstring_view format_str, const Args & ... args) { + format_arg_store as{args...}; + return vformat(format_str, as); +} + +FMT_API void vprint(std::FILE *f, string_view format_str, format_args args); +FMT_API void vprint(std::FILE *f, wstring_view format_str, wformat_args args); + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template +inline void print(std::FILE *f, string_view format_str, const Args & ... args) { + format_arg_store as(args...); + vprint(f, format_str, as); +} +template +inline void print(std::FILE *f, wstring_view format_str, const Args & ... args) { + format_arg_store as(args...); + vprint(f, format_str, as); +} + +FMT_API void vprint(string_view format_str, format_args args); +FMT_API void vprint(wstring_view format_str, wformat_args args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template +inline void print(string_view format_str, const Args & ... args) { + format_arg_store as{args...}; + vprint(format_str, as); +} + +template +inline void print(wstring_view format_str, const Args & ... args) { + format_arg_store as(args...); + vprint(format_str, as); +} +FMT_END_NAMESPACE + +#endif // FMT_CORE_H_ diff --git a/src/fmt/format-inl.h b/src/fmt/format-inl.h new file mode 100644 index 0000000..00c97c3 --- /dev/null +++ b/src/fmt/format-inl.h @@ -0,0 +1,546 @@ +// Formatting library for C++ +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_FORMAT_INL_H_ +#define FMT_FORMAT_INL_H_ + +#include "format.h" + +#include + +#include +#include +#include +#include +#include +#include // for std::ptrdiff_t +#include + +#if defined(_WIN32) && defined(__MINGW32__) +# include +#endif + +#if FMT_USE_WINDOWS_H +# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif +# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) +# include +# else +# define NOMINMAX +# include +# undef NOMINMAX +# endif +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifdef __GNUC__ +// Disable the warning about declaration shadowing because it affects too +// many valid cases. +# pragma GCC diagnostic ignored "-Wshadow" +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4702) // unreachable code +// Disable deprecation warning for strerror. The latter is not called but +// MSVC fails to detect it. +# pragma warning(disable: 4996) +#endif + +// Dummy implementations of strerror_r and strerror_s called if corresponding +// system functions are not available. +inline fmt::internal::null<> strerror_r(int, char *, ...) { + return fmt::internal::null<>(); +} +inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) { + return fmt::internal::null<>(); +} + +FMT_BEGIN_NAMESPACE + +FMT_FUNC format_error::~format_error() throw() {} +FMT_FUNC system_error::~system_error() FMT_DTOR_NOEXCEPT {} + +namespace { + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) +# define FMT_SWPRINTF snwprintf +#else +# define FMT_SWPRINTF swprintf +#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) + +const char RESET_COLOR[] = "\x1b[0m"; +const wchar_t WRESET_COLOR[] = L"\x1b[0m"; + +typedef void (*FormatFunc)(internal::buffer &, int, string_view); + +// Portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +int safe_strerror( + int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { + FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer"); + + class dispatcher { + private: + int error_code_; + char *&buffer_; + std::size_t buffer_size_; + + // A noop assignment operator to avoid bogus warnings. + void operator=(const dispatcher &) {} + + // Handle the result of XSI-compliant version of strerror_r. + int handle(int result) { + // glibc versions before 2.13 return result in errno. + return result == -1 ? errno : result; + } + + // Handle the result of GNU-specific version of strerror_r. + int handle(char *message) { + // If the buffer is full then the message is probably truncated. + if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) + return ERANGE; + buffer_ = message; + return 0; + } + + // Handle the case when strerror_r is not available. + int handle(internal::null<>) { + return fallback(strerror_s(buffer_, buffer_size_, error_code_)); + } + + // Fallback to strerror_s when strerror_r is not available. + int fallback(int result) { + // If the buffer is full then the message is probably truncated. + return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? + ERANGE : result; + } + + // Fallback to strerror if strerror_r and strerror_s are not available. + int fallback(internal::null<>) { + errno = 0; + buffer_ = strerror(error_code_); + return errno; + } + + public: + dispatcher(int err_code, char *&buf, std::size_t buf_size) + : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} + + int run() { + return handle(strerror_r(error_code_, buffer_, buffer_size_)); + } + }; + return dispatcher(error_code, buffer, buffer_size).run(); +} + +void format_error_code(internal::buffer &out, int error_code, + string_view message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.resize(0); + static const char SEP[] = ": "; + static const char ERROR_STR[] = "error "; + // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. + std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + typedef internal::int_traits::main_type main_type; + main_type abs_value = static_cast(error_code); + if (internal::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += internal::count_digits(abs_value); + writer w(out); + if (message.size() <= inline_buffer_size - error_code_size) { + w.write(message); + w.write(SEP); + } + w.write(ERROR_STR); + w.write(error_code); + assert(out.size() <= inline_buffer_size); +} + +void report_error(FormatFunc func, int error_code, + string_view message) FMT_NOEXCEPT { + memory_buffer full_message; + func(full_message, error_code, message); + // Use Writer::data instead of Writer::c_str to avoid potential memory + // allocation. + std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} +} // namespace + +class locale { + private: + std::locale locale_; + + public: + explicit locale(std::locale loc = std::locale()) : locale_(loc) {} + std::locale get() { return locale_; } +}; + +template +FMT_FUNC Char internal::thousands_sep(locale_provider *lp) { + std::locale loc = lp ? lp->locale().get() : std::locale(); + return std::use_facet>(loc).thousands_sep(); +} + +FMT_FUNC void system_error::init( + int err_code, string_view format_str, format_args args) { + error_code_ = err_code; + memory_buffer buffer; + format_system_error(buffer, err_code, vformat(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(to_string(buffer)); +} + +namespace internal { +template +int char_traits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, value) : + FMT_SNPRINTF(buffer, size, format, precision, value); + } + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, width, value) : + FMT_SNPRINTF(buffer, size, format, width, precision, value); +} + +template +int char_traits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + FMT_SWPRINTF(buffer, size, format, value) : + FMT_SWPRINTF(buffer, size, format, precision, value); + } + return precision < 0 ? + FMT_SWPRINTF(buffer, size, format, width, value) : + FMT_SWPRINTF(buffer, size, format, width, precision, value); +} + +template +const char basic_data::DIGITS[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, \ + factor * 100, \ + factor * 1000, \ + factor * 10000, \ + factor * 100000, \ + factor * 1000000, \ + factor * 10000000, \ + factor * 100000000, \ + factor * 1000000000 + +template +const uint32_t basic_data::POWERS_OF_10_32[] = { + 0, FMT_POWERS_OF_10(1) +}; + +template +const uint64_t basic_data::POWERS_OF_10_64[] = { + 0, + FMT_POWERS_OF_10(1), + FMT_POWERS_OF_10(1000000000ull), + 10000000000000000000ull +}; + +// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. +// These are generated by support/compute-powers.py. +template +const uint64_t basic_data::POW10_SIGNIFICANDS[] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea, + 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, + 0xbe5691ef416bd60c, 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, 0xc21094364dfb5637, + 0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5, + 0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8, + 0x87625f056c7c4a8b, 0xc9bcff6034c13053, 0x964e858c91ba2655, 0xdff9772470297ebd, + 0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3, + 0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c, + 0x9c40000000000000, 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, 0x9f4f2726179a2245, + 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db, 0xc45d1df942711d9a, + 0x924d692ca61be758, 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3, + 0xde469fbd99a05fe3, 0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, + 0x88fcf317f22241e2, 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, 0x8bab8eefb6409c1a, + 0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429, + 0x80444b5e7aa7cf85, 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b +}; + +// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding +// to significands above. +template +const int16_t basic_data::POW10_EXPONENTS[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066 +}; + +FMT_FUNC fp operator*(fp x, fp y) { + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = x.f >> 32, b = x.f & mask; + uint64_t c = y.f >> 32, d = y.f & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64); +} + +FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) { + const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10) + int index = static_cast(std::ceil( + (min_exponent + fp::fp_significand_size - 1) * one_over_log2_10)); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between two consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]); +} +} // namespace internal + +#if FMT_USE_WINDOWS_H + +FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) { + static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; + if (s.size() > INT_MAX) + FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG)); + int s_size = static_cast(s.size()); + if (s_size == 0) { + // MultiByteToWideChar does not support zero length, handle separately. + buffer_.resize(1); + buffer_[0] = 0; + return; + } + + int length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0); + if (length == 0) + FMT_THROW(windows_error(GetLastError(), ERROR_MSG)); + buffer_.resize(length + 1); + length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); + if (length == 0) + FMT_THROW(windows_error(GetLastError(), ERROR_MSG)); + buffer_[length] = 0; +} + +FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) { + if (int error_code = convert(s)) { + FMT_THROW(windows_error(error_code, + "cannot convert string from UTF-16 to UTF-8")); + } +} + +FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) { + if (s.size() > INT_MAX) + return ERROR_INVALID_PARAMETER; + int s_size = static_cast(s.size()); + if (s_size == 0) { + // WideCharToMultiByte does not support zero length, handle separately. + buffer_.resize(1); + buffer_[0] = 0; + return 0; + } + + int length = WideCharToMultiByte( + CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL); + if (length == 0) + return GetLastError(); + buffer_.resize(length + 1); + length = WideCharToMultiByte( + CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); + if (length == 0) + return GetLastError(); + buffer_[length] = 0; + return 0; +} + +FMT_FUNC void windows_error::init( + int err_code, string_view format_str, format_args args) { + error_code_ = err_code; + memory_buffer buffer; + internal::format_windows_error(buffer, err_code, vformat(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(to_string(buffer)); +} + +FMT_FUNC void internal::format_windows_error( + internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT { + FMT_TRY { + wmemory_buffer buf; + buf.resize(inline_buffer_size); + for (;;) { + wchar_t *system_message = &buf[0]; + int result = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + system_message, static_cast(buf.size()), FMT_NULL); + if (result != 0) { + utf16_to_utf8 utf8_message; + if (utf8_message.convert(system_message) == ERROR_SUCCESS) { + writer w(out); + w.write(message); + w.write(": "); + w.write(utf8_message); + return; + } + break; + } + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + break; // Can't get error message, report error code instead. + buf.resize(buf.size() * 2); + } + } FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +#endif // FMT_USE_WINDOWS_H + +FMT_FUNC void format_system_error( + internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT { + FMT_TRY { + memory_buffer buf; + buf.resize(inline_buffer_size); + for (;;) { + char *system_message = &buf[0]; + int result = safe_strerror(error_code, system_message, buf.size()); + if (result == 0) { + writer w(out); + w.write(message); + w.write(": "); + w.write(system_message); + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buf.resize(buf.size() * 2); + } + } FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +template +void basic_fixed_buffer::grow(std::size_t) { + FMT_THROW(std::runtime_error("buffer overflow")); +} + +FMT_FUNC void internal::error_handler::on_error(const char *message) { + FMT_THROW(format_error(message)); +} + +FMT_FUNC void report_system_error( + int error_code, fmt::string_view message) FMT_NOEXCEPT { + report_error(format_system_error, error_code, message); +} + +#if FMT_USE_WINDOWS_H +FMT_FUNC void report_windows_error( + int error_code, fmt::string_view message) FMT_NOEXCEPT { + report_error(internal::format_windows_error, error_code, message); +} +#endif + +FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) { + memory_buffer buffer; + vformat_to(buffer, format_str, args); + std::fwrite(buffer.data(), 1, buffer.size(), f); +} + +FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) { + wmemory_buffer buffer; + vformat_to(buffer, format_str, args); + std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f); +} + +FMT_FUNC void vprint(string_view format_str, format_args args) { + vprint(stdout, format_str, args); +} + +FMT_FUNC void vprint(wstring_view format_str, wformat_args args) { + vprint(stdout, format_str, args); +} + +FMT_FUNC void vprint_colored(color c, string_view format, format_args args) { + char escape[] = "\x1b[30m"; + escape[3] = static_cast('0' + c); + std::fputs(escape, stdout); + vprint(format, args); + std::fputs(RESET_COLOR, stdout); +} + +FMT_FUNC void vprint_colored(color c, wstring_view format, wformat_args args) { + wchar_t escape[] = L"\x1b[30m"; + escape[3] = static_cast('0' + c); + std::fputws(escape, stdout); + vprint(format, args); + std::fputws(WRESET_COLOR, stdout); +} + +FMT_FUNC locale locale_provider::locale() { return fmt::locale(); } + +FMT_END_NAMESPACE + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#endif // FMT_FORMAT_INL_H_ diff --git a/src/fmt/format.cc b/src/fmt/format.cc new file mode 100644 index 0000000..9ebce0c --- /dev/null +++ b/src/fmt/format.cc @@ -0,0 +1,47 @@ +// Formatting library for C++ +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/format-inl.h" + +FMT_BEGIN_NAMESPACE + +template struct internal::basic_data; + +// Explicit instantiations for char. + +template FMT_API char internal::thousands_sep(locale_provider *lp); + +template void basic_fixed_buffer::grow(std::size_t); + +template void internal::arg_map::init( + const basic_format_args &args); + +template FMT_API int internal::char_traits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, double value); + +template FMT_API int internal::char_traits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, long double value); + +// Explicit instantiations for wchar_t. + +template FMT_API wchar_t internal::thousands_sep(locale_provider *lp); + +template void basic_fixed_buffer::grow(std::size_t); + +template void internal::arg_map::init( + const basic_format_args &args); + +template FMT_API int internal::char_traits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, double value); + +template FMT_API int internal::char_traits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, long double value); +FMT_END_NAMESPACE diff --git a/src/fmt/format.h b/src/fmt/format.h new file mode 100644 index 0000000..0f2baab --- /dev/null +++ b/src/fmt/format.h @@ -0,0 +1,3698 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - present, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#ifdef _SECURE_SCL +# define FMT_SECURE_SCL _SECURE_SCL +#else +# define FMT_SECURE_SCL 0 +#endif + +#if FMT_SECURE_SCL +# include +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#ifdef __GNUC__ +# if FMT_GCC_VERSION >= 406 +# pragma GCC diagnostic push + +// Disable the warning about declaration shadowing because it affects too +// many valid cases. +# pragma GCC diagnostic ignored "-Wshadow" + +// Disable the warning about implicit conversions that may change the sign of +// an integer; silencing it otherwise would require many explicit casts. +# pragma GCC diagnostic ignored "-Wsign-conversion" +# endif +#endif + +#ifdef __clang__ +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#endif + +#if defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#elif defined(__ICL) +# define FMT_ICC_VERSION __ICL +#endif + +#if defined(__clang__) && !defined(FMT_ICC_VERSION) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +# pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template" +# pragma clang diagnostic ignored "-Wpadded" +#endif + +#ifdef __GNUC_LIBSTD__ +# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# define FMT_THROW(x) throw x +# else +# define FMT_THROW(x) assert(false) +# endif +#endif + +#ifndef FMT_USE_USER_DEFINED_LITERALS +// For Intel's compiler both it and the system gcc/msc must support UDLs. +# if (FMT_HAS_FEATURE(cxx_user_literals) || \ + FMT_GCC_VERSION >= 407 || FMT_MSC_VER >= 1900) && \ + (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif +#endif + +#if FMT_USE_USER_DEFINED_LITERALS && !defined(FMT_ICC_VERSION) && \ + ((FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L) || \ + (defined(FMT_CLANG_VERSION) && FMT_CLANG_VERSION >= 304)) +# define FMT_UDL_TEMPLATE 1 +#else +# define FMT_UDL_TEMPLATE 0 +#endif + +#ifndef FMT_USE_EXTERN_TEMPLATES +# ifndef FMT_HEADER_ONLY +# define FMT_USE_EXTERN_TEMPLATES \ + (FMT_CLANG_VERSION >= 209 || (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) +# else +# define FMT_USE_EXTERN_TEMPLATES 0 +# endif +#endif + +#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || FMT_MSC_VER >= 1600 +# define FMT_USE_TRAILING_RETURN 1 +#endif + +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519 +#ifndef _MSC_VER +# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +# endif + +# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +# endif +#endif + +// A workaround for gcc 4.4 that doesn't support union members with ctors. +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 404 +# define FMT_UNION struct +#else +# define FMT_UNION union +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the +// MSVC intrinsics if the clz and clzll builtins are not available. +#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED) +# include // _BitScanReverse, _BitScanReverse64 + +FMT_BEGIN_NAMESPACE +namespace internal { +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# ifndef __clang__ +# pragma intrinsic(_BitScanReverse) +# endif +inline uint32_t clz(uint32_t x) { + unsigned long r = 0; + _BitScanReverse(&r, x); + + assert(x != 0); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. +# pragma warning(suppress: 6102) + return 31 - r; +} +# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) + +# if defined(_WIN64) && !defined(__clang__) +# pragma intrinsic(_BitScanReverse64) +# endif + +inline uint32_t clzll(uint64_t x) { + unsigned long r = 0; +# ifdef _WIN64 + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + + assert(x != 0); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. +# pragma warning(suppress: 6102) + return 63 - r; +} +# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) +} +FMT_END_NAMESPACE +#endif + +FMT_BEGIN_NAMESPACE +namespace internal { + +// An equivalent of `*reinterpret_cast(&source)` that doesn't produce +// undefined behavior (e.g. due to type aliasing). +// Example: uint64_t d = bit_cast(2.718); +template +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + Dest dest; + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// An implementation of begin and end for pre-C++11 compilers such as gcc 4. +template +FMT_CONSTEXPR auto begin(const C &c) -> decltype(c.begin()) { + return c.begin(); +} +template +FMT_CONSTEXPR T *begin(T (&array)[N]) FMT_NOEXCEPT { return array; } +template +FMT_CONSTEXPR auto end(const C &c) -> decltype(c.end()) { return c.end(); } +template +FMT_CONSTEXPR T *end(T (&array)[N]) FMT_NOEXCEPT { return array + N; } + +// For std::result_of in gcc 4.4. +template +struct function { + template + struct result { typedef Result type; }; +}; + +struct dummy_int { + int data[2]; + operator int() const { return 0; } +}; +typedef std::numeric_limits fputil; + +// Dummy implementations of system functions such as signbit and ecvt called +// if the latter are not available. +inline dummy_int signbit(...) { return dummy_int(); } +inline dummy_int _ecvt_s(...) { return dummy_int(); } +inline dummy_int isinf(...) { return dummy_int(); } +inline dummy_int _finite(...) { return dummy_int(); } +inline dummy_int isnan(...) { return dummy_int(); } +inline dummy_int _isnan(...) { return dummy_int(); } + +// A handmade floating-point number f * pow(2, e). +class fp { + private: + typedef uint64_t significand_type; + + // All sizes are in bits. + static constexpr int char_size = std::numeric_limits::digits; + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + static constexpr int double_significand_size = + std::numeric_limits::digits - 1; + static constexpr uint64_t implicit_bit = 1ull << double_significand_size; + + public: + significand_type f; + int e; + + static constexpr int fp_significand_size = + sizeof(significand_type) * char_size; + + fp(uint64_t f, int e): f(f), e(e) {} + + // Constructs fp from an IEEE754 double. It is a template to prevent compile + // errors on platforms where double is not IEEE754. + template + explicit fp(Double d) { + // Assume double is in the format [sign][exponent][significand]. + typedef std::numeric_limits limits; + const int double_size = sizeof(Double) * char_size; + const int exponent_size = + double_size - double_significand_size - 1; // -1 for sign + const uint64_t significand_mask = implicit_bit - 1; + const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask; + const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; + auto u = bit_cast(d); + auto biased_e = (u & exponent_mask) >> double_significand_size; + f = u & significand_mask; + if (biased_e != 0) + f += implicit_bit; + else + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + e = biased_e - exponent_bias - double_significand_size; + } + + // Normalizes the value converted from double and multiplied by (1 << SHIFT). + template + void normalize() { + // Handle subnormals. + auto shifted_implicit_bit = implicit_bit << SHIFT; + while ((f & shifted_implicit_bit) == 0) { + f <<= 1; + --e; + } + // Subtract 1 to account for hidden bit. + auto offset = fp_significand_size - double_significand_size - SHIFT - 1; + f <<= offset; + e -= offset; + } +}; + +// Returns an fp number representing x - y. Result may not be normalized. +inline fp operator-(fp x, fp y) { + FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands"); + return fp(x.f - y.f, x.e); +} + +// Computes an fp number r with r.f = x.f * y.f / pow(2, 32) rounded to nearest +// with half-up tie breaking, r.e = x.e + y.e + 32. Result may not be normalized. +fp operator*(fp x, fp y); + +// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its +// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3. +fp get_cached_power(int min_exponent, int &pow10_exponent); + +template +typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) { +#if __cplusplus >= 201103L || FMT_MSC_VER >= 1700 + return std::allocator_traits::allocate(alloc, n); +#else + return alloc.allocate(n); +#endif +} + +// A helper function to suppress bogus "conditional expression is constant" +// warnings. +template +inline T const_check(T value) { return value; } +} // namespace internal +FMT_END_NAMESPACE + +namespace std { +// Standard permits specialization of std::numeric_limits. This specialization +// is used to resolve ambiguity between isinf and std::isinf in glibc: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891 +// and the same for isnan and signbit. +template <> +class numeric_limits : + public std::numeric_limits { + public: + // Portable version of isinf. + template + static bool isinfinity(T x) { + using namespace fmt::internal; + // The resolution "priority" is: + // isinf macro > std::isinf > ::isinf > fmt::internal::isinf + if (const_check(sizeof(isinf(x)) != sizeof(dummy_int))) + return isinf(x) != 0; + return !_finite(static_cast(x)); + } + + // Portable version of isnan. + template + static bool isnotanumber(T x) { + using namespace fmt::internal; + if (const_check(sizeof(isnan(x)) != sizeof(fmt::internal::dummy_int))) + return isnan(x) != 0; + return _isnan(static_cast(x)) != 0; + } + + // Portable version of signbit. + static bool isnegative(double x) { + using namespace fmt::internal; + if (const_check(sizeof(signbit(x)) != sizeof(fmt::internal::dummy_int))) + return signbit(x) != 0; + if (x < 0) return true; + if (!isnotanumber(x)) return false; + int dec = 0, sign = 0; + char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. + _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign); + return sign != 0; + } +}; +} // namespace std + +FMT_BEGIN_NAMESPACE +template +class basic_writer; + +/** A formatting error such as invalid format string. */ +class format_error : public std::runtime_error { + public: + explicit format_error(const char *message) + : std::runtime_error(message) {} + + explicit format_error(const std::string &message) + : std::runtime_error(message) {} + + FMT_API ~format_error() throw(); +}; + +namespace internal { + +// Casts nonnegative integer to unsigned. +template +FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::type>(value); +} + +#if FMT_SECURE_SCL +template +struct checked { typedef stdext::checked_array_iterator type; }; + +// Make a checked iterator to avoid warnings on MSVC. +template +inline stdext::checked_array_iterator make_checked(T *p, std::size_t size) { + return {p, size}; +} +#else +template +struct checked { typedef T *type; }; +template +inline T *make_checked(T *p, std::size_t) { return p; } +#endif + +template +template +void basic_buffer::append(const U *begin, const U *end) { + std::size_t new_size = size_ + internal::to_unsigned(end - begin); + reserve(new_size); + std::uninitialized_copy(begin, end, + internal::make_checked(ptr_, capacity_) + size_); + size_ = new_size; +} +} // namespace internal + +// A wrapper around std::locale used to reduce compile times since +// is very heavy. +class locale; + +class locale_provider { + public: + virtual ~locale_provider() {} + virtual fmt::locale locale(); +}; + +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; + +/** + \rst + A dynamically growing memory buffer for trivially copyable/constructible types + with the first ``SIZE`` elements stored in the object itself. + + You can use one of the following typedefs for common character types: + + +----------------+------------------------------+ + | Type | Definition | + +================+==============================+ + | memory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + | wmemory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + + **Example**:: + + fmt::memory_buffer out; + format_to(out, "The answer is {}.", 42); + + This will write the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42. + + The output can be converted to an ``std::string`` with ``to_string(out)``. + \endrst + */ +template > +class basic_memory_buffer: private Allocator, public internal::basic_buffer { + private: + T store_[SIZE]; + + // Deallocate memory allocated by the buffer. + void deallocate() { + T* data = this->data(); + if (data != store_) Allocator::deallocate(data, this->capacity()); + } + + protected: + void grow(std::size_t size) FMT_OVERRIDE; + + public: + explicit basic_memory_buffer(const Allocator &alloc = Allocator()) + : Allocator(alloc) { + this->set(store_, SIZE); + } + ~basic_memory_buffer() { deallocate(); } + + private: + // Move data from other to this buffer. + void move(basic_memory_buffer &other) { + Allocator &this_alloc = *this, &other_alloc = other; + this_alloc = std::move(other_alloc); + T* data = other.data(); + std::size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { + this->set(store_, capacity); + std::uninitialized_copy(other.store_, other.store_ + size, + internal::make_checked(store_, capacity)); + } else { + this->set(data, capacity); + // Set pointer to the inline array so that delete is not called + // when deallocating. + other.set(other.store_, 0); + } + this->resize(size); + } + + public: + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + basic_memory_buffer(basic_memory_buffer &&other) { + move(other); + } + + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + basic_memory_buffer &operator=(basic_memory_buffer &&other) { + assert(this != &other); + deallocate(); + move(other); + return *this; + } + + // Returns a copy of the allocator associated with this buffer. + Allocator get_allocator() const { return *this; } +}; + +template +void basic_memory_buffer::grow(std::size_t size) { + std::size_t old_capacity = this->capacity(); + std::size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) + new_capacity = size; + T *old_data = this->data(); + T *new_data = internal::allocate(*this, new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::uninitialized_copy(old_data, old_data + this->size(), + internal::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) + Allocator::deallocate(old_data, old_capacity); +} + +typedef basic_memory_buffer memory_buffer; +typedef basic_memory_buffer wmemory_buffer; + +/** + \rst + A fixed-size memory buffer. For a dynamically growing buffer use + :class:`fmt::basic_memory_buffer`. + + Trying to increase the buffer size past the initial capacity will throw + ``std::runtime_error``. + \endrst + */ +template +class basic_fixed_buffer : public internal::basic_buffer { + public: + /** + \rst + Constructs a :class:`fmt::basic_fixed_buffer` object for *array* of the + given size. + \endrst + */ + basic_fixed_buffer(Char *array, std::size_t size) { + this->set(array, size); + } + + /** + \rst + Constructs a :class:`fmt::basic_fixed_buffer` object for *array* of the + size known at compile time. + \endrst + */ + template + explicit basic_fixed_buffer(Char (&array)[SIZE]) { + this->set(array, SIZE); + } + + protected: + FMT_API void grow(std::size_t size) FMT_OVERRIDE; +}; + +namespace internal { + +template +struct char_traits; + +template <> +struct char_traits { + // Formats a floating-point number. + template + FMT_API static int format_float(char *buffer, std::size_t size, + const char *format, unsigned width, int precision, T value); +}; + +template <> +struct char_traits { + template + FMT_API static int format_float(wchar_t *buffer, std::size_t size, + const wchar_t *format, unsigned width, int precision, T value); +}; + +#if FMT_USE_EXTERN_TEMPLATES +extern template int char_traits::format_float( + char *buffer, std::size_t size, const char* format, unsigned width, + int precision, double value); +extern template int char_traits::format_float( + char *buffer, std::size_t size, const char* format, unsigned width, + int precision, long double value); + +extern template int char_traits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width, + int precision, double value); +extern template int char_traits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width, + int precision, long double value); +#endif + +template +inline typename std::enable_if< + is_contiguous::value, + typename checked::type>::type + reserve(std::back_insert_iterator &it, std::size_t n) { + Container &c = internal::get_container(it); + std::size_t size = c.size(); + c.resize(size + n); + return make_checked(&c[size], n); +} + +template +inline Iterator &reserve(Iterator &it, std::size_t) { return it; } + +template +class null_terminating_iterator; + +template +FMT_CONSTEXPR_DECL const Char *pointer_from(null_terminating_iterator it); + +// An iterator that produces a null terminator on *end. This simplifies parsing +// and allows comparing the performance of processing a null-terminated string +// vs string_view. +template +class null_terminating_iterator { + public: + typedef std::ptrdiff_t difference_type; + typedef Char value_type; + typedef const Char* pointer; + typedef const Char& reference; + typedef std::random_access_iterator_tag iterator_category; + + null_terminating_iterator() : ptr_(0), end_(0) {} + + FMT_CONSTEXPR null_terminating_iterator(const Char *ptr, const Char *end) + : ptr_(ptr), end_(end) {} + + template + FMT_CONSTEXPR explicit null_terminating_iterator(const Range &r) + : ptr_(r.begin()), end_(r.end()) {} + + null_terminating_iterator &operator=(const Char *ptr) { + assert(ptr <= end_); + ptr_ = ptr; + return *this; + } + + FMT_CONSTEXPR Char operator*() const { + return ptr_ != end_ ? *ptr_ : 0; + } + + FMT_CONSTEXPR null_terminating_iterator operator++() { + ++ptr_; + return *this; + } + + FMT_CONSTEXPR null_terminating_iterator operator++(int) { + null_terminating_iterator result(*this); + ++ptr_; + return result; + } + + FMT_CONSTEXPR null_terminating_iterator operator--() { + --ptr_; + return *this; + } + + FMT_CONSTEXPR null_terminating_iterator operator+(difference_type n) { + return null_terminating_iterator(ptr_ + n, end_); + } + + FMT_CONSTEXPR null_terminating_iterator operator-(difference_type n) { + return null_terminating_iterator(ptr_ - n, end_); + } + + FMT_CONSTEXPR null_terminating_iterator operator+=(difference_type n) { + ptr_ += n; + return *this; + } + + FMT_CONSTEXPR difference_type operator-( + null_terminating_iterator other) const { + return ptr_ - other.ptr_; + } + + FMT_CONSTEXPR bool operator!=(null_terminating_iterator other) const { + return ptr_ != other.ptr_; + } + + bool operator>=(null_terminating_iterator other) const { + return ptr_ >= other.ptr_; + } + + // This should be a friend specialization pointer_from but the latter + // doesn't compile by gcc 5.1 due to a compiler bug. + template + friend FMT_CONSTEXPR_DECL const CharT *pointer_from( + null_terminating_iterator it); + + private: + const Char *ptr_; + const Char *end_; +}; + +template +FMT_CONSTEXPR const T *pointer_from(const T *p) { return p; } + +template +FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator it) { + return it.ptr_; +} + +// An output iterator that counts the number of objects written to it and +// discards them. +template +class counting_iterator { + private: + std::size_t count_; + mutable T blackhole_; + + public: + typedef std::output_iterator_tag iterator_category; + typedef T value_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T& reference; + typedef counting_iterator _Unchecked_type; // Mark iterator as checked. + + counting_iterator(): count_(0) {} + + std::size_t count() const { return count_; } + + counting_iterator& operator++() { + ++count_; + return *this; + } + + counting_iterator operator++(int) { return ++*this; } + + T &operator*() const { return blackhole_; } +}; + +// An output iterator that truncates the output and counts the number of objects +// written to it. +template +class truncating_iterator { + private: + typedef std::iterator_traits traits; + + OutputIt out_; + std::size_t limit_; + std::size_t count_; + mutable typename traits::value_type blackhole_; + + public: + typedef std::output_iterator_tag iterator_category; + typedef typename traits::value_type value_type; + typedef typename traits::difference_type difference_type; + typedef typename traits::pointer pointer; + typedef typename traits::reference reference; + typedef truncating_iterator _Unchecked_type; // Mark iterator as checked. + + truncating_iterator(OutputIt out, std::size_t limit) + : out_(out), limit_(limit), count_(0) {} + + OutputIt base() const { return out_; } + std::size_t count() const { return count_; } + + truncating_iterator& operator++() { + if (count_++ < limit_) + ++out_; + return *this; + } + + truncating_iterator operator++(int) { return ++*this; } + + reference operator*() const { return count_ < limit_ ? *out_ : blackhole_; } +}; + +// Returns true if value is negative, false otherwise. +// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. +template +FMT_CONSTEXPR typename std::enable_if< + std::numeric_limits::is_signed, bool>::type is_negative(T value) { + return value < 0; +} +template +FMT_CONSTEXPR typename std::enable_if< + !std::numeric_limits::is_signed, bool>::type is_negative(T) { + return false; +} + +template +struct int_traits { + // Smallest of uint32_t and uint64_t that is large enough to represent + // all values of T. + typedef typename std::conditional< + std::numeric_limits::digits <= 32, uint32_t, uint64_t>::type main_type; +}; + +// Static data is placed in this class template to allow header-only +// configuration. +template +struct FMT_API basic_data { + static const uint32_t POWERS_OF_10_32[]; + static const uint64_t POWERS_OF_10_64[]; + static const uint64_t POW10_SIGNIFICANDS[]; + static const int16_t POW10_EXPONENTS[]; + static const char DIGITS[]; +}; + +#if FMT_USE_EXTERN_TEMPLATES +extern template struct basic_data; +#endif + +typedef basic_data<> data; + +#ifdef FMT_BUILTIN_CLZLL +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +inline unsigned count_digits(uint64_t n) { + // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. + int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; + return to_unsigned(t) - (n < data::POWERS_OF_10_64[t]) + 1; +} +#else +// Fallback version of count_digits used when __builtin_clz is not available. +inline unsigned count_digits(uint64_t n) { + unsigned count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#endif + +#if FMT_HAS_CPP_ATTRIBUTE(always_inline) +# define FMT_ALWAYS_INLINE __attribute__((always_inline)) +#else +# define FMT_ALWAYS_INLINE +#endif + +template +inline char *lg(uint32_t n, Handler h) FMT_ALWAYS_INLINE; + +// Computes g = floor(log10(n)) and calls h.on(n); +template +inline char *lg(uint32_t n, Handler h) { + return n < 100 ? n < 10 ? h.template on<0>(n) : h.template on<1>(n) + : n < 1000000 + ? n < 10000 ? n < 1000 ? h.template on<2>(n) + : h.template on<3>(n) + : n < 100000 ? h.template on<4>(n) + : h.template on<5>(n) + : n < 100000000 ? n < 10000000 ? h.template on<6>(n) + : h.template on<7>(n) + : n < 1000000000 ? h.template on<8>(n) + : h.template on<9>(n); +} + +// An lg handler that formats a decimal number. +// Usage: lg(n, decimal_formatter(buffer)); +class decimal_formatter { + private: + char *buffer_; + + void write_pair(unsigned N, uint32_t index) { + std::memcpy(buffer_ + N, data::DIGITS + index * 2, 2); + } + + public: + explicit decimal_formatter(char *buf) : buffer_(buf) {} + + template char *on(uint32_t u) { + if (N == 0) { + *buffer_ = static_cast(u) + '0'; + } else if (N == 1) { + write_pair(0, u); + } else { + // The idea of using 4.32 fixed-point numbers is based on + // https://github.com/jeaiii/itoa + unsigned n = N - 1; + unsigned a = n / 5 * n * 53 / 16; + uint64_t t = ((1ULL << (32 + a)) / data::POWERS_OF_10_32[n] + 1 - n / 9); + t = ((t * u) >> a) + n / 5 * 4; + write_pair(0, t >> 32); + for (int i = 2; i < N; i += 2) { + t = 100ULL * static_cast(t); + write_pair(i, t >> 32); + } + if (N % 2 == 0) { + buffer_[N] = static_cast( + (10ULL * static_cast(t)) >> 32) + '0'; + } + } + return buffer_ += N + 1; + } +}; + +// An lg handler that formats a decimal number with a terminating null. +class decimal_formatter_null : public decimal_formatter { + public: + explicit decimal_formatter_null(char *buf) : decimal_formatter(buf) {} + + template char *on(uint32_t u) { + char *buf = decimal_formatter::on(u); + *buf = '\0'; + return buf; + } +}; + +#ifdef FMT_BUILTIN_CLZ +// Optional version of count_digits for better performance on 32-bit platforms. +inline unsigned count_digits(uint32_t n) { + int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; + return to_unsigned(t) - (n < data::POWERS_OF_10_32[t]) + 1; +} +#endif + +// A functor that doesn't add a thousands separator. +struct no_thousands_sep { + typedef char char_type; + + template + void operator()(Char *) {} +}; + +// A functor that adds a thousands separator. +template +class add_thousands_sep { + private: + basic_string_view sep_; + + // Index of a decimal digit with the least significant digit having index 0. + unsigned digit_index_; + + public: + typedef Char char_type; + + explicit add_thousands_sep(basic_string_view sep) + : sep_(sep), digit_index_(0) {} + + void operator()(Char *&buffer) { + if (++digit_index_ % 3 != 0) + return; + buffer -= sep_.size(); + std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), + internal::make_checked(buffer, sep_.size())); + } +}; + +template +FMT_API Char thousands_sep(locale_provider *lp); + +// Formats a decimal unsigned integer value writing into buffer. +// thousands_sep is a functor that is called after writing each char to +// add a thousands separator if necessary. +template +inline Char *format_decimal(Char *buffer, UInt value, unsigned num_digits, + ThousandsSep thousands_sep) { + buffer += num_digits; + Char *end = buffer; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = static_cast((value % 100) * 2); + value /= 100; + *--buffer = data::DIGITS[index + 1]; + thousands_sep(buffer); + *--buffer = data::DIGITS[index]; + thousands_sep(buffer); + } + if (value < 10) { + *--buffer = static_cast('0' + value); + return end; + } + unsigned index = static_cast(value * 2); + *--buffer = data::DIGITS[index + 1]; + thousands_sep(buffer); + *--buffer = data::DIGITS[index]; + return end; +} + +template +inline Iterator format_decimal( + Iterator out, UInt value, unsigned num_digits, ThousandsSep sep) { + typedef typename ThousandsSep::char_type char_type; + // Buffer should be large enough to hold all digits (digits10 + 1) and null. + char_type buffer[std::numeric_limits::digits10 + 2]; + format_decimal(buffer, value, num_digits, sep); + return std::copy_n(buffer, num_digits, out); +} + +template +inline It format_decimal(It out, UInt value, unsigned num_digits) { + return format_decimal(out, value, num_digits, no_thousands_sep()); +} + +template +inline Char *format_uint(Char *buffer, UInt value, unsigned num_digits, + bool upper = false) { + buffer += num_digits; + Char *end = buffer; + do { + const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--buffer = BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]; + } while ((value >>= BASE_BITS) != 0); + return end; +} + +template +inline It format_uint(It out, UInt value, unsigned num_digits, + bool upper = false) { + // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1) + // and null. + char buffer[std::numeric_limits::digits / BASE_BITS + 2]; + format_uint(buffer, value, num_digits, upper); + return std::copy_n(buffer, num_digits, out); +} + +#ifndef _WIN32 +# define FMT_USE_WINDOWS_H 0 +#elif !defined(FMT_USE_WINDOWS_H) +# define FMT_USE_WINDOWS_H 1 +#endif + +// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. +// All the functionality that relies on it will be disabled too. +#if FMT_USE_WINDOWS_H +// A converter from UTF-8 to UTF-16. +// It is only provided for Windows since other systems support UTF-8 natively. +class utf8_to_utf16 { + private: + wmemory_buffer buffer_; + + public: + FMT_API explicit utf8_to_utf16(string_view s); + operator wstring_view() const { return wstring_view(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const wchar_t *c_str() const { return &buffer_[0]; } + std::wstring str() const { return std::wstring(&buffer_[0], size()); } +}; + +// A converter from UTF-16 to UTF-8. +// It is only provided for Windows since other systems support UTF-8 natively. +class utf16_to_utf8 { + private: + memory_buffer buffer_; + + public: + utf16_to_utf8() {} + FMT_API explicit utf16_to_utf8(wstring_view s); + operator string_view() const { return string_view(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const char *c_str() const { return &buffer_[0]; } + std::string str() const { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a system error code instead of + // throwing exception on conversion error. This method may still throw + // in case of memory allocation error. + FMT_API int convert(wstring_view s); +}; + +FMT_API void format_windows_error(fmt::internal::buffer &out, int error_code, + fmt::string_view message) FMT_NOEXCEPT; +#endif + +template +struct null {}; +} // namespace internal + +struct monostate {}; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +FMT_CONSTEXPR typename internal::result_of::type + visit(Visitor &&vis, basic_format_arg arg) { + typedef typename Context::char_type char_type; + switch (arg.type_) { + case internal::none_type: + break; + case internal::name_arg_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case internal::int_type: + return vis(arg.value_.int_value); + case internal::uint_type: + return vis(arg.value_.uint_value); + case internal::long_long_type: + return vis(arg.value_.long_long_value); + case internal::ulong_long_type: + return vis(arg.value_.ulong_long_value); + case internal::bool_type: + return vis(arg.value_.int_value != 0); + case internal::char_type: + return vis(static_cast(arg.value_.int_value)); + case internal::double_type: + return vis(arg.value_.double_value); + case internal::long_double_type: + return vis(arg.value_.long_double_value); + case internal::cstring_type: + return vis(arg.value_.string.value); + case internal::string_type: + return vis(basic_string_view( + arg.value_.string.value, arg.value_.string.size)); + case internal::pointer_type: + return vis(arg.value_.pointer); + case internal::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} + +enum alignment { + ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC +}; + +// Flags. +enum {SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8}; + +enum format_spec_tag {fill_tag, align_tag, width_tag, type_tag}; + +// Format specifier. +template +class format_spec { + private: + T value_; + + public: + typedef T value_type; + + explicit format_spec(T value) : value_(value) {} + + T value() const { return value_; } +}; + +// template +// typedef format_spec fill_spec; +template +class fill_spec : public format_spec { + public: + explicit fill_spec(Char value) : format_spec(value) {} +}; + +typedef format_spec width_spec; +typedef format_spec type_spec; + +// An empty format specifier. +struct empty_spec {}; + +// An alignment specifier. +struct align_spec : empty_spec { + unsigned width_; + // Fill is always wchar_t and cast to char if necessary to avoid having + // two specialization of AlignSpec and its subclasses. + wchar_t fill_; + alignment align_; + + FMT_CONSTEXPR align_spec( + unsigned width, wchar_t fill, alignment align = ALIGN_DEFAULT) + : width_(width), fill_(fill), align_(align) {} + + FMT_CONSTEXPR unsigned width() const { return width_; } + FMT_CONSTEXPR wchar_t fill() const { return fill_; } + FMT_CONSTEXPR alignment align() const { return align_; } + + int precision() const { return -1; } +}; + +// Format specifiers. +template +class basic_format_specs : public align_spec { + private: + template + typename std::enable_if::value || + std::is_same::value, void>::type + set(fill_spec fill) { + fill_ = fill.value(); + } + + void set(width_spec width) { + width_ = width.value(); + } + + void set(type_spec type) { + type_ = type.value(); + } + + template + void set(Spec spec, Specs... tail) { + set(spec); + set(tail...); + } + + public: + unsigned flags_; + int precision_; + Char type_; + + FMT_CONSTEXPR basic_format_specs( + unsigned width = 0, char type = 0, wchar_t fill = ' ') + : align_spec(width, fill), flags_(0), precision_(-1), type_(type) {} + + template + explicit basic_format_specs(FormatSpecs... specs) + : align_spec(0, ' '), flags_(0), precision_(-1), type_(0) { + set(specs...); + } + + FMT_CONSTEXPR bool flag(unsigned f) const { return (flags_ & f) != 0; } + FMT_CONSTEXPR int precision() const { return precision_; } + FMT_CONSTEXPR Char type() const { return type_; } +}; + +typedef basic_format_specs format_specs; + +template +FMT_CONSTEXPR unsigned basic_parse_context::next_arg_id() { + if (next_arg_id_ >= 0) + return internal::to_unsigned(next_arg_id_++); + on_error("cannot switch from manual to automatic argument indexing"); + return 0; +} + +struct format_string {}; + +namespace internal { + +template +FMT_CONSTEXPR void handle_int_type_spec(Char spec, Handler &&handler) { + switch (spec) { + case 0: case 'd': + handler.on_dec(); + break; + case 'x': case 'X': + handler.on_hex(); + break; + case 'b': case 'B': + handler.on_bin(); + break; + case 'o': + handler.on_oct(); + break; + case 'n': + handler.on_num(); + break; + default: + handler.on_error(); + } +} + +template +FMT_CONSTEXPR void handle_float_type_spec(Char spec, Handler &&handler) { + switch (spec) { + case 0: case 'g': case 'G': + handler.on_general(); + break; + case 'e': case 'E': + handler.on_exp(); + break; + case 'f': case 'F': + handler.on_fixed(); + break; + case 'a': case 'A': + handler.on_hex(); + break; + default: + handler.on_error(); + break; + } +} + +template +FMT_CONSTEXPR void handle_char_specs( + const basic_format_specs &specs, Handler &&handler) { + if (specs.type() && specs.type() != 'c') { + handler.on_int(); + return; + } + if (specs.align() == ALIGN_NUMERIC || specs.flag(~0u) != 0) + handler.on_error("invalid format specifier for char"); + handler.on_char(); +} + +template +FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler &&handler) { + if (spec == 0 || spec == 's') + handler.on_string(); + else if (spec == 'p') + handler.on_pointer(); + else + handler.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler &&eh) { + if (spec != 0 && spec != 's') + eh.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler &&eh) { + if (spec != 0 && spec != 'p') + eh.on_error("invalid type specifier"); +} + +template +class int_type_checker : private ErrorHandler { + public: + FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_dec() {} + FMT_CONSTEXPR void on_hex() {} + FMT_CONSTEXPR void on_bin() {} + FMT_CONSTEXPR void on_oct() {} + FMT_CONSTEXPR void on_num() {} + + FMT_CONSTEXPR void on_error() { + ErrorHandler::on_error("invalid type specifier"); + } +}; + +template +class float_type_checker : private ErrorHandler { + public: + FMT_CONSTEXPR explicit float_type_checker(ErrorHandler eh) + : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_general() {} + FMT_CONSTEXPR void on_exp() {} + FMT_CONSTEXPR void on_fixed() {} + FMT_CONSTEXPR void on_hex() {} + + FMT_CONSTEXPR void on_error() { + ErrorHandler::on_error("invalid type specifier"); + } +}; + +template +class char_specs_checker : public ErrorHandler { + private: + CharType type_; + + public: + FMT_CONSTEXPR char_specs_checker(CharType type, ErrorHandler eh) + : ErrorHandler(eh), type_(type) {} + + FMT_CONSTEXPR void on_int() { + handle_int_type_spec(type_, int_type_checker(*this)); + } + FMT_CONSTEXPR void on_char() {} +}; + +template +class cstring_type_checker : public ErrorHandler { + public: + FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) + : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_string() {} + FMT_CONSTEXPR void on_pointer() {} +}; + +template +void arg_map::init(const basic_format_args &args) { + if (map_) + return; + map_ = new entry[args.max_size()]; + bool use_values = args.type(max_packed_args - 1) == internal::none_type; + if (use_values) { + for (unsigned i = 0;/*nothing*/; ++i) { + internal::type arg_type = args.type(i); + switch (arg_type) { + case internal::none_type: + return; + case internal::name_arg_type: + push_back(args.values_[i]); + break; + default: + break; // Do nothing. + } + } + return; + } + for (unsigned i = 0; i != max_packed_args; ++i) { + if (args.type(i) == internal::name_arg_type) + push_back(args.args_[i].value_); + } + for (unsigned i = max_packed_args; ; ++i) { + switch (args.args_[i].type_) { + case internal::none_type: + return; + case internal::name_arg_type: + push_back(args.args_[i].value_); + break; + default: + break; // Do nothing. + } + } +} + +template +class arg_formatter_base { + public: + typedef typename Range::value_type char_type; + typedef decltype(internal::declval().begin()) iterator; + typedef basic_format_specs format_specs; + + private: + typedef basic_writer writer_type; + writer_type writer_; + format_specs &specs_; + + FMT_DISALLOW_COPY_AND_ASSIGN(arg_formatter_base); + + struct char_writer { + char_type value; + template + void operator()(It &&it) const { *it++ = value; } + }; + + void write_char(char_type value) { + writer_.write_padded(1, specs_, char_writer{value}); + } + + void write_pointer(const void *p) { + format_specs specs = specs_; + specs.flags_ = HASH_FLAG; + specs.type_ = 'x'; + writer_.write_int(reinterpret_cast(p), specs); + } + + protected: + writer_type &writer() { return writer_; } + format_specs &spec() { return specs_; } + iterator out() { return writer_.out(); } + + void write(bool value) { + writer_.write_str(string_view(value ? "true" : "false"), specs_); + } + + void write(const char_type *value) { + if (!value) + FMT_THROW(format_error("string pointer is null")); + auto length = std::char_traits::length(value); + writer_.write_str(basic_string_view(value, length), specs_); + } + + public: + arg_formatter_base(Range r, format_specs &s): writer_(r), specs_(s) {} + + iterator operator()(monostate) { + FMT_ASSERT(false, "invalid argument type"); + return out(); + } + + template + typename std::enable_if::value, iterator>::type + operator()(T value) { + writer_.write_int(value, specs_); + return out(); + } + + template + typename std::enable_if::value, iterator>::type + operator()(T value) { + writer_.write_double(value, specs_); + return out(); + } + + iterator operator()(bool value) { + if (specs_.type_) + return (*this)(value ? 1 : 0); + write(value); + return out(); + } + + struct char_spec_handler : internal::error_handler { + arg_formatter_base &formatter; + char_type value; + + char_spec_handler(arg_formatter_base& f, char_type val) + : formatter(f), value(val) {} + + void on_int() { formatter.writer_.write_int(value, formatter.specs_); } + void on_char() { formatter.write_char(value); } + }; + + iterator operator()(char_type value) { + internal::handle_char_specs(specs_, char_spec_handler(*this, value)); + return out(); + } + + struct cstring_spec_handler : internal::error_handler { + arg_formatter_base &formatter; + const char_type *value; + + cstring_spec_handler(arg_formatter_base &f, const char_type *val) + : formatter(f), value(val) {} + + void on_string() { formatter.write(value); } + void on_pointer() { formatter.write_pointer(value); } + }; + + iterator operator()(const char_type *value) { + internal::handle_cstring_type_spec( + specs_.type_, cstring_spec_handler(*this, value)); + return out(); + } + + iterator operator()(basic_string_view value) { + internal::check_string_type_spec(specs_.type_, internal::error_handler()); + writer_.write_str(value, specs_); + return out(); + } + + iterator operator()(const void *value) { + check_pointer_type_spec(specs_.type_, internal::error_handler()); + write_pointer(value); + return out(); + } +}; + +template +struct is_format_string: + std::integral_constant::value> {}; + +template +FMT_CONSTEXPR bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} + +// Parses the input as an unsigned integer. This function assumes that the +// first character is a digit and presence of a non-digit character at the end. +// it: an iterator pointing to the beginning of the input range. +template +FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) { + assert('0' <= *it && *it <= '9'); + unsigned value = 0; + // Convert to unsigned to prevent a warning. + unsigned max_int = (std::numeric_limits::max)(); + unsigned big = max_int / 10; + do { + // Check for overflow. + if (value > big) { + value = max_int + 1; + break; + } + value = value * 10 + (*it - '0'); + // Workaround for MSVC "setup_exception stack overflow" error: + auto next = it; + ++next; + it = next; + } while ('0' <= *it && *it <= '9'); + if (value > max_int) + eh.on_error("number is too big"); + return value; +} + +template +class custom_formatter: public function { + private: + Context &ctx_; + + public: + explicit custom_formatter(Context &ctx): ctx_(ctx) {} + + bool operator()(typename basic_format_arg::handle h) const { + h.format(ctx_); + return true; + } + + template + bool operator()(T) const { return false; } +}; + +template +struct is_integer { + enum { + value = std::is_integral::value && !std::is_same::value && + !std::is_same::value && !std::is_same::value + }; +}; + +template +class width_checker: public function { + public: + explicit FMT_CONSTEXPR width_checker(ErrorHandler &eh) : handler_(eh) {} + + template + FMT_CONSTEXPR + typename std::enable_if< + is_integer::value, unsigned long long>::type operator()(T value) { + if (is_negative(value)) + handler_.on_error("negative width"); + return value; + } + + template + FMT_CONSTEXPR typename std::enable_if< + !is_integer::value, unsigned long long>::type operator()(T) { + handler_.on_error("width is not integer"); + return 0; + } + + private: + ErrorHandler &handler_; +}; + +template +class precision_checker: public function { + public: + explicit FMT_CONSTEXPR precision_checker(ErrorHandler &eh) : handler_(eh) {} + + template + FMT_CONSTEXPR typename std::enable_if< + is_integer::value, unsigned long long>::type operator()(T value) { + if (is_negative(value)) + handler_.on_error("negative precision"); + return value; + } + + template + FMT_CONSTEXPR typename std::enable_if< + !is_integer::value, unsigned long long>::type operator()(T) { + handler_.on_error("precision is not integer"); + return 0; + } + + private: + ErrorHandler &handler_; +}; + +// A format specifier handler that sets fields in basic_format_specs. +template +class specs_setter { + public: + explicit FMT_CONSTEXPR specs_setter(basic_format_specs &specs): + specs_(specs) {} + + FMT_CONSTEXPR specs_setter(const specs_setter &other) : specs_(other.specs_) {} + + FMT_CONSTEXPR void on_align(alignment align) { specs_.align_ = align; } + FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill_ = fill; } + FMT_CONSTEXPR void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; } + FMT_CONSTEXPR void on_minus() { specs_.flags_ |= MINUS_FLAG; } + FMT_CONSTEXPR void on_space() { specs_.flags_ |= SIGN_FLAG; } + FMT_CONSTEXPR void on_hash() { specs_.flags_ |= HASH_FLAG; } + + FMT_CONSTEXPR void on_zero() { + specs_.align_ = ALIGN_NUMERIC; + specs_.fill_ = '0'; + } + + FMT_CONSTEXPR void on_width(unsigned width) { specs_.width_ = width; } + FMT_CONSTEXPR void on_precision(unsigned precision) { + specs_.precision_ = precision; + } + FMT_CONSTEXPR void end_precision() {} + + FMT_CONSTEXPR void on_type(Char type) { specs_.type_ = type; } + + protected: + basic_format_specs &specs_; +}; + +// A format specifier handler that checks if specifiers are consistent with the +// argument type. +template +class specs_checker : public Handler { + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type) + : Handler(handler), arg_type_(arg_type) {} + + FMT_CONSTEXPR specs_checker(const specs_checker &other) + : Handler(other), arg_type_(other.arg_type_) {} + + FMT_CONSTEXPR void on_align(alignment align) { + if (align == ALIGN_NUMERIC) + require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_plus() { + check_sign(); + Handler::on_plus(); + } + + FMT_CONSTEXPR void on_minus() { + check_sign(); + Handler::on_minus(); + } + + FMT_CONSTEXPR void on_space() { + check_sign(); + Handler::on_space(); + } + + FMT_CONSTEXPR void on_hash() { + require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_zero() { + require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { + if (is_integral(arg_type_) || arg_type_ == pointer_type) + this->on_error("precision not allowed for this argument type"); + } + + private: + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic(arg_type_)) + this->on_error("format specifier requires numeric argument"); + } + + FMT_CONSTEXPR void check_sign() { + require_numeric_argument(); + if (is_integral(arg_type_) && arg_type_ != int_type && + arg_type_ != long_long_type && arg_type_ != internal::char_type) { + this->on_error("format specifier requires signed argument"); + } + } + + internal::type arg_type_; +}; + +template