From 9eac9dd30a80c220aef40323039080c04e1aaad3 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sun, 15 Aug 2021 02:12:33 +0700
Subject: [PATCH 01/20] v6.14.2-dev

---
 src/version.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/version.h b/src/version.h
index 99836c0ad..e251dff68 100644
--- a/src/version.h
+++ b/src/version.h
@@ -28,7 +28,7 @@
 #define APP_ID        "xmrig"
 #define APP_NAME      "XMRig"
 #define APP_DESC      "XMRig miner"
-#define APP_VERSION   "6.14.1"
+#define APP_VERSION   "6.14.2-dev"
 #define APP_DOMAIN    "xmrig.com"
 #define APP_SITE      "www.xmrig.com"
 #define APP_COPYRIGHT "Copyright (C) 2016-2021 xmrig.com"
@@ -36,7 +36,7 @@
 
 #define APP_VER_MAJOR  6
 #define APP_VER_MINOR  14
-#define APP_VER_PATCH  1
+#define APP_VER_PATCH  2
 
 #ifdef _MSC_VER
 #   if (_MSC_VER >= 1920)

From d1033abbe54e9819b0815fde5e15026e51b2b2e5 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Tue, 17 Aug 2021 08:17:21 +0700
Subject: [PATCH 02/20] Update Coin, BlobReader and WalletAddress.

---
 src/base/crypto/Coin.cpp                    | 130 +++++++-------
 src/base/crypto/Coin.h                      |  55 +++---
 src/base/net/stratum/DaemonClient.cpp       |   6 +-
 src/base/tools/cryptonote/BlobReader.h      |  66 +++++---
 src/base/tools/cryptonote/BlockTemplate.cpp |  12 +-
 src/base/tools/cryptonote/BlockTemplate.h   |   8 +-
 src/base/tools/cryptonote/WalletAddress.cpp | 178 ++++++++++++++++----
 src/base/tools/cryptonote/WalletAddress.h   |  79 +++++++--
 8 files changed, 369 insertions(+), 165 deletions(-)

diff --git a/src/base/crypto/Coin.cpp b/src/base/crypto/Coin.cpp
index a70ca0751..2895636ea 100644
--- a/src/base/crypto/Coin.cpp
+++ b/src/base/crypto/Coin.cpp
@@ -1,13 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,9 +16,10 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/crypto/Coin.h"
 #include "3rdparty/rapidjson/document.h"
+#include "base/io/json/Json.h"
+#include "base/io/log/Log.h"
 
 
 #include <cstring>
@@ -39,74 +33,66 @@
 namespace xmrig {
 
 
-struct CoinName
+struct CoinInfo
 {
+    const Algorithm::Id algorithm;
+    const char *code;
     const char *name;
-    const Coin::Id id;
+    const uint64_t target;
+    const uint64_t units;
+    const char *tag;
 };
 
 
-static CoinName const coin_names[] = {
-    { "monero",     Coin::MONERO  },
-    { "xmr",        Coin::MONERO  },
-    { "arqma",      Coin::ARQMA   },
-    { "arq",        Coin::ARQMA   },
-    { "dero",       Coin::DERO    },
-    { "keva",       Coin::KEVA    },
-    { "ravencoin",  Coin::RAVEN   },
-    { "raven",      Coin::RAVEN   },
-    { "rvn",        Coin::RAVEN   },
-    { "conceal",    Coin::CONCEAL },
-    { "wownero",    Coin::WOWNERO }
+static const CoinInfo coinInfo[] = {
+    { Algorithm::INVALID,       nullptr,    nullptr,        0,      0,              nullptr },
+    { Algorithm::RX_0,          "XMR",      "Monero",       120,    1000000000000,  YELLOW_BG_BOLD( WHITE_BOLD_S " monero  ") },
+    { Algorithm::CN_R,          "SUMO",     "Sumokoin",     240,    1000000000,     BLUE_BG_BOLD(   WHITE_BOLD_S " sumo    ") },
+    { Algorithm::RX_ARQ,        "ARQ",      "ArQmA",        120,    1000000000,     BLUE_BG_BOLD(   WHITE_BOLD_S " arqma   ") },
+    { Algorithm::ASTROBWT_DERO, "DERO",     "DERO",         0,      0,              BLUE_BG_BOLD(   WHITE_BOLD_S " dero    ") },
+    { Algorithm::RX_KEVA,       "KVA",      "Kevacoin",     0,      0,              MAGENTA_BG_BOLD(WHITE_BOLD_S " keva    ") },
+    { Algorithm::KAWPOW_RVN,    "RVN",      "Ravencoin",    0,      0,              BLUE_BG_BOLD(   WHITE_BOLD_S " raven   ") },
+    { Algorithm::RX_WOW,        "WOW",      "Wownero",      300,    100000000000,   MAGENTA_BG_BOLD(WHITE_BOLD_S " wownero ") },
 };
 
 
+static_assert(Coin::MAX == sizeof(coinInfo) / sizeof(coinInfo[0]), "size mismatch");
+
+
+const char *Coin::kDisabled = "DISABLED_COIN";
+const char *Coin::kField    = "coin";
+const char *Coin::kUnknown  = "UNKNOWN_COIN";
+
+
 } /* namespace xmrig */
 
 
-
-xmrig::Algorithm::Id xmrig::Coin::algorithm(uint8_t blobVersion) const
+xmrig::Coin::Coin(const rapidjson::Value &value)
 {
-    switch (id()) {
-    case MONERO:
-        return (blobVersion >= 12) ? Algorithm::RX_0 : Algorithm::CN_R;
-
-    case ARQMA:
-        return (blobVersion >= 15) ? Algorithm::RX_ARQ : Algorithm::CN_PICO_0;
-
-    case DERO:
-        return (blobVersion >= 4) ? Algorithm::ASTROBWT_DERO : Algorithm::CN_0;
-
-    case KEVA:
-        return (blobVersion >= 11) ? Algorithm::RX_KEVA : Algorithm::CN_R;
-
-    case RAVEN:
-        return Algorithm::KAWPOW_RVN;
-
-    case CONCEAL:
-        return Algorithm::CN_CCX;
-
-    case WOWNERO:
-        return Algorithm::RX_WOW;
-
-    case INVALID:
-        break;
+    if (value.IsString()) {
+        m_id = parse(value.GetString());
+    }
+    else if (value.IsObject() && !value.ObjectEmpty()) {
+        m_id = parse(Json::getString(value, kField));
     }
-
-    return Algorithm::INVALID;
 }
 
 
+xmrig::Algorithm xmrig::Coin::algorithm(uint8_t) const
+{
+    return coinInfo[m_id].algorithm;
+}
+
+
+const char *xmrig::Coin::code() const
+{
+    return coinInfo[m_id].code;
+}
+
 
 const char *xmrig::Coin::name() const
 {
-    for (const auto &i : coin_names) {
-        if (i.id == m_id) {
-            return i.name;
-        }
-    }
-
-    return nullptr;
+    return coinInfo[m_id].name;
 }
 
 
@@ -114,7 +100,19 @@ rapidjson::Value xmrig::Coin::toJSON() const
 {
     using namespace rapidjson;
 
-    return isValid() ? Value(StringRef(name())) : Value(kNullType);
+    return isValid() ? Value(StringRef(code())) : Value(kNullType);
+}
+
+
+uint64_t xmrig::Coin::target(uint8_t) const
+{
+    return coinInfo[m_id].target;
+}
+
+
+uint64_t xmrig::Coin::units() const
+{
+    return coinInfo[m_id].units;
 }
 
 
@@ -124,11 +122,17 @@ xmrig::Coin::Id xmrig::Coin::parse(const char *name)
         return INVALID;
     }
 
-    for (const auto &i : coin_names) {
-        if (strcasecmp(name, i.name) == 0) {
-            return i.id;
+    for (uint32_t i = 1; i < MAX; ++i) {
+        if (strcasecmp(name, coinInfo[i].code) == 0 || strcasecmp(name, coinInfo[i].name) == 0) {
+            return static_cast<Id>(i);
         }
     }
 
     return INVALID;
 }
+
+
+const char *xmrig::Coin::tag(Id id)
+{
+    return coinInfo[id].tag;
+}
diff --git a/src/base/crypto/Coin.h b/src/base/crypto/Coin.h
index 985335cb4..ac661b7d1 100644
--- a/src/base/crypto/Coin.h
+++ b/src/base/crypto/Coin.h
@@ -1,13 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -37,38 +30,52 @@ namespace xmrig {
 class Coin
 {
 public:
-    enum Id : int {
-        INVALID = -1,
+    enum Id : uint32_t {
+        INVALID,
         MONERO,
+        SUMO,
         ARQMA,
         DERO,
         KEVA,
         RAVEN,
-        CONCEAL,
-        WOWNERO
+        WOWNERO,
+        MAX
     };
 
+    static const char *kDisabled;
+    static const char *kField;
+    static const char *kUnknown;
 
     Coin() = default;
-    inline Coin(const char *name) : m_id(parse(name)) {}
-    inline Coin(Id id) : m_id(id)                     {}
+    Coin(const rapidjson::Value &value);
+    inline Coin(const char *name) : m_id(parse(name))                           {}
+    inline Coin(Id id) : m_id(id)                                               {}
+    inline Coin(uint32_t id) : m_id(id < MAX ? static_cast<Id>(id) : INVALID)   {}
 
 
-    inline bool isEqual(const Coin &other) const        { return m_id == other.m_id; }
-    inline bool isValid() const                         { return m_id != INVALID; }
-    inline Id id() const                                { return m_id; }
+    inline bool isEqual(const Coin &other) const                                { return m_id == other.m_id; }
+    inline bool isValid() const                                                 { return m_id != INVALID; }
+    inline Id id() const                                                        { return m_id; }
+    inline const char *tag() const                                              { return tag(m_id); }
+    inline double decimal(uint64_t amount) const                                { return static_cast<double>(amount) / units(); }
 
-    Algorithm::Id algorithm(uint8_t blobVersion = 255) const;
+    Algorithm algorithm(uint8_t blobVersion = 255) const;
+    const char *code() const;
     const char *name() const;
     rapidjson::Value toJSON() const;
+    uint64_t target(uint8_t blobVersion = 255) const;
+    uint64_t units() const;
 
-    inline bool operator!=(Coin::Id id) const           { return m_id != id; }
-    inline bool operator!=(const Coin &other) const     { return !isEqual(other); }
-    inline bool operator==(Coin::Id id) const           { return m_id == id; }
-    inline bool operator==(const Coin &other) const     { return isEqual(other); }
-    inline operator Coin::Id() const                    { return m_id; }
+    inline bool operator!=(Id id) const                                         { return m_id != id; }
+    inline bool operator!=(const Coin &other) const                             { return !isEqual(other); }
+    inline bool operator<(Id id) const                                          { return m_id < id; }
+    inline bool operator<(const Coin &other) const                              { return m_id < other.m_id; }
+    inline bool operator==(Id id) const                                         { return m_id == id; }
+    inline bool operator==(const Coin &other) const                             { return isEqual(other); }
+    inline operator Id() const                                                  { return m_id; }
 
     static Id parse(const char *name);
+    static const char *tag(Id id);
 
 private:
     Id m_id = INVALID;
diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp
index 3a1a3abf1..c9af2801e 100644
--- a/src/base/net/stratum/DaemonClient.cpp
+++ b/src/base/net/stratum/DaemonClient.cpp
@@ -414,19 +414,19 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
         }
 
         WalletAddress user_address;
-        if (!user_address.Decode(m_pool.user())) {
+        if (!user_address.decode(m_pool.user())) {
             LOG_ERR("Invalid wallet address.");
             *code = 10;
             return false;
         }
 
-        if (memcmp(user_address.public_spend_key, public_spendkey, sizeof(public_spendkey)) != 0) {
+        if (memcmp(user_address.spendKey(), public_spendkey, sizeof(public_spendkey)) != 0) {
             LOG_ERR("Wallet address and spend key don't match.");
             *code = 11;
             return false;
         }
 
-        if (memcmp(user_address.public_view_key, public_viewkey, sizeof(public_viewkey)) != 0) {
+        if (memcmp(user_address.viewKey(), public_viewkey, sizeof(public_viewkey)) != 0) {
             LOG_ERR("Wallet address and view key don't match.");
             *code = 12;
             return false;
diff --git a/src/base/tools/cryptonote/BlobReader.h b/src/base/tools/cryptonote/BlobReader.h
index f10bcd88b..e8be0d65f 100644
--- a/src/base/tools/cryptonote/BlobReader.h
+++ b/src/base/tools/cryptonote/BlobReader.h
@@ -1,8 +1,8 @@
 /* XMRig
- * Copyright 2012-2013 The Cryptonote developers
- * Copyright 2014-2021 The Monero Project
- * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2012-2013 The Cryptonote developers
+ * Copyright (c) 2014-2021 The Monero Project
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,58 +23,71 @@
 
 
 #include <cstdint>
+#include <cstring>
 
 
 namespace xmrig {
 
 
-class CBlobReader
+class BlobReader
 {
 public:
-    inline CBlobReader(const void* data, size_t size)
-        : m_data(reinterpret_cast<const uint8_t*>(data))
-        , m_size(size)
-        , m_index(0)
+    inline BlobReader(const uint8_t *data, size_t size) :
+        m_size(size),
+        m_data(data)
     {}
 
-    inline bool operator()(uint8_t& data) { return getByte(data); }
-    inline bool operator()(uint64_t& data) { return getVarint(data); }
+    inline bool operator()(uint64_t &data)  { return getVarint(data); }
+    inline bool operator()(uint8_t &data)   { return getByte(data); }
+    inline size_t index() const             { return m_index; }
+    inline size_t remaining() const         { return m_size - m_index;  }
+
+    inline bool skip(size_t n)
+    {
+        if (m_index + n > m_size) {
+            return false;
+        }
+
+        m_index += n;
+
+        return true;
+    }
 
     template<size_t N>
     inline bool operator()(uint8_t(&data)[N])
     {
-        for (size_t i = 0; i < N; ++i) {
-            if (!getByte(data[i])) {
-                return false;
-            }
+        if (m_index + N > m_size) {
+            return false;
         }
+
+        memcpy(data, m_data + m_index, N);
+        m_index += N;
+
         return true;
     }
 
     template<typename T>
-    inline void readItems(T& data, size_t count)
+    inline void readItems(T &data, size_t count)
     {
         data.resize(count);
-        for (size_t i = 0; i < count; ++i)
+        for (size_t i = 0; i < count; ++i) {
             operator()(data[i]);
+        }
     }
 
-    inline size_t index() const { return m_index; }
-
-    inline void skip(size_t N) { m_index += N; }
-
 private:
-    inline bool getByte(uint8_t& data)
+    inline bool getByte(uint8_t &data)
     {
         if (m_index >= m_size) {
             return false;
         }
 
         data = m_data[m_index++];
+
         return true;
     }
 
-    inline bool getVarint(uint64_t& data)
+    inline bool getVarint(uint64_t &data)
     {
         uint64_t result = 0;
         uint8_t t;
@@ -89,12 +102,13 @@ private:
         } while (t & 0x80);
 
         data = result;
+
         return true;
     }
 
-    const uint8_t* m_data;
-    size_t m_size;
-    size_t m_index;
+    const size_t m_size;
+    const uint8_t *m_data;
+    size_t m_index  = 0;
 };
 
 
diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp
index 29653441d..ede0b19a8 100644
--- a/src/base/tools/cryptonote/BlockTemplate.cpp
+++ b/src/base/tools/cryptonote/BlockTemplate.cpp
@@ -1,8 +1,8 @@
 /* XMRig
- * Copyright 2012-2013 The Cryptonote developers
- * Copyright 2014-2021 The Monero Project
- * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2012-2013 The Cryptonote developers
+ * Copyright (c) 2014-2021 The Monero Project
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@ bool BlockTemplate::Init(const String& blockTemplate, Coin coin)
 {
     raw_blob = Cvt::fromHex(blockTemplate);
 
-    CBlobReader ar(raw_blob.data(), raw_blob.size());
+    BlobReader ar(raw_blob.data(), raw_blob.size());
 
     // Block header
     ar(major_version);
@@ -90,7 +90,7 @@ bool BlockTemplate::Init(const String& blockTemplate, Coin coin)
 
     ar.readItems(extra, extra_size);
 
-    CBlobReader ar_extra(extra.data(), extra_size);
+    BlobReader ar_extra(extra.data(), extra_size);
 
     tx_extra_nonce_size = 0;
     tx_extra_nonce_index = 0;
diff --git a/src/base/tools/cryptonote/BlockTemplate.h b/src/base/tools/cryptonote/BlockTemplate.h
index 4a6ef8051..6effd3feb 100644
--- a/src/base/tools/cryptonote/BlockTemplate.h
+++ b/src/base/tools/cryptonote/BlockTemplate.h
@@ -1,8 +1,8 @@
 /* XMRig
- * Copyright 2012-2013 The Cryptonote developers
- * Copyright 2014-2021 The Monero Project
- * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2012-2013 The Cryptonote developers
+ * Copyright (c) 2014-2021 The Monero Project
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/tools/cryptonote/WalletAddress.cpp b/src/base/tools/cryptonote/WalletAddress.cpp
index 7be1ef9d6..6b7f5343d 100644
--- a/src/base/tools/cryptonote/WalletAddress.cpp
+++ b/src/base/tools/cryptonote/WalletAddress.cpp
@@ -1,8 +1,8 @@
 /* XMRig
- * Copyright 2012-2013 The Cryptonote developers
- * Copyright 2014-2021 The Monero Project
- * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2012-2013 The Cryptonote developers
+ * Copyright (c) 2014-2021 The Monero Project
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -18,24 +18,29 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-#include "base/crypto/keccak.h"
-#include "base/tools/cryptonote/BlobReader.h"
 #include "base/tools/cryptonote/WalletAddress.h"
-#include "base/tools/cryptonote/umul128.h"
+#include "3rdparty/rapidjson/document.h"
+#include "base/crypto/keccak.h"
 #include "base/tools/Buffer.h"
+#include "base/tools/cryptonote/BlobReader.h"
+#include "base/tools/cryptonote/umul128.h"
+#include "base/tools/Cvt.h"
+
+
 #include <array>
+#include <map>
 
 
-namespace xmrig {
-
-
-bool WalletAddress::Decode(const String& address)
+bool xmrig::WalletAddress::decode(const char *address, size_t size)
 {
     static constexpr std::array<int, 9> block_sizes{ 0, 2, 3, 5, 6, 7, 9, 10, 11 };
     static constexpr char alphabet[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
     constexpr size_t alphabet_size = sizeof(alphabet) - 1;
 
+    if (size < kMinSize || size > kMaxSize) {
+        return false;
+    }
+
     int8_t reverse_alphabet[256];
     memset(reverse_alphabet, -1, sizeof(reverse_alphabet));
 
@@ -43,7 +48,7 @@ bool WalletAddress::Decode(const String& address)
         reverse_alphabet[static_cast<int>(alphabet[i])] = i;
     }
 
-    const int len = static_cast<int>(address.size());
+    const int len = static_cast<int>(size);
     const int num_full_blocks = len / block_sizes.back();
     const int last_block_size = len % block_sizes.back();
 
@@ -60,10 +65,15 @@ bool WalletAddress::Decode(const String& address)
         return false;
     }
 
-    Buffer data;
-    data.reserve(static_cast<size_t>(num_full_blocks) * sizeof(uint64_t) + last_block_size_index);
+    const size_t data_size = static_cast<size_t>(num_full_blocks) * sizeof(uint64_t) + last_block_size_index;
+    if (data_size < kMinDataSize) {
+        return false;
+    }
 
-    const char* address_data = address.data();
+    Buffer data;
+    data.reserve(data_size);
+
+    const char *address_data = address;
 
     for (int i = 0; i <= num_full_blocks; ++i) {
         uint64_t num = 0;
@@ -87,28 +97,138 @@ bool WalletAddress::Decode(const String& address)
 
         address_data += block_sizes.back();
 
-        uint8_t* p = reinterpret_cast<uint8_t*>(&num);
-        for (int j = ((i < num_full_blocks) ? sizeof(num) : last_block_size_index) - 1; j >= 0; --j) {
+        auto p = reinterpret_cast<const uint8_t*>(&num);
+        for (int j = ((i < num_full_blocks) ? static_cast<int>(sizeof(num)) : last_block_size_index) - 1; j >= 0; --j) {
             data.emplace_back(p[j]);
         }
     }
 
-    CBlobReader ar(data.data(), data.size());
+    assert(data.size() == data_size);
 
-    ar(tag);
-    ar(public_spend_key);
-    ar(public_view_key);
-    ar(checksum);
+    BlobReader ar(data.data(), data_size);
 
-    uint8_t md[200];
-    keccak(data.data(), data.size() - sizeof(checksum), md);
+    if (ar(m_tag) && ar(m_publicSpendKey) && ar(m_publicViewKey) && ar.skip(ar.remaining() - sizeof(m_checksum)) && ar(m_checksum)) {
+        uint8_t md[200];
+        keccak(data.data(), data_size - sizeof(m_checksum), md);
 
-    if (memcmp(checksum, md, sizeof(checksum)) != 0) {
-        return false;
+        if (memcmp(m_checksum, md, sizeof(m_checksum)) == 0) {
+            m_data = { address, size };
+
+            return true;
+        }
     }
 
-    return true;
+    m_tag = 0;
+
+    return false;
 }
 
 
-} /* namespace xmrig */
+bool xmrig::WalletAddress::decode(const rapidjson::Value &address)
+{
+    return address.IsString() && decode(address.GetString(), address.GetStringLength());
+}
+
+
+const char *xmrig::WalletAddress::netName() const
+{
+    static const std::array<const char *, 3> names = { "mainnet", "testnet", "stagenet" };
+
+    return names[net()];
+}
+
+
+const char *xmrig::WalletAddress::typeName() const
+{
+    static const std::array<const char *, 3> names = { "public", "integrated", "subaddress" };
+
+    return names[type()];
+}
+
+
+rapidjson::Value xmrig::WalletAddress::toJSON(rapidjson::Document &doc) const
+{
+    using namespace rapidjson;
+
+    return isValid() ? m_data.toJSON(doc) : Value(kNullType);
+}
+
+
+#ifdef XMRIG_FEATURE_API
+rapidjson::Value xmrig::WalletAddress::toAPI(rapidjson::Document &doc) const
+{
+    using namespace rapidjson;
+
+    if (!isValid()) {
+        return Value(kNullType);
+    }
+
+    auto &allocator = doc.GetAllocator();
+    Value out(kObjectType);
+    out.AddMember(StringRef(Coin::kField),  coin().toJSON(), allocator);
+    out.AddMember("address",                m_data.toJSON(doc), allocator);
+    out.AddMember("type",                   StringRef(typeName()), allocator);
+    out.AddMember("net",                    StringRef(netName()), allocator);
+    out.AddMember("rpc_port",               rpcPort(), allocator);
+    out.AddMember("zmq_port",               zmqPort(), allocator);
+    out.AddMember("tag",                    m_tag, allocator);
+    out.AddMember("view_key",               Cvt::toHex(m_publicViewKey, kKeySize, doc), allocator);
+    out.AddMember("spend_key",              Cvt::toHex(m_publicSpendKey, kKeySize, doc), allocator);
+    out.AddMember("checksum",               Cvt::toHex(m_checksum, sizeof(m_checksum), doc), allocator);
+
+    return out;
+}
+#endif
+
+
+const xmrig::WalletAddress::TagInfo &xmrig::WalletAddress::tagInfo(uint64_t tag)
+{
+    static TagInfo dummy = { Coin::INVALID, MAINNET, PUBLIC, 0 };
+    static const std::map<uint64_t, TagInfo> tags = {
+        { 18,       { Coin::MONERO,     MAINNET,    PUBLIC,         18081,  18082 } },
+        { 19,       { Coin::MONERO,     MAINNET,    INTEGRATED,     18081,  18082 } },
+        { 42,       { Coin::MONERO,     MAINNET,    SUBADDRESS,     18081,  18082 } },
+
+        { 53,       { Coin::MONERO,     TESTNET,    PUBLIC,         28081,  28082 } },
+        { 54,       { Coin::MONERO,     TESTNET,    INTEGRATED,     28081,  28082 } },
+        { 63,       { Coin::MONERO,     TESTNET,    SUBADDRESS,     28081,  28082 } },
+
+        { 24,       { Coin::MONERO,     STAGENET,   PUBLIC,         38081,  38082 } },
+        { 25,       { Coin::MONERO,     STAGENET,   INTEGRATED,     38081,  38082 } },
+        { 36,       { Coin::MONERO,     STAGENET,   SUBADDRESS,     38081,  38082 } },
+
+        { 0x2bb39a, { Coin::SUMO,       MAINNET,    PUBLIC,         19734,  19735 } },
+        { 0x29339a, { Coin::SUMO,       MAINNET,    INTEGRATED,     19734,  19735 } },
+        { 0x8319a,  { Coin::SUMO,       MAINNET,    SUBADDRESS,     19734,  19735 } },
+
+        { 0x37751a, { Coin::SUMO,       TESTNET,    PUBLIC,         29734,  29735 } },
+        { 0x34f51a, { Coin::SUMO,       TESTNET,    INTEGRATED,     29734,  29735 } },
+        { 0x1d351a, { Coin::SUMO,       TESTNET,    SUBADDRESS,     29734,  29735 } },
+
+        { 0x2cca,   { Coin::ARQMA,      MAINNET,    PUBLIC,         19994,  19995 } },
+        { 0x116bc7, { Coin::ARQMA,      MAINNET,    INTEGRATED,     19994,  19995 } },
+        { 0x6847,   { Coin::ARQMA,      MAINNET,    SUBADDRESS,     19994,  19995 } },
+
+        { 0x53ca,   { Coin::ARQMA,      TESTNET,    PUBLIC,         29994,  29995 } },
+        { 0x504a,   { Coin::ARQMA,      TESTNET,    INTEGRATED,     29994,  29995 } },
+        { 0x524a,   { Coin::ARQMA,      TESTNET,    SUBADDRESS,     29994,  29995 } },
+
+        { 0x39ca,   { Coin::ARQMA,      STAGENET,   PUBLIC,         39994,  39995 } },
+        { 0x1742ca, { Coin::ARQMA,      STAGENET,   INTEGRATED,     39994,  39995 } },
+        { 0x1d84ca, { Coin::ARQMA,      STAGENET,   SUBADDRESS,     39994,  39995 } },
+
+        { 0xc8ed8,  { Coin::DERO,       MAINNET,    PUBLIC,         20206,  0 } },
+        { 0xa0ed8,  { Coin::DERO,       MAINNET,    INTEGRATED,     20206,  0 } },
+
+        { 0x6cf58,  { Coin::DERO,       TESTNET,    PUBLIC,         30306,  0 } },
+        { 0x44f58,  { Coin::DERO,       TESTNET,    INTEGRATED,     30306,  0 } },
+
+        { 4146,     { Coin::WOWNERO,    MAINNET,    PUBLIC,         34568,  34569 } },
+        { 6810,     { Coin::WOWNERO,    MAINNET,    INTEGRATED,     34568,  34569 } },
+        { 12208,    { Coin::WOWNERO,    MAINNET,    SUBADDRESS,     34568,  34569 } },
+    };
+
+    const auto it = tags.find(tag);
+
+    return it == tags.end() ? dummy : it->second;
+}
diff --git a/src/base/tools/cryptonote/WalletAddress.h b/src/base/tools/cryptonote/WalletAddress.h
index cc3c2ec42..1c82c92bc 100644
--- a/src/base/tools/cryptonote/WalletAddress.h
+++ b/src/base/tools/cryptonote/WalletAddress.h
@@ -1,8 +1,8 @@
 /* XMRig
- * Copyright 2012-2013 The Cryptonote developers
- * Copyright 2014-2021 The Monero Project
- * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2012-2013 The Cryptonote developers
+ * Copyright (c) 2014-2021 The Monero Project
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,19 +23,78 @@
 
 
 #include "base/tools/String.h"
+#include "base/crypto/Coin.h"
 
 
 namespace xmrig {
 
 
-struct WalletAddress
+class WalletAddress
 {
-    uint64_t tag;
-    uint8_t public_spend_key[32];
-    uint8_t public_view_key[32];
-    uint8_t checksum[4];
+public:
+    enum Net : uint32_t {
+        MAINNET,
+        TESTNET,
+        STAGENET
+    };
 
-    bool Decode(const String& address);
+    enum Type : uint32_t {
+        PUBLIC,
+        INTEGRATED,
+        SUBADDRESS
+    };
+
+    constexpr static size_t kKeySize        = 32;
+    constexpr static size_t kMaxSize        = 256;
+    constexpr static size_t kMinDataSize    = 69;
+    constexpr static size_t kMinSize        = 95;
+
+    WalletAddress() = default;
+    inline WalletAddress(const char *address, size_t size)  { decode(address, size); }
+    inline WalletAddress(const char *address)               { decode(address); }
+    inline WalletAddress(const rapidjson::Value &address)   { decode(address); }
+    inline WalletAddress(const String &address)             { decode(address); }
+
+    inline bool decode(const char *address)                 { return decode(address, strlen(address)); }
+    inline bool decode(const String &address)               { return decode(address, address.size()); }
+    inline bool isValid() const                             { return m_tag > 0 && m_data.size() >= kMinSize; }
+    inline const char *data() const                         { return m_data; }
+    inline const Coin &coin() const                         { return tagInfo(m_tag).coin; }
+    inline const uint8_t *spendKey() const                  { return m_publicSpendKey; }
+    inline const uint8_t *viewKey() const                   { return m_publicViewKey; }
+    inline Net net() const                                  { return tagInfo(m_tag).net; }
+    inline Type type() const                                { return tagInfo(m_tag).type; }
+    inline uint16_t rpcPort() const                         { return tagInfo(m_tag).rpcPort; }
+    inline uint16_t zmqPort() const                         { return tagInfo(m_tag).zmqPort; }
+    inline uint64_t tag() const                             { return m_tag; }
+
+    bool decode(const char *address, size_t size);
+    bool decode(const rapidjson::Value &address);
+    const char *netName() const;
+    const char *typeName() const;
+    rapidjson::Value toJSON(rapidjson::Document &doc) const;
+
+#   ifdef XMRIG_FEATURE_API
+    rapidjson::Value toAPI(rapidjson::Document &doc) const;
+#   endif
+
+private:
+    struct TagInfo
+    {
+        const Coin coin;
+        const Net net;
+        const Type type;
+        const uint16_t rpcPort;
+        const uint16_t zmqPort;
+    };
+
+    static const TagInfo &tagInfo(uint64_t tag);
+
+    String m_data;
+    uint64_t m_tag  = 0;
+    uint8_t m_checksum[4]{};
+    uint8_t m_publicSpendKey[kKeySize]{};
+    uint8_t m_publicViewKey[kKeySize]{};
 };
 
 

From 460d9c75c5ecfb0c9db37285d3bebea1aca33168 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Wed, 18 Aug 2021 13:36:50 +0700
Subject: [PATCH 03/20] Add global wallet address parser for DaemonClient.

---
 src/base/kernel/interfaces/IClient.h  |  16 ++-
 src/base/net/stratum/BaseClient.cpp   |  14 +--
 src/base/net/stratum/BaseClient.h     |  10 +-
 src/base/net/stratum/DaemonClient.cpp | 143 ++++++++++++++------------
 src/base/net/stratum/DaemonClient.h   |  40 +++----
 src/base/net/stratum/Pool.cpp         |   4 -
 6 files changed, 110 insertions(+), 117 deletions(-)

diff --git a/src/base/kernel/interfaces/IClient.h b/src/base/kernel/interfaces/IClient.h
index 456d603d2..6cf4ff03a 100644
--- a/src/base/kernel/interfaces/IClient.h
+++ b/src/base/kernel/interfaces/IClient.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -27,6 +21,7 @@
 
 
 #include "3rdparty/rapidjson/fwd.h"
+#include "base/tools/Object.h"
 
 
 #include <functional>
@@ -46,6 +41,8 @@ class String;
 class IClient
 {
 public:
+    XMRIG_DISABLE_COPY_MOVE(IClient)
+
     enum Extension {
         EXT_ALGO,
         EXT_NICEHASH,
@@ -57,7 +54,8 @@ public:
 
     using Callback = std::function<void(const rapidjson::Value &result, bool success, uint64_t elapsed)>;
 
-    virtual ~IClient() = default;
+    IClient()           = default;
+    virtual ~IClient()  = default;
 
     virtual bool disconnect()                                               = 0;
     virtual bool hasExtension(Extension extension) const noexcept           = 0;
diff --git a/src/base/net/stratum/BaseClient.cpp b/src/base/net/stratum/BaseClient.cpp
index 12b7ca76e..7c9a728d1 100644
--- a/src/base/net/stratum/BaseClient.cpp
+++ b/src/base/net/stratum/BaseClient.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,8 +16,8 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/stratum/BaseClient.h"
+#include "3rdparty/fmt/core.h"
 #include "3rdparty/rapidjson/document.h"
 #include "base/io/Env.h"
 #include "base/io/log/Log.h"
@@ -58,7 +52,7 @@ void xmrig::BaseClient::setPool(const Pool &pool)
     m_user      = Env::expand(pool.user());
     m_password  = Env::expand(pool.password());
     m_rigId     = Env::expand(pool.rigId());
-    m_tag       = std::string(Tags::network()) + " " CYAN_BOLD_S + m_pool.url().data() + CLEAR;
+    m_tag       = fmt::format("{} " CYAN_BOLD("{}"), Tags::network(), m_pool.url().data());
 }
 
 
diff --git a/src/base/net/stratum/BaseClient.h b/src/base/net/stratum/BaseClient.h
index 0a87fedb0..53fa59f72 100644
--- a/src/base/net/stratum/BaseClient.h
+++ b/src/base/net/stratum/BaseClient.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp
index c9af2801e..5cea361d8 100644
--- a/src/base/net/stratum/DaemonClient.cpp
+++ b/src/base/net/stratum/DaemonClient.cpp
@@ -24,6 +24,9 @@
  */
 
 
+#include <uv.h>
+
+
 #include "base/net/stratum/DaemonClient.h"
 #include "3rdparty/rapidjson/document.h"
 #include "3rdparty/rapidjson/error/en.h"
@@ -42,7 +45,6 @@
 #include "base/tools/Cvt.h"
 #include "base/tools/Timer.h"
 #include "base/tools/cryptonote/Signatures.h"
-#include "base/tools/cryptonote/WalletAddress.h"
 #include "net/JobResult.h"
 
 
@@ -181,12 +183,28 @@ int64_t xmrig::DaemonClient::submit(const JobResult &result)
 
 void xmrig::DaemonClient::connect()
 {
-    if ((m_pool.algorithm() == Algorithm::ASTROBWT_DERO) || (m_pool.coin() == Coin::DERO)) {
-        m_apiVersion = API_DERO;
-    }
+    auto connectError = [this](const char *message) {
+        if (!isQuiet()) {
+            LOG_ERR("%s " RED("connect error: ") RED_BOLD("\"%s\""), tag(), message);
+        }
+
+        retry();
+    };
 
     setState(ConnectingState);
 
+    if (!m_walletAddress.isValid()) {
+        return connectError("Invalid wallet address.");
+    }
+
+    if (!m_coin.isValid() && !m_pool.algorithm().isValid()) {
+        return connectError("Invalid algorithm.");
+    }
+
+    if ((m_pool.algorithm() == Algorithm::ASTROBWT_DERO) || (m_coin == Coin::DERO)) {
+        m_apiVersion = API_DERO;
+    }
+
     if (m_pool.zmq_port() >= 0) {
         m_dns = Dns::resolve(m_pool.host(), this);
     }
@@ -203,6 +221,20 @@ void xmrig::DaemonClient::connect(const Pool &pool)
 }
 
 
+void xmrig::DaemonClient::setPool(const Pool &pool)
+{
+    BaseClient::setPool(pool);
+
+    m_walletAddress.decode(m_user);
+
+    m_coin = pool.coin().isValid() ?  pool.coin() : m_walletAddress.coin();
+
+    if (!m_coin.isValid() && pool.algorithm() == Algorithm::RX_WOW) {
+        m_coin = Coin::WOWNERO;
+    }
+}
+
+
 void xmrig::DaemonClient::onHttpData(const HttpData &data)
 {
     if (data.status != 200) {
@@ -219,7 +251,7 @@ void xmrig::DaemonClient::onHttpData(const HttpData &data)
     rapidjson::Document doc;
     if (doc.Parse(data.body.c_str()).HasParseError()) {
         if (!isQuiet()) {
-            LOG_ERR("[%s:%d] JSON decode failed: \"%s\"", m_pool.host().data(), m_pool.port(), rapidjson::GetParseError_En(doc.GetParseError()));
+            LOG_ERR("%s " RED("JSON decode failed: ") RED_BOLD("\"%s\""), tag(), rapidjson::GetParseError_En(doc.GetParseError()));
         }
 
         return retry();
@@ -284,7 +316,7 @@ void xmrig::DaemonClient::onTimer(const Timer *)
 }
 
 
-void xmrig::DaemonClient::onResolved(const DnsRecords& records, int status, const char* error)
+void xmrig::DaemonClient::onResolved(const DnsRecords &records, int status, const char* error)
 {
     m_dns.reset();
 
@@ -297,14 +329,14 @@ void xmrig::DaemonClient::onResolved(const DnsRecords& records, int status, cons
         return;
     }
 
-    if (m_ZMQSocket) {
-        delete m_ZMQSocket;
-    }
 
-    const auto& record = records.get();
+    delete m_ZMQSocket;
+
+
+    const auto &record = records.get();
     m_ip = record.ip();
 
-    uv_connect_t* req = new uv_connect_t;
+    auto req = new uv_connect_t;
     req->data = m_storage.ptr(m_key);
 
     m_ZMQSocket = new uv_tcp_t;
@@ -329,26 +361,26 @@ bool xmrig::DaemonClient::isOutdated(uint64_t height, const char *hash) const
 
 bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
 {
+    auto jobError = [this, code](const char *message) {
+        if (!isQuiet()) {
+            LOG_ERR("%s " RED("job error: ") RED_BOLD("\"%s\""), tag(), message);
+        }
+
+        *code = 1;
+
+        return false;
+    };
+
     Job job(false, m_pool.algorithm(), String());
 
     String blocktemplate = Json::getString(params, kBlocktemplateBlob);
 
     if (blocktemplate.isNull()) {
-        LOG_ERR("Empty block template received from daemon");
-        *code = 1;
-        return false;
+        return jobError("Empty block template received from daemon.");
     }
 
-    Coin pool_coin = m_pool.coin();
-
-    if (!pool_coin.isValid() && (m_pool.algorithm() == Algorithm::RX_WOW)) {
-        pool_coin = Coin::WOWNERO;
-    }
-
-    if (!m_blocktemplate.Init(blocktemplate, pool_coin)) {
-        LOG_ERR("Invalid block template received from daemon");
-        *code = 2;
-        return false;
+    if (!m_blocktemplate.Init(blocktemplate, m_coin)) {
+        return jobError("Invalid block template received from daemon.");
     }
 
 #   ifdef XMRIG_PROXY_PROJECT
@@ -368,29 +400,21 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
 
     if (m_blocktemplate.has_miner_signature) {
         if (m_pool.spendSecretKey().isEmpty()) {
-            LOG_ERR("Secret spend key is not set");
-            *code = 4;
-            return false;
+            return jobError("Secret spend key is not set.");
         }
 
         if (m_pool.spendSecretKey().size() != 64) {
-            LOG_ERR("Secret spend key has invalid length. It must be 64 hex characters.");
-            *code = 5;
-            return false;
+            return jobError("Secret spend key has invalid length. It must be 64 hex characters.");
         }
 
         uint8_t secret_spendkey[32];
         if (!Cvt::fromHex(secret_spendkey, 32, m_pool.spendSecretKey(), 64)) {
-            LOG_ERR("Secret spend key is not a valid hex data.");
-            *code = 6;
-            return false;
+            return jobError("Secret spend key is not a valid hex data.");
         }
 
         uint8_t public_spendkey[32];
         if (!secret_key_to_public_key(secret_spendkey, public_spendkey)) {
-            LOG_ERR("Secret spend key is invalid.");
-            *code = 7;
-            return false;
+            return jobError("Secret spend key is invalid.");
         }
 
 #       ifdef XMRIG_PROXY_PROJECT
@@ -401,35 +425,24 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
 
         uint8_t public_viewkey[32];
         if (!secret_key_to_public_key(secret_viewkey, public_viewkey)) {
-            LOG_ERR("Secret view key is invalid.");
-            *code = 8;
-            return false;
+            return jobError("Secret view key is invalid.");
         }
 
         uint8_t derivation[32];
         if (!generate_key_derivation(m_blocktemplate.raw_blob.data() + m_blocktemplate.tx_pubkey_index, secret_viewkey, derivation)) {
-            LOG_ERR("Failed to generate key derivation for miner signature.");
-            *code = 9;
-            return false;
+            return jobError("Failed to generate key derivation for miner signature.");
         }
 
-        WalletAddress user_address;
-        if (!user_address.decode(m_pool.user())) {
-            LOG_ERR("Invalid wallet address.");
-            *code = 10;
-            return false;
+        if (!m_walletAddress.decode(m_pool.user())) {
+            return jobError("Invalid wallet address.");
         }
 
-        if (memcmp(user_address.spendKey(), public_spendkey, sizeof(public_spendkey)) != 0) {
-            LOG_ERR("Wallet address and spend key don't match.");
-            *code = 11;
-            return false;
+        if (memcmp(m_walletAddress.spendKey(), public_spendkey, sizeof(public_spendkey)) != 0) {
+            return jobError("Wallet address and spend key don't match.");
         }
 
-        if (memcmp(user_address.viewKey(), public_viewkey, sizeof(public_viewkey)) != 0) {
-            LOG_ERR("Wallet address and view key don't match.");
-            *code = 12;
-            return false;
+        if (memcmp(m_walletAddress.viewKey(), public_viewkey, sizeof(public_viewkey)) != 0) {
+            return jobError("Wallet address and view key don't match.");
         }
 
         uint8_t eph_secret_key[32];
@@ -444,8 +457,8 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
         Cvt::toHex(m_blockhashingblob.data() + offset * 2, kBlobReserveSize * 2, Cvt::randomBytes(kBlobReserveSize).data(), kBlobReserveSize);
     }
 
-    if (pool_coin.isValid()) {
-        job.setAlgorithm(pool_coin.algorithm(m_blocktemplate.major_version));
+    if (m_coin.isValid()) {
+        job.setAlgorithm(m_coin.algorithm(m_blocktemplate.major_version));
     }
 
     if (!job.setBlob(m_blockhashingblob)) {
@@ -594,7 +607,6 @@ void xmrig::DaemonClient::send(const char *path)
 
 void xmrig::DaemonClient::setState(SocketState state)
 {
-    assert(m_state != state);
     if (m_state == state) {
         return;
     }
@@ -735,10 +747,9 @@ void xmrig::DaemonClient::ZMQRead(ssize_t nread, const uv_buf_t* buf)
                     m_ZMQConnectionState = ZMQ_GREETING_2;
                     break;
                 }
-                else {
-                    LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format"), tag());
-                    ZMQClose();
-                }
+
+                LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format"), tag());
+                ZMQClose();
             }
             return;
 
@@ -751,10 +762,10 @@ void xmrig::DaemonClient::ZMQRead(ssize_t nread, const uv_buf_t* buf)
                     ZMQWrite(kZMQHandshake, sizeof(kZMQHandshake) - 1);
                     break;
                 }
-                else {
-                    LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format 2"), tag());
-                    ZMQClose();
-                }
+
+                LOG_ERR("%s " RED("ZMQ handshake failed: invalid greeting format 2"), tag());
+                ZMQClose();
+
             }
             return;
 
diff --git a/src/base/net/stratum/DaemonClient.h b/src/base/net/stratum/DaemonClient.h
index 2844d15fe..5a6ca8ca8 100644
--- a/src/base/net/stratum/DaemonClient.h
+++ b/src/base/net/stratum/DaemonClient.h
@@ -1,13 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2019      Howard Chu  <https://github.com/hyc>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2019      Howard Chu  <https://github.com/hyc>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -27,21 +21,25 @@
 #define XMRIG_DAEMONCLIENT_H
 
 
-#include <uv.h>
-
-
 #include "base/kernel/interfaces/IDnsListener.h"
 #include "base/kernel/interfaces/IHttpListener.h"
 #include "base/kernel/interfaces/ITimerListener.h"
 #include "base/net/stratum/BaseClient.h"
-#include "base/tools/Object.h"
-#include "base/tools/cryptonote/BlockTemplate.h"
 #include "base/net/tools/Storage.h"
+#include "base/tools/cryptonote/BlockTemplate.h"
+#include "base/tools/cryptonote/WalletAddress.h"
 
 
 #include <memory>
 
 
+using uv_buf_t      = struct uv_buf_t;
+using uv_connect_t  = struct uv_connect_s;
+using uv_handle_t   = struct uv_handle_s;
+using uv_stream_t   = struct uv_stream_s;
+using uv_tcp_t      = struct uv_tcp_s;
+
+
 namespace xmrig {
 
 
@@ -62,10 +60,11 @@ protected:
     int64_t submit(const JobResult &result) override;
     void connect() override;
     void connect(const Pool &pool) override;
+    void setPool(const Pool &pool) override;
 
     void onHttpData(const HttpData &data) override;
     void onTimer(const Timer *timer) override;
-    void onResolved(const DnsRecords& records, int status, const char* error) override;
+    void onResolved(const DnsRecords &records, int status, const char* error) override;
 
     inline bool hasExtension(Extension) const noexcept override         { return false; }
     inline const char *mode() const override                            { return "daemon"; }
@@ -92,18 +91,19 @@ private:
         API_DERO,
     } m_apiVersion = API_MONERO;
 
+    BlockTemplate m_blocktemplate;
+    Coin m_coin;
     std::shared_ptr<IHttpListener> m_httpListener;
-    String m_currentJobId;
-    String m_blocktemplateStr;
     String m_blockhashingblob;
+    String m_blocktemplateRequestHash;
+    String m_blocktemplateStr;
+    String m_currentJobId;
     String m_prevHash;
     String m_tlsFingerprint;
     String m_tlsVersion;
     Timer *m_timer;
     uint64_t m_blocktemplateRequestHeight = 0;
-    String m_blocktemplateRequestHash;
-
-    BlockTemplate m_blocktemplate;
+    WalletAddress m_walletAddress;
 
 private:
     static inline DaemonClient* getClient(void* data) { return m_storage.get(data); }
diff --git a/src/base/net/stratum/Pool.cpp b/src/base/net/stratum/Pool.cpp
index f11c5e2d3..367e66677 100644
--- a/src/base/net/stratum/Pool.cpp
+++ b/src/base/net/stratum/Pool.cpp
@@ -190,10 +190,6 @@ bool xmrig::Pool::isEnabled() const
     }
 #   endif
 
-    if (m_mode == MODE_DAEMON && (!algorithm().isValid() && !coin().isValid())) {
-        return false;
-    }
-
     return m_flags.test(FLAG_ENABLED) && isValid();
 }
 

From bea2a6cf5b41a456d098188f42f1323767e7f34a Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Mon, 23 Aug 2021 18:32:58 +0700
Subject: [PATCH 04/20] Update BlockTemplate class.

---
 src/3rdparty/epee/span.h                    | 176 +++++++++
 src/base/base.cmake                         |  14 +-
 src/base/net/stratum/DaemonClient.cpp       |  38 +-
 src/base/net/stratum/Job.cpp                |   8 +-
 src/base/tools/Cvt.cpp                      |   7 +-
 src/base/tools/Cvt.h                        |   6 +-
 src/base/tools/Span.h                       |  35 ++
 src/base/tools/cryptonote/BlobReader.h      |  32 +-
 src/base/tools/cryptonote/BlockTemplate.cpp | 393 +++++++++++---------
 src/base/tools/cryptonote/BlockTemplate.h   | 144 ++++---
 src/base/tools/cryptonote/WalletAddress.cpp |   2 +-
 11 files changed, 598 insertions(+), 257 deletions(-)
 create mode 100644 src/3rdparty/epee/span.h
 create mode 100644 src/base/tools/Span.h

diff --git a/src/3rdparty/epee/span.h b/src/3rdparty/epee/span.h
new file mode 100644
index 000000000..b355c960a
--- /dev/null
+++ b/src/3rdparty/epee/span.h
@@ -0,0 +1,176 @@
+// Copyright (c) 2017-2020, The Monero Project
+//
+// 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.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+//    used to endorse or promote products derived from this software without specific
+//    prior written permission.
+//
+// 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 HOLDER 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.
+
+#pragma once
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+namespace epee
+{
+  /*!
+    \brief Non-owning sequence of data. Does not deep copy
+
+    Inspired by `gsl::span` and/or `boost::iterator_range`. This class is
+    intended to be used as a parameter type for functions that need to take a
+    writable or read-only sequence of data. Most common cases are `span<char>`
+    and `span<std::uint8_t>`. Using as a class member is only recommended if
+    clearly documented as not doing a deep-copy. C-arrays are easily convertible
+    to this type.
+
+    \note Conversion from C string literal to `span<const char>` will include
+      the NULL-terminator.
+    \note Never allows derived-to-base pointer conversion; an array of derived
+      types is not an array of base types.
+   */
+  template<typename T>
+  class span
+  {
+    template<typename U>
+    static constexpr bool safe_conversion() noexcept
+    {
+      // Allow exact matches or `T*` -> `const T*`.
+      using with_const = typename std::add_const<U>::type;
+      return std::is_same<T, U>() ||
+        (std::is_const<T>() && std::is_same<T, with_const>());
+    }
+
+  public:
+    using value_type = T;
+    using size_type = std::size_t;
+    using difference_type = std::ptrdiff_t;
+    using pointer = T*;
+    using const_pointer = const T*;
+    using reference = T&;
+    using const_reference = const T&;
+    using iterator = pointer;
+    using const_iterator = const_pointer;
+
+    constexpr span() noexcept : ptr(nullptr), len(0) {}
+    constexpr span(std::nullptr_t) noexcept : span() {}
+
+    //! Prevent derived-to-base conversions; invalid in this context.
+    template<typename U, typename = typename std::enable_if<safe_conversion<U>()>::type>
+    constexpr span(U* const src_ptr, const std::size_t count) noexcept
+      : ptr(src_ptr), len(count) {}
+
+    //! Conversion from C-array. Prevents common bugs with sizeof + arrays.
+    template<std::size_t N>
+    constexpr span(T (&src)[N]) noexcept : span(src, N) {}
+
+    constexpr span(const span&) noexcept = default;
+    span& operator=(const span&) noexcept = default;
+
+    /*! Try to remove `amount` elements from beginning of span.
+    \return Number of elements removed. */
+    std::size_t remove_prefix(std::size_t amount) noexcept
+    {
+        amount = std::min(len, amount);
+        ptr += amount;
+        len -= amount;
+        return amount;
+    }
+
+    constexpr iterator begin() const noexcept { return ptr; }
+    constexpr const_iterator cbegin() const noexcept { return ptr; }
+
+    constexpr iterator end() const noexcept { return begin() + size(); }
+    constexpr const_iterator cend() const noexcept { return cbegin() + size(); }
+
+    constexpr bool empty() const noexcept { return size() == 0; }
+    constexpr pointer data() const noexcept { return ptr; }
+    constexpr std::size_t size() const noexcept { return len; }
+    constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(value_type); }
+
+    T &operator[](size_t idx) noexcept { return ptr[idx]; }
+    const T &operator[](size_t idx) const noexcept { return ptr[idx]; }
+
+  private:
+    T* ptr;
+    std::size_t len;
+  };
+
+  //! \return `span<const T::value_type>` from a STL compatible `src`.
+  template<typename T>
+  constexpr span<const typename T::value_type> to_span(const T& src)
+  {
+    // compiler provides diagnostic if size() is not size_t.
+    return {src.data(), src.size()};
+  }
+
+  //! \return `span<T::value_type>` from a STL compatible `src`.
+  template<typename T>
+  constexpr span<typename T::value_type> to_mut_span(T& src)
+  {
+    // compiler provides diagnostic if size() is not size_t.
+    return {src.data(), src.size()};
+  }
+
+  template<typename T>
+  constexpr bool has_padding() noexcept
+  {
+    return !std::is_standard_layout<T>() || alignof(T) != 1;
+  }
+
+  //! \return Cast data from `src` as `span<const std::uint8_t>`.
+  template<typename T>
+  span<const std::uint8_t> to_byte_span(const span<const T> src) noexcept
+  {
+    static_assert(!has_padding<T>(), "source type may have padding");
+    return {reinterpret_cast<const std::uint8_t*>(src.data()), src.size_bytes()}; 
+  }
+
+  //! \return `span<const std::uint8_t>` which represents the bytes at `&src`.
+  template<typename T>
+  span<const std::uint8_t> as_byte_span(const T& src) noexcept
+  {
+    static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
+    static_assert(!has_padding<T>(), "source type may have padding");
+    return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)};
+  }
+
+  //! \return `span<std::uint8_t>` which represents the bytes at `&src`.
+  template<typename T>
+  span<std::uint8_t> as_mut_byte_span(T& src) noexcept
+  {
+    static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1");
+    static_assert(!has_padding<T>(), "source type may have padding");
+    return {reinterpret_cast<std::uint8_t*>(std::addressof(src)), sizeof(T)};
+  }
+
+  //! make a span from a std::string
+  template<typename T>
+  span<const T> strspan(const std::string &s) noexcept
+  {
+    static_assert(std::is_same<T, char>() || std::is_same<T, unsigned char>() || std::is_same<T, int8_t>() || std::is_same<T, uint8_t>(), "Unexpected type");
+    return {reinterpret_cast<const T*>(s.data()), s.size()};
+  }
+}
diff --git a/src/base/base.cmake b/src/base/base.cmake
index 4cf1ab72d..3246d6f99 100644
--- a/src/base/base.cmake
+++ b/src/base/base.cmake
@@ -1,5 +1,5 @@
 set(HEADERS_BASE
-    src/3rdparty/fmt/format.cc
+    src/3rdparty/epee/span.h
     src/base/api/interfaces/IApiListener.h
     src/base/crypto/Algorithm.h
     src/base/crypto/Coin.h
@@ -81,11 +81,13 @@ set(HEADERS_BASE
     src/base/tools/cryptonote/WalletAddress.h
     src/base/tools/Cvt.h
     src/base/tools/Handle.h
+    src/base/tools/Span.h
     src/base/tools/String.h
     src/base/tools/Timer.h
    )
 
 set(SOURCES_BASE
+    src/3rdparty/fmt/format.cc
     src/base/crypto/Algorithm.cpp
     src/base/crypto/Coin.cpp
     src/base/crypto/keccak.cpp
@@ -130,14 +132,14 @@ set(SOURCES_BASE
     src/base/net/tools/LineReader.cpp
     src/base/net/tools/NetBuffer.cpp
     src/base/tools/Arguments.cpp
+    src/base/tools/cryptonote/BlockTemplate.cpp
+    src/base/tools/cryptonote/crypto-ops-data.c
+    src/base/tools/cryptonote/crypto-ops.c
+    src/base/tools/cryptonote/Signatures.cpp
+    src/base/tools/cryptonote/WalletAddress.cpp
     src/base/tools/Cvt.cpp
     src/base/tools/String.cpp
     src/base/tools/Timer.cpp
-    src/base/tools/cryptonote/BlockTemplate.cpp
-    src/base/tools/cryptonote/Signatures.cpp
-    src/base/tools/cryptonote/WalletAddress.cpp
-    src/base/tools/cryptonote/crypto-ops.c
-    src/base/tools/cryptonote/crypto-ops-data.c
    )
 
 
diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp
index 5cea361d8..50a3a2324 100644
--- a/src/base/net/stratum/DaemonClient.cpp
+++ b/src/base/net/stratum/DaemonClient.cpp
@@ -137,21 +137,21 @@ int64_t xmrig::DaemonClient::submit(const JobResult &result)
 
     memcpy(data + m_job.nonceOffset() * 2, result.nonce, 8);
 
-    if (m_blocktemplate.has_miner_signature && result.sig) {
+    if (m_blocktemplate.hasMinerSignature() && result.sig) {
         memcpy(data + sig_offset * 2, result.sig, 64 * 2);
-        memcpy(data + m_blocktemplate.tx_pubkey_index * 2, result.sig_data, 32 * 2);
-        memcpy(data + m_blocktemplate.eph_public_key_index * 2, result.sig_data + 32 * 2, 32 * 2);
+        memcpy(data + m_blocktemplate.offset(BlockTemplate::TX_PUBKEY_OFFSET) * 2, result.sig_data, 32 * 2);
+        memcpy(data + m_blocktemplate.offset(BlockTemplate::EPH_PUBLIC_KEY_OFFSET) * 2, result.sig_data + 32 * 2, 32 * 2);
     }
 
     if (result.extra_nonce >= 0) {
-        Cvt::toHex(data + m_blocktemplate.tx_extra_nonce_index * 2, 8, reinterpret_cast<const uint8_t*>(&result.extra_nonce), 4);
+        Cvt::toHex(data + m_blocktemplate.offset(BlockTemplate::TX_EXTRA_NONCE_OFFSET) * 2, 8, reinterpret_cast<const uint8_t*>(&result.extra_nonce), 4);
     }
 
 #   else
 
     Cvt::toHex(data + m_job.nonceOffset() * 2, 8, reinterpret_cast<const uint8_t*>(&result.nonce), 4);
 
-    if (m_blocktemplate.has_miner_signature) {
+    if (m_blocktemplate.hasMinerSignature()) {
         Cvt::toHex(data + sig_offset * 2, 128, result.minerSignature(), 64);
     }
 
@@ -376,29 +376,29 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
     String blocktemplate = Json::getString(params, kBlocktemplateBlob);
 
     if (blocktemplate.isNull()) {
-        return jobError("Empty block template received from daemon.");
+        return jobError("Empty block template received from daemon."); // FIXME
     }
 
-    if (!m_blocktemplate.Init(blocktemplate, m_coin)) {
+    if (!m_blocktemplate.parse(blocktemplate, m_coin)) {
         return jobError("Invalid block template received from daemon.");
     }
 
 #   ifdef XMRIG_PROXY_PROJECT
-    const size_t k = m_blocktemplate.miner_tx_prefix_begin_index;
+    const size_t k = m_blocktemplate.offset(BlockTemplate::MINER_TX_PREFIX_OFFSET);
     job.setMinerTx(
-        m_blocktemplate.raw_blob.data() + k,
-        m_blocktemplate.raw_blob.data() + m_blocktemplate.miner_tx_prefix_end_index,
-        m_blocktemplate.eph_public_key_index - k,
-        m_blocktemplate.tx_pubkey_index - k,
-        m_blocktemplate.tx_extra_nonce_index - k,
-        m_blocktemplate.tx_extra_nonce_size,
-        m_blocktemplate.miner_tx_merkle_tree_branch
+        m_blocktemplate.blob() + k,
+        m_blocktemplate.blob() + m_blocktemplate.offset(BlockTemplate::MINER_TX_PREFIX_END_OFFSET),
+        m_blocktemplate.offset(BlockTemplate::EPH_PUBLIC_KEY_OFFSET) - k,
+        m_blocktemplate.offset(BlockTemplate::TX_PUBKEY_OFFSET) - k,
+        m_blocktemplate.offset(BlockTemplate::TX_EXTRA_NONCE_OFFSET) - k,
+        m_blocktemplate.txExtraNonce().size(),
+        m_blocktemplate.minerTxMerkleTreeBranch()
     );
 #   endif
 
     m_blockhashingblob = Json::getString(params, "blockhashing_blob");
 
-    if (m_blocktemplate.has_miner_signature) {
+    if (m_blocktemplate.hasMinerSignature()) {
         if (m_pool.spendSecretKey().isEmpty()) {
             return jobError("Secret spend key is not set.");
         }
@@ -429,7 +429,7 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
         }
 
         uint8_t derivation[32];
-        if (!generate_key_derivation(m_blocktemplate.raw_blob.data() + m_blocktemplate.tx_pubkey_index, secret_viewkey, derivation)) {
+        if (!generate_key_derivation(m_blocktemplate.blob(BlockTemplate::TX_PUBKEY_OFFSET), secret_viewkey, derivation)) {
             return jobError("Failed to generate key derivation for miner signature.");
         }
 
@@ -448,7 +448,7 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
         uint8_t eph_secret_key[32];
         derive_secret_key(derivation, 0, secret_spendkey, eph_secret_key);
 
-        job.setEphemeralKeys(m_blocktemplate.raw_blob.data() + m_blocktemplate.eph_public_key_index, eph_secret_key);
+        job.setEphemeralKeys(m_blocktemplate.blob(BlockTemplate::EPH_PUBLIC_KEY_OFFSET), eph_secret_key);
 #       endif
     }
 
@@ -458,7 +458,7 @@ bool xmrig::DaemonClient::parseJob(const rapidjson::Value &params, int *code)
     }
 
     if (m_coin.isValid()) {
-        job.setAlgorithm(m_coin.algorithm(m_blocktemplate.major_version));
+        job.setAlgorithm(m_coin.algorithm(m_blocktemplate.majorVersion()));
     }
 
     if (!job.setBlob(m_blockhashingblob)) {
diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp
index e765978f4..818160113 100644
--- a/src/base/net/stratum/Job.cpp
+++ b/src/base/net/stratum/Job.cpp
@@ -352,16 +352,16 @@ void xmrig::Job::generateHashingBlob(String &blob) const
 {
     uint8_t root_hash[32];
     const uint8_t* p = m_minerTxPrefix.data();
-    BlockTemplate::CalculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash);
+    BlockTemplate::calculateRootHash(p, p + m_minerTxPrefix.size(), m_minerTxMerkleTreeBranch, root_hash);
 
     uint64_t root_hash_offset = nonceOffset() + nonceSize();
 
     if (m_hasMinerSignature) {
-        root_hash_offset += BlockTemplate::SIGNATURE_SIZE + 2 /* vote */;
+        root_hash_offset += BlockTemplate::kSignatureSize + 2 /* vote */;
     }
 
     blob = rawBlob();
-    Cvt::toHex(blob.data() + root_hash_offset * 2, 64, root_hash, BlockTemplate::HASH_SIZE);
+    Cvt::toHex(blob.data() + root_hash_offset * 2, 64, root_hash, BlockTemplate::kHashSize);
 }
 
 
@@ -374,7 +374,7 @@ void xmrig::Job::generateMinerSignature(const uint8_t* blob, size_t size, uint8_
     memcpy(tmp, blob, size);
 
     // Fill signature with zeros
-    memset(tmp + nonceOffset() + nonceSize(), 0, BlockTemplate::SIGNATURE_SIZE);
+    memset(tmp + nonceOffset() + nonceSize(), 0, BlockTemplate::kSignatureSize);
 
     uint8_t prefix_hash[32];
     xmrig::keccak(tmp, static_cast<int>(size), prefix_hash, sizeof(prefix_hash));
diff --git a/src/base/tools/Cvt.cpp b/src/base/tools/Cvt.cpp
index 18d5e627a..0d1c89fb0 100644
--- a/src/base/tools/Cvt.cpp
+++ b/src/base/tools/Cvt.cpp
@@ -17,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/tools/Cvt.h"
 #include "3rdparty/rapidjson/document.h"
 
@@ -245,6 +244,12 @@ rapidjson::Value xmrig::Cvt::toHex(const Buffer &data, rapidjson::Document &doc)
 }
 
 
+rapidjson::Value xmrig::Cvt::toHex(const Span &data, rapidjson::Document &doc)
+{
+    return toHex(data.data(), data.size(), doc);
+}
+
+
 rapidjson::Value xmrig::Cvt::toHex(const std::string &data, rapidjson::Document &doc)
 {
     return toHex(reinterpret_cast<const uint8_t *>(data.data()), data.size(), doc);
diff --git a/src/base/tools/Cvt.h b/src/base/tools/Cvt.h
index e7507aafd..58058127b 100644
--- a/src/base/tools/Cvt.h
+++ b/src/base/tools/Cvt.h
@@ -22,6 +22,7 @@
 
 #include "3rdparty/rapidjson/fwd.h"
 #include "base/tools/Buffer.h"
+#include "base/tools/Span.h"
 #include "base/tools/String.h"
 
 
@@ -37,9 +38,11 @@ public:
     inline static bool fromHex(Buffer &buf, const String &hex)                  { return fromHex(buf, hex.data(), hex.size()); }
     inline static Buffer fromHex(const std::string &hex)                        { return fromHex(hex.data(), hex.size()); }
     inline static Buffer fromHex(const String &hex)                             { return fromHex(hex.data(), hex.size()); }
-    inline static String toHex(const Buffer &data)                              { return toHex(data.data(), data.size()); }
     inline static String toHex(const std::string &data)                         { return toHex(reinterpret_cast<const uint8_t *>(data.data()), data.size()); }
 
+    template<typename T>
+    inline static String toHex(const T &data)                                   { return toHex(data.data(), data.size()); }
+
     static bool fromHex(Buffer &buf, const char *in, size_t size);
     static bool fromHex(Buffer &buf, const rapidjson::Value &value);
     static bool fromHex(std::string &buf, const char *in, size_t size);
@@ -49,6 +52,7 @@ public:
     static Buffer fromHex(const char *in, size_t size);
     static Buffer randomBytes(size_t size);
     static rapidjson::Value toHex(const Buffer &data, rapidjson::Document &doc);
+    static rapidjson::Value toHex(const Span &data, rapidjson::Document &doc);
     static rapidjson::Value toHex(const std::string &data, rapidjson::Document &doc);
     static rapidjson::Value toHex(const uint8_t *in, size_t size, rapidjson::Document &doc);
     static String toHex(const uint8_t *in, size_t size);
diff --git a/src/base/tools/Span.h b/src/base/tools/Span.h
new file mode 100644
index 000000000..5d8acebc4
--- /dev/null
+++ b/src/base/tools/Span.h
@@ -0,0 +1,35 @@
+/* XMRig
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef XMRIG_SPAN_H
+#define XMRIG_SPAN_H
+
+
+#include "3rdparty/epee/span.h"
+
+
+namespace xmrig {
+
+
+using Span = epee::span<const uint8_t>;
+
+
+} /* namespace xmrig */
+
+
+#endif /* XMRIG_SPAN_H */
diff --git a/src/base/tools/cryptonote/BlobReader.h b/src/base/tools/cryptonote/BlobReader.h
index e8be0d65f..a45bb972e 100644
--- a/src/base/tools/cryptonote/BlobReader.h
+++ b/src/base/tools/cryptonote/BlobReader.h
@@ -24,11 +24,13 @@
 
 #include <cstdint>
 #include <cstring>
+#include <stdexcept>
 
 
 namespace xmrig {
 
 
+template<bool EXCEPTIONS>
 class BlobReader
 {
 public:
@@ -45,7 +47,7 @@ public:
     inline bool skip(size_t n)
     {
         if (m_index + n > m_size) {
-            return false;
+            return outOfRange();
         }
 
         m_index += n;
@@ -57,7 +59,7 @@ public:
     inline bool operator()(uint8_t(&data)[N])
     {
         if (m_index + N > m_size) {
-            return false;
+            return outOfRange();
         }
 
         memcpy(data, m_data + m_index, N);
@@ -67,19 +69,23 @@ public:
     }
 
     template<typename T>
-    inline void readItems(T &data, size_t count)
+    inline bool operator()(T &data, size_t n)
     {
-        data.resize(count);
-        for (size_t i = 0; i < count; ++i) {
-            operator()(data[i]);
+        if (m_index + n > m_size) {
+            return outOfRange();
         }
+
+        data = { m_data + m_index, n };
+        m_index += n;
+
+        return true;
     }
 
 private:
     inline bool getByte(uint8_t &data)
     {
         if (m_index >= m_size) {
-            return false;
+            return outOfRange();
         }
 
         data = m_data[m_index++];
@@ -95,8 +101,9 @@ private:
 
         do {
             if (!getByte(t)) {
-                return false;
+                return outOfRange();
             }
+
             result |= static_cast<uint64_t>(t & 0x7F) << shift;
             shift += 7;
         } while (t & 0x80);
@@ -106,6 +113,15 @@ private:
         return true;
     }
 
+    inline bool outOfRange()
+    {
+        if (EXCEPTIONS) {
+            throw std::out_of_range("Blob read out of range");
+        }
+
+        return false;
+    }
+
     const size_t m_size;
     const uint8_t *m_data;
     size_t m_index  = 0;
diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp
index ede0b19a8..db3d0496b 100644
--- a/src/base/tools/cryptonote/BlockTemplate.cpp
+++ b/src/base/tools/cryptonote/BlockTemplate.cpp
@@ -18,177 +18,64 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-#include "base/crypto/keccak.h"
-#include "base/tools/Cvt.h"
-#include "base/tools/cryptonote/BlobReader.h"
 #include "base/tools/cryptonote/BlockTemplate.h"
+#include "3rdparty/rapidjson/document.h"
+#include "base/crypto/keccak.h"
+#include "base/tools/cryptonote/BlobReader.h"
+#include "base/tools/Cvt.h"
 
 
-namespace xmrig {
-
-
-bool BlockTemplate::Init(const String& blockTemplate, Coin coin)
+void xmrig::BlockTemplate::calculateMinerTxHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, uint8_t *hash)
 {
-    raw_blob = Cvt::fromHex(blockTemplate);
-
-    BlobReader ar(raw_blob.data(), raw_blob.size());
-
-    // Block header
-    ar(major_version);
-    ar(minor_version);
-    ar(timestamp);
-    ar(prev_id);
-    ar(nonce);
-
-    // Wownero block template has miner signature starting from version 18
-    has_miner_signature = (coin == Coin::WOWNERO) && (major_version >= 18);
-    if (has_miner_signature) {
-        ar(miner_signature);
-        ar(vote);
-    }
-
-    // Miner transaction begin
-    // Prefix begin
-    miner_tx_prefix_begin_index = ar.index();
-
-    ar(tx_version);
-    ar(unlock_time);
-    ar(num_inputs);
-
-    // must be 1 input
-    if (num_inputs != 1)
-        return false;
-
-    ar(input_type);
-
-    // input type must be txin_gen (0xFF)
-    if (input_type != 0xFF)
-        return false;
-
-    ar(height);
-
-    ar(num_outputs);
-
-    // must be 1 output
-    if (num_outputs != 1)
-        return false;
-
-    ar(amount);
-    ar(output_type);
-
-    // output type must be txout_to_key (2)
-    if (output_type != 2)
-        return false;
-
-    eph_public_key_index = ar.index();
-
-    ar(eph_public_key);
-    ar(extra_size);
-
-    const uint64_t tx_extra_index = ar.index();
-
-    ar.readItems(extra, extra_size);
-
-    BlobReader ar_extra(extra.data(), extra_size);
-
-    tx_extra_nonce_size = 0;
-    tx_extra_nonce_index = 0;
-
-    while (ar_extra.index() < extra_size) {
-        uint64_t extra_tag = 0;
-        ar_extra(extra_tag);
-
-        switch (extra_tag) {
-        case 0x01: // TX_EXTRA_TAG_PUBKEY
-            tx_pubkey_index = tx_extra_index + ar_extra.index();
-            ar_extra.skip(KEY_SIZE);
-            break;
-
-        case 0x02: // TX_EXTRA_NONCE
-            ar_extra(tx_extra_nonce_size);
-            tx_extra_nonce_index = tx_extra_index + ar_extra.index();
-            ar_extra.skip(tx_extra_nonce_size);
-            break;
-
-        default:
-            return false; // TODO: handle other tags
-        }
-    }
-
-    miner_tx_prefix_end_index = ar.index();
-    // Prefix end
-
-    // RCT signatures (empty in miner transaction)
-    ar(vin_rct_type);
-
-    // must be RCTTypeNull (0)
-    if (vin_rct_type != 0)
-        return false;
-
-    const size_t miner_tx_end = ar.index();
-    // Miner transaction end
-
-    // Miner transaction must have exactly 1 byte with value 0 after the prefix
-    if ((miner_tx_end != miner_tx_prefix_end_index + 1) || (raw_blob[miner_tx_prefix_end_index] != 0))
-        return false;
-
-    // Other transaction hashes
-    ar(num_hashes);
-
-#   ifdef XMRIG_PROXY_PROJECT
-    hashes.resize((num_hashes + 1) * HASH_SIZE);
-    CalculateMinerTxHash(raw_blob.data() + miner_tx_prefix_begin_index, raw_blob.data() + miner_tx_prefix_end_index, hashes.data());
-
-    for (uint64_t i = 1; i <= num_hashes; ++i) {
-        uint8_t h[HASH_SIZE];
-        ar(h);
-        memcpy(hashes.data() + i * HASH_SIZE, h, HASH_SIZE);
-    }
-
-    CalculateMerkleTreeHash();
-#   endif
-
-    return true;
-}
-
-
-void BlockTemplate::CalculateMinerTxHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, uint8_t* hash)
-{
-    uint8_t hashes[HASH_SIZE * 3];
+    uint8_t hashes[kHashSize * 3];
 
     // Calculate 3 partial hashes
 
     // 1. Prefix
-    keccak(prefix_begin, static_cast<int>(prefix_end - prefix_begin), hashes, HASH_SIZE);
+    keccak(prefix_begin, static_cast<int>(prefix_end - prefix_begin), hashes, kHashSize);
 
     // 2. Base RCT, single 0 byte in miner tx
-    static const uint8_t known_second_hash[HASH_SIZE] = {
+    static const uint8_t known_second_hash[kHashSize] = {
         188,54,120,158,122,30,40,20,54,70,66,41,130,143,129,125,102,18,247,180,119,214,101,145,255,150,169,224,100,188,201,138
     };
-    memcpy(hashes + HASH_SIZE, known_second_hash, HASH_SIZE);
+    memcpy(hashes + kHashSize, known_second_hash, kHashSize);
 
     // 3. Prunable RCT, empty in miner tx
-    memset(hashes + HASH_SIZE * 2, 0, HASH_SIZE);
+    memset(hashes + kHashSize * 2, 0, kHashSize);
 
     // Calculate miner transaction hash
-    keccak(hashes, sizeof(hashes), hash, HASH_SIZE);
+    keccak(hashes, sizeof(hashes), hash, kHashSize);
 }
 
 
-void BlockTemplate::CalculateMerkleTreeHash()
+void xmrig::BlockTemplate::calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint8_t *root_hash)
 {
-    miner_tx_merkle_tree_branch.clear();
+    calculateMinerTxHash(prefix_begin, prefix_end, root_hash);
 
-    const uint64_t count = num_hashes + 1;
-    uint8_t* h = hashes.data();
+    for (size_t i = 0; i < miner_tx_merkle_tree_branch.size(); i += kHashSize) {
+        uint8_t h[kHashSize * 2];
+
+        memcpy(h, root_hash, kHashSize);
+        memcpy(h + kHashSize, miner_tx_merkle_tree_branch.data() + i, kHashSize);
+
+        keccak(h, kHashSize * 2, root_hash, kHashSize);
+    }
+}
+
+
+void xmrig::BlockTemplate::calculateMerkleTreeHash()
+{
+    m_minerTxMerkleTreeBranch.clear();
+
+    const uint64_t count = m_numHashes + 1;
+    const uint8_t *h = m_hashes.data();
 
     if (count == 1) {
-        memcpy(root_hash, h, HASH_SIZE);
+        memcpy(m_rootHash, h, kHashSize);
     }
     else if (count == 2) {
-        miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
-        keccak(h, HASH_SIZE * 2, root_hash, HASH_SIZE);
+        m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize, h + kHashSize * 2);
+        keccak(h, kHashSize * 2, m_rootHash, kHashSize);
     }
     else {
         size_t i, j, cnt;
@@ -197,64 +84,222 @@ void BlockTemplate::CalculateMerkleTreeHash()
 
         cnt >>= 1;
 
-        miner_tx_merkle_tree_branch.reserve(HASH_SIZE * (i - 1));
+        m_minerTxMerkleTreeBranch.reserve(kHashSize * (i - 1));
 
-        Buffer ints(cnt * HASH_SIZE);
-        memcpy(ints.data(), h, (cnt * 2 - count) * HASH_SIZE);
+        Buffer ints(cnt * kHashSize);
+        memcpy(ints.data(), h, (cnt * 2 - count) * kHashSize);
 
         for (i = cnt * 2 - count, j = cnt * 2 - count; j < cnt; i += 2, ++j) {
             if (i == 0) {
-                miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), h + HASH_SIZE, h + HASH_SIZE * 2);
+                m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), h + kHashSize, h + kHashSize * 2);
             }
-            keccak(h + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
+            keccak(h + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize);
         }
 
         while (cnt > 2) {
             cnt >>= 1;
             for (i = 0, j = 0; j < cnt; i += 2, ++j) {
                 if (i == 0) {
-                    miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
+                    m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize, ints.data() + kHashSize * 2);
                 }
-                keccak(ints.data() + i * HASH_SIZE, HASH_SIZE * 2, ints.data() + j * HASH_SIZE, HASH_SIZE);
+                keccak(ints.data() + i * kHashSize, kHashSize * 2, ints.data() + j * kHashSize, kHashSize);
             }
         }
 
-        miner_tx_merkle_tree_branch.insert(miner_tx_merkle_tree_branch.end(), ints.data() + HASH_SIZE, ints.data() + HASH_SIZE * 2);
-        keccak(ints.data(), HASH_SIZE * 2, root_hash, HASH_SIZE);
+        m_minerTxMerkleTreeBranch.insert(m_minerTxMerkleTreeBranch.end(), ints.data() + kHashSize, ints.data() + kHashSize * 2);
+        keccak(ints.data(), kHashSize * 2, m_rootHash, kHashSize);
     }
 }
 
 
-void BlockTemplate::CalculateRootHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, const Buffer& miner_tx_merkle_tree_branch, uint8_t* root_hash)
+bool xmrig::BlockTemplate::parse(const Buffer &blocktemplate, const Coin &coin, bool hashes)
 {
-    CalculateMinerTxHash(prefix_begin, prefix_end, root_hash);
-
-    for (size_t i = 0; i < miner_tx_merkle_tree_branch.size(); i += HASH_SIZE) {
-        uint8_t h[HASH_SIZE * 2];
-
-        memcpy(h, root_hash, HASH_SIZE);
-        memcpy(h + HASH_SIZE, miner_tx_merkle_tree_branch.data() + i, HASH_SIZE);
-
-        keccak(h, HASH_SIZE * 2, root_hash, HASH_SIZE);
+    if (blocktemplate.size() < kMinSize) {
+        return false;
     }
+
+    m_blob  = blocktemplate;
+    m_coin  = coin;
+    bool rc = false;
+
+    try {
+        rc = parse(hashes);
+    } catch (...) {}
+
+    return rc;
 }
 
 
-void BlockTemplate::GenerateHashingBlob()
+bool xmrig::BlockTemplate::parse(const char *blocktemplate, size_t size, const Coin &coin, bool hashes)
 {
-    hashingBlob.clear();
-    hashingBlob.reserve(miner_tx_prefix_begin_index + HASH_SIZE + 3);
+    if (size < (kMinSize * 2) || !Cvt::fromHex(m_blob, blocktemplate, size)) {
+        return false;
+    }
 
-    hashingBlob.assign(raw_blob.begin(), raw_blob.begin() + miner_tx_prefix_begin_index);
-    hashingBlob.insert(hashingBlob.end(), root_hash, root_hash + HASH_SIZE);
+    m_coin  = coin;
+    bool rc = false;
 
-    uint64_t k = num_hashes + 1;
+    try {
+        rc = parse(hashes);
+    } catch (...) {}
+
+    return rc;
+}
+
+
+bool xmrig::BlockTemplate::parse(const rapidjson::Value &blocktemplate, const Coin &coin, bool hashes)
+{
+    return blocktemplate.IsString() && parse(blocktemplate.GetString(), blocktemplate.GetStringLength(), coin, hashes);
+}
+
+
+bool xmrig::BlockTemplate::parse(const String &blocktemplate, const Coin &coin, bool hashes)
+{
+    return parse(blocktemplate.data(), blocktemplate.size(), coin, hashes);
+}
+
+
+void xmrig::BlockTemplate::generateHashingBlob(Buffer &out) const
+{
+    out.clear();
+    out.reserve(offset(MINER_TX_PREFIX_OFFSET) + kHashSize + 3);
+
+    out.assign(m_blob.begin(), m_blob.begin() + offset(MINER_TX_PREFIX_OFFSET));
+    out.insert(out.end(), m_rootHash, m_rootHash + kHashSize);
+
+    uint64_t k = m_numHashes + 1;
     while (k >= 0x80) {
-        hashingBlob.emplace_back((static_cast<uint8_t>(k) & 0x7F) | 0x80);
+        out.emplace_back((static_cast<uint8_t>(k) & 0x7F) | 0x80);
         k >>= 7;
     }
-    hashingBlob.emplace_back(static_cast<uint8_t>(k));
+    out.emplace_back(static_cast<uint8_t>(k));
 }
 
 
-} /* namespace xmrig */
+bool xmrig::BlockTemplate::parse(bool hashes)
+{
+    BlobReader<true> ar(m_blob.data(), m_blob.size());
+
+    // Block header
+    ar(m_version.first);
+    ar(m_version.second);
+    ar(m_timestamp);
+    ar(m_prevId, kHashSize);
+
+    setOffset(NONCE_OFFSET, ar.index());
+    ar.skip(kNonceSize);
+
+    // Wownero block template has miner signature starting from version 18
+    if (m_coin == Coin::WOWNERO && majorVersion() >= 18) {
+        ar(m_minerSignature, kSignatureSize);
+        ar(m_vote);
+    }
+
+    // Miner transaction begin
+    // Prefix begin
+    setOffset(MINER_TX_PREFIX_OFFSET, ar.index());
+
+    ar(m_txVersion);
+    ar(m_unlockTime);
+    ar(m_numInputs);
+
+    // must be 1 input
+    if (m_numInputs != 1) {
+        return false;
+    }
+
+    ar(m_inputType);
+
+    // input type must be txin_gen (0xFF)
+    if (m_inputType != 0xFF) {
+        return false;
+    }
+
+    ar(m_height);
+    ar(m_numOutputs);
+
+    // must be 1 output
+    if (m_numOutputs != 1) {
+        return false;
+    }
+
+    ar(m_amount);
+    ar(m_outputType);
+
+    // output type must be txout_to_key (2)
+    if (m_outputType != 2) {
+        return false;
+    }
+
+    setOffset(EPH_PUBLIC_KEY_OFFSET, ar.index());
+
+    ar(m_ephPublicKey, kKeySize);
+    ar(m_extraSize);
+
+    setOffset(TX_EXTRA_OFFSET, ar.index());
+
+    BlobReader<true> ar_extra(blob(TX_EXTRA_OFFSET), m_extraSize);
+    ar.skip(m_extraSize);
+
+    while (ar_extra.index() < m_extraSize) {
+        uint64_t extra_tag = 0;
+        ar_extra(extra_tag);
+
+        switch (extra_tag) {
+        case 0x01: // TX_EXTRA_TAG_PUBKEY
+            setOffset(TX_PUBKEY_OFFSET, offset(TX_EXTRA_OFFSET) + ar_extra.index());
+            ar_extra.skip(kKeySize);
+            break;
+
+        case 0x02: // TX_EXTRA_NONCE
+            {
+                uint64_t size = 0;
+                ar_extra(size);
+                setOffset(TX_EXTRA_NONCE_OFFSET, offset(TX_EXTRA_OFFSET) + ar_extra.index());
+                ar_extra(m_txExtraNonce, size);
+            }
+            break;
+
+        default:
+            return false; // TODO: handle other tags
+        }
+    }
+
+    setOffset(MINER_TX_PREFIX_END_OFFSET, ar.index());
+    // Prefix end
+
+    // RCT signatures (empty in miner transaction)
+    uint8_t vin_rct_type = 0;
+    ar(vin_rct_type);
+
+    // must be RCTTypeNull (0)
+    if (vin_rct_type != 0) {
+        return false;
+    }
+
+    const size_t miner_tx_end = ar.index();
+    // Miner transaction end
+
+    // Miner transaction must have exactly 1 byte with value 0 after the prefix
+    if ((miner_tx_end != offset(MINER_TX_PREFIX_END_OFFSET) + 1) || (*blob(MINER_TX_PREFIX_END_OFFSET) != 0)) {
+        return false;
+    }
+
+    // Other transaction hashes
+    ar(m_numHashes);
+
+    if (hashes) {
+        m_hashes.resize((m_numHashes + 1) * kHashSize);
+        calculateMinerTxHash(blob(MINER_TX_PREFIX_OFFSET), blob(MINER_TX_PREFIX_END_OFFSET), m_hashes.data());
+
+        for (uint64_t i = 1; i <= m_numHashes; ++i) {
+            Span h;
+            ar(h, kHashSize);
+            memcpy(m_hashes.data() + i * kHashSize, h.data(), kHashSize);
+        }
+
+        calculateMerkleTreeHash();
+    }
+
+    return true;
+}
diff --git a/src/base/tools/cryptonote/BlockTemplate.h b/src/base/tools/cryptonote/BlockTemplate.h
index 6effd3feb..11d9e33d1 100644
--- a/src/base/tools/cryptonote/BlockTemplate.h
+++ b/src/base/tools/cryptonote/BlockTemplate.h
@@ -22,71 +22,129 @@
 #define XMRIG_BLOCKTEMPLATE_H
 
 
+#include "3rdparty/rapidjson/fwd.h"
 #include "base/crypto/Coin.h"
 #include "base/tools/Buffer.h"
 #include "base/tools/String.h"
+#include "base/tools/Span.h"
 
 
 namespace xmrig {
 
 
-struct BlockTemplate
+class BlockTemplate
 {
-    enum {
-        HASH_SIZE = 32,
-        KEY_SIZE = 32,
-        SIGNATURE_SIZE = 64,
-        NONCE_SIZE = 4,
+public:
+    static constexpr size_t kHashSize       = 32;
+    static constexpr size_t kKeySize        = 32;
+    static constexpr size_t kNonceSize      = 4;
+    static constexpr size_t kSignatureSize  = 64;
+
+#   ifdef XMRIG_PROXY_PROJECT
+    static constexpr bool kCalcHashes       = true;
+#   else
+    static constexpr bool kCalcHashes       = false;
+#   endif
+
+    enum Offset : uint32_t {
+        NONCE_OFFSET,
+        MINER_TX_PREFIX_OFFSET,
+        MINER_TX_PREFIX_END_OFFSET,
+        EPH_PUBLIC_KEY_OFFSET,
+        TX_EXTRA_OFFSET,
+        TX_PUBKEY_OFFSET,
+        TX_EXTRA_NONCE_OFFSET,
+        OFFSET_COUNT
     };
 
-    Buffer raw_blob;
-    size_t eph_public_key_index;
-    size_t tx_pubkey_index;
-    uint64_t tx_extra_nonce_size;
-    size_t tx_extra_nonce_index;
-    size_t miner_tx_prefix_begin_index;
-    size_t miner_tx_prefix_end_index;
+    inline const Coin &coin() const                         { return m_coin; }
+    inline const uint8_t *blob() const                      { return m_blob.data(); }
+    inline const uint8_t *blob(Offset offset) const         { return m_blob.data() + m_offsets[offset]; }
+    inline size_t offset(Offset offset) const               { return m_offsets[offset]; }
+    inline size_t size() const                              { return m_blob.size(); }
 
     // Block header
-    uint8_t major_version;
-    uint8_t minor_version;
-    uint64_t timestamp;
-    uint8_t prev_id[HASH_SIZE];
-    uint8_t nonce[NONCE_SIZE];
+    inline uint8_t majorVersion() const                     { return m_version.first; }
+    inline uint8_t minorVersion() const                     { return m_version.second; }
+    inline uint64_t timestamp() const                       { return m_timestamp; }
+    inline const Span &prevId() const                       { return m_prevId; }
+    inline const uint8_t *nonce() const                     { return blob(NONCE_OFFSET); }
 
-    bool has_miner_signature;
-    uint8_t miner_signature[SIGNATURE_SIZE];
-    uint8_t vote[2];
+    // Wownero miner signature
+    inline bool hasMinerSignature() const                   { return !m_minerSignature.empty(); }
+    inline const Span &minerSignature() const               { return m_minerSignature; }
+    inline const uint8_t *vote() const                      { return m_vote; }
 
     // Miner tx
-    uint64_t tx_version;
-    uint64_t unlock_time;
-    uint64_t num_inputs;
-    uint8_t input_type;
-    uint64_t height;
-    uint64_t num_outputs;
-    uint64_t amount;
-    uint8_t output_type;
-    uint8_t eph_public_key[KEY_SIZE];
-    uint64_t extra_size;
-    Buffer extra;
-    uint8_t vin_rct_type;
+    inline uint64_t txVersion() const                       { return m_txVersion; }
+    inline uint64_t unlockTime() const                      { return m_unlockTime; }
+    inline uint64_t numInputs() const                       { return m_numInputs; }
+    inline uint8_t inputType() const                        { return m_inputType; }
+    inline uint64_t height() const                          { return m_height; }
+    inline uint64_t numOutputs() const                      { return m_numOutputs; }
+    inline uint64_t amount() const                          { return m_amount; }
+    inline uint64_t outputType() const                      { return m_outputType; }
+    inline const Span &ephPublicKey() const                 { return m_ephPublicKey; }
+    inline const Span &txExtraNonce() const                 { return m_txExtraNonce; }
 
     // Transaction hashes
-    uint64_t num_hashes;
-    Buffer hashes;
+    inline uint64_t numHashes() const                       { return m_numHashes; }
+    inline const Buffer &hashes() const                     { return m_hashes; }
+    inline const Buffer &minerTxMerkleTreeBranch() const    { return m_minerTxMerkleTreeBranch; }
+    inline const uint8_t *rootHash() const                  { return m_rootHash; }
 
-    Buffer miner_tx_merkle_tree_branch;
-    uint8_t root_hash[HASH_SIZE];
+    inline Buffer generateHashingBlob() const
+    {
+        Buffer out;
+        generateHashingBlob(out);
 
-    Buffer hashingBlob;
+        return out;
+    }
 
-    bool Init(const String& blockTemplate, Coin coin);
+    static void calculateMinerTxHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, uint8_t *hash);
+    static void calculateRootHash(const uint8_t *prefix_begin, const uint8_t *prefix_end, const Buffer &miner_tx_merkle_tree_branch, uint8_t *root_hash);
 
-    static void CalculateMinerTxHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, uint8_t* hash);
-    static void CalculateRootHash(const uint8_t* prefix_begin, const uint8_t* prefix_end, const Buffer& miner_tx_merkle_tree_branch, uint8_t* root_hash);
-    void CalculateMerkleTreeHash();
-    void GenerateHashingBlob();
+    bool parse(const Buffer &blocktemplate, const Coin &coin, bool hashes = kCalcHashes);
+    bool parse(const char *blocktemplate, size_t size, const Coin &coin, bool hashes);
+    bool parse(const rapidjson::Value &blocktemplate, const Coin &coin, bool hashes = kCalcHashes);
+    bool parse(const String &blocktemplate, const Coin &coin, bool hashes = kCalcHashes);
+    void calculateMerkleTreeHash();
+    void generateHashingBlob(Buffer &out) const;
+
+private:
+    static constexpr size_t kMinSize = 76;
+
+    inline void setOffset(Offset offset, size_t value)  { m_offsets[offset] = static_cast<uint32_t>(value); }
+
+    bool parse(bool hashes);
+
+    Buffer m_blob;
+    Coin m_coin;
+    uint32_t m_offsets[OFFSET_COUNT]{};
+
+    std::pair<uint8_t, uint8_t> m_version;
+    uint64_t m_timestamp    = 0;
+    Span m_prevId;
+
+    Span m_minerSignature;
+    uint8_t m_vote[2]{};
+
+    uint64_t m_txVersion    = 0;
+    uint64_t m_unlockTime   = 0;
+    uint64_t m_numInputs    = 0;
+    uint8_t m_inputType     = 0;
+    uint64_t m_height       = 0;
+    uint64_t m_numOutputs   = 0;
+    uint64_t m_amount       = 0;
+    uint8_t m_outputType    = 0;
+    Span m_ephPublicKey;
+    uint64_t m_extraSize    = 0;
+    Span m_txExtraNonce;
+
+    uint64_t m_numHashes    = 0;
+    Buffer m_hashes;
+    Buffer m_minerTxMerkleTreeBranch;
+    uint8_t m_rootHash[kHashSize]{};
 };
 
 
diff --git a/src/base/tools/cryptonote/WalletAddress.cpp b/src/base/tools/cryptonote/WalletAddress.cpp
index 6b7f5343d..8ea2bbd64 100644
--- a/src/base/tools/cryptonote/WalletAddress.cpp
+++ b/src/base/tools/cryptonote/WalletAddress.cpp
@@ -105,7 +105,7 @@ bool xmrig::WalletAddress::decode(const char *address, size_t size)
 
     assert(data.size() == data_size);
 
-    BlobReader ar(data.data(), data_size);
+    BlobReader<false> ar(data.data(), data_size);
 
     if (ar(m_tag) && ar(m_publicSpendKey) && ar(m_publicViewKey) && ar.skip(ar.remaining() - sizeof(m_checksum)) && ar(m_checksum)) {
         uint8_t md[200];

From 3215403815deb2dd9252c6d30e94bc7f6533865b Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Mon, 23 Aug 2021 18:43:14 +0700
Subject: [PATCH 05/20] Add missing files.

---
 src/3rdparty/epee/LICENSE.txt | 25 +++++++++++++++++++++++++
 src/3rdparty/epee/README.md   |  1 +
 2 files changed, 26 insertions(+)
 create mode 100644 src/3rdparty/epee/LICENSE.txt
 create mode 100644 src/3rdparty/epee/README.md

diff --git a/src/3rdparty/epee/LICENSE.txt b/src/3rdparty/epee/LICENSE.txt
new file mode 100644
index 000000000..9835c2f69
--- /dev/null
+++ b/src/3rdparty/epee/LICENSE.txt
@@ -0,0 +1,25 @@
+Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * 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.
+    * Neither the name of the Andrey N. Sabelnikov nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+
+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 Andrey N. Sabelnikov 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.
diff --git a/src/3rdparty/epee/README.md b/src/3rdparty/epee/README.md
new file mode 100644
index 000000000..8157d3e56
--- /dev/null
+++ b/src/3rdparty/epee/README.md
@@ -0,0 +1 @@
+epee -  is a small library of helpers, wrappers, tools and and so on, used to make my life easier. 

From c7ac314110711af2a0bae4fddf914e1eebd32bc7 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Wed, 25 Aug 2021 18:45:15 +0700
Subject: [PATCH 06/20] Code cleanup based on Clang-Tidy.

---
 src/backend/common/Threads.cpp                |  4 +-
 src/backend/common/Workers.cpp                |  7 ++-
 src/backend/common/Workers.h                  |  4 +-
 src/backend/cpu/CpuBackend.cpp                |  6 +--
 src/backend/cpu/CpuConfig.cpp                 |  3 +-
 src/backend/cpu/CpuThreads.cpp                | 15 ++----
 src/backend/cpu/CpuThreads.h                  | 12 ++---
 src/backend/cpu/CpuWorker.cpp                 |  8 +--
 src/backend/cpu/CpuWorker.h                   |  4 +-
 src/backend/cpu/platform/BasicCpuInfo.cpp     |  2 +-
 src/backend/cpu/platform/HwlocCpuInfo.h       |  2 +-
 src/backend/cuda/CudaBackend.cpp              | 13 ++---
 src/backend/cuda/CudaBackend.h                | 10 +---
 src/backend/cuda/CudaConfig.cpp               | 15 ++----
 src/backend/cuda/CudaConfig.h                 | 10 +---
 src/backend/cuda/CudaThread.cpp               | 13 ++---
 src/backend/cuda/CudaThread.h                 | 10 +---
 src/backend/cuda/CudaThreads.cpp              | 13 ++---
 src/backend/cuda/CudaThreads.h                | 12 ++---
 src/backend/cuda/runners/CudaKawPowRunner.cpp | 13 ++---
 src/backend/cuda/runners/CudaKawPowRunner.h   | 10 +---
 src/backend/cuda/wrappers/NvmlLib.cpp         | 13 ++---
 src/backend/cuda/wrappers/NvmlLib.h           | 10 +---
 src/backend/opencl/OclBackend.cpp             | 13 ++---
 src/backend/opencl/OclBackend.h               | 10 +---
 src/backend/opencl/OclCache.cpp               | 17 ++----
 src/backend/opencl/OclCache.h                 | 10 +---
 src/backend/opencl/OclConfig.cpp              | 13 ++---
 src/backend/opencl/OclConfig.h                | 10 +---
 src/backend/opencl/OclThread.cpp              | 19 +++----
 src/backend/opencl/OclThread.h                | 10 +---
 src/backend/opencl/OclWorker.cpp              |  2 +-
 .../kawpow/KawPow_CalculateDAGKernel.cpp      | 15 ++----
 .../kawpow/KawPow_CalculateDAGKernel.h        | 10 +---
 .../opencl/runners/OclAstroBWTRunner.cpp      | 14 ++---
 .../opencl/runners/OclAstroBWTRunner.h        | 10 +---
 .../opencl/runners/OclKawPowRunner.cpp        | 19 +++----
 src/backend/opencl/runners/OclKawPowRunner.h  | 10 +---
 src/backend/opencl/runners/OclRxJitRunner.cpp | 20 +++----
 src/backend/opencl/runners/OclRxJitRunner.h   | 10 +---
 src/backend/opencl/runners/OclRxVmRunner.cpp  | 13 ++---
 src/backend/opencl/runners/OclRxVmRunner.h    | 10 +---
 src/backend/opencl/runners/tools/OclCnR.cpp   | 19 +++----
 .../opencl/runners/tools/OclKawPow.cpp        | 24 ++++-----
 src/backend/opencl/runners/tools/OclKawPow.h  | 10 +---
 .../opencl/runners/tools/OclSharedData.cpp    | 17 ++----
 .../opencl/runners/tools/OclSharedData.h      | 10 +---
 src/backend/opencl/wrappers/AdlLib.cpp        | 13 ++---
 src/backend/opencl/wrappers/AdlLib.h          |  6 +--
 src/backend/opencl/wrappers/OclDevice.cpp     | 12 ++---
 src/backend/opencl/wrappers/OclDevice.h       | 10 +---
 src/backend/opencl/wrappers/OclLib.cpp        | 27 ++++------
 src/backend/opencl/wrappers/OclLib.h          | 10 +---
 src/base/api/Api.cpp                          |  3 +-
 src/base/api/Httpd.cpp                        |  1 +
 src/base/api/requests/HttpApiRequest.cpp      |  3 ++
 src/base/io/Console.cpp                       |  7 ++-
 src/base/io/Console.h                         |  6 +--
 src/base/io/Env.cpp                           |  5 +-
 src/base/io/json/Json.cpp                     |  7 ++-
 src/base/io/json/Json.h                       |  4 +-
 src/base/io/json/JsonChain.cpp                | 11 ++--
 src/base/io/log/Log.cpp                       | 11 ++--
 src/base/io/log/Log.h                         |  4 +-
 src/base/io/log/backends/ConsoleLog.cpp       |  9 ++--
 src/base/io/log/backends/ConsoleLog.h         |  6 +--
 src/base/kernel/Base.cpp                      | 17 ++----
 src/base/kernel/Base.h                        | 10 +---
 src/base/kernel/Entry.cpp                     |  2 +-
 src/base/kernel/Platform_win.cpp              |  6 +--
 src/base/kernel/Process.cpp                   |  7 ++-
 src/base/kernel/Process.h                     |  4 +-
 src/base/kernel/config/BaseConfig.h           |  4 +-
 src/base/kernel/config/BaseTransform.cpp      |  5 +-
 src/base/net/dns/DnsConfig.cpp                |  1 -
 src/base/net/dns/DnsConfig.h                  |  2 +-
 src/base/net/dns/DnsRecords.cpp               |  5 +-
 src/base/net/http/Fetch.cpp                   |  3 +-
 src/base/net/http/Http.cpp                    | 13 ++---
 src/base/net/http/Http.h                      | 11 +---
 src/base/net/https/HttpsClient.cpp            |  4 +-
 src/base/net/https/HttpsContext.cpp           |  3 +-
 src/base/net/https/HttpsServer.cpp            |  7 ++-
 src/base/net/https/HttpsServer.h              |  5 +-
 src/base/net/stratum/AutoClient.cpp           | 11 ++--
 src/base/net/stratum/AutoClient.h             |  4 +-
 src/base/net/stratum/Client.cpp               | 54 +++++++++----------
 src/base/net/stratum/Client.h                 |  2 +-
 src/base/net/stratum/DaemonClient.cpp         |  6 +--
 src/base/net/stratum/EthStratumClient.cpp     |  2 +-
 src/base/net/stratum/EthStratumClient.h       |  3 +-
 src/base/net/stratum/Pool.cpp                 |  2 +-
 src/base/net/stratum/Pools.cpp                |  3 ++
 src/base/net/stratum/Socks5.cpp               |  4 +-
 src/base/net/stratum/Socks5.h                 |  5 +-
 src/base/net/stratum/Tls.cpp                  | 15 ++----
 src/base/net/stratum/Tls.h                    | 11 ++--
 src/base/net/stratum/Url.cpp                  | 14 ++---
 src/base/net/stratum/Url.h                    | 11 +---
 .../net/stratum/benchmark/BenchClient.cpp     |  2 +-
 src/base/net/stratum/benchmark/BenchClient.h  |  2 +-
 src/base/net/tls/TlsConfig.cpp                | 13 ++---
 src/base/net/tls/TlsConfig.h                  | 14 ++---
 src/base/net/tls/TlsContext.cpp               | 15 ++----
 src/base/net/tls/TlsContext.h                 | 12 ++---
 src/base/net/tls/TlsGen.cpp                   |  7 +--
 src/base/net/tls/TlsGen.h                     |  4 +-
 src/base/tools/Cvt.cpp                        | 32 +++++------
 src/base/tools/cryptonote/BlockTemplate.cpp   |  6 ++-
 src/base/tools/cryptonote/WalletAddress.cpp   |  2 +-
 src/core/Controller.cpp                       |  7 ++-
 src/core/Controller.h                         |  6 +--
 src/core/Miner.cpp                            |  4 +-
 src/core/config/Config.cpp                    |  3 +-
 src/core/config/ConfigTransform.cpp           |  4 +-
 src/crypto/astrobwt/AstroBWT.cpp              | 41 +++++++-------
 src/crypto/astrobwt/AstroBWT.h                | 19 +++----
 src/crypto/cn/CnCtx.cpp                       | 16 ++----
 src/crypto/cn/CnCtx.h                         | 12 ++---
 src/crypto/common/Nonce.cpp                   | 17 +++---
 src/crypto/common/Nonce.h                     | 10 +---
 src/crypto/common/VirtualMemory_hwloc.cpp     | 14 ++---
 src/crypto/rx/Rx.cpp                          |  2 +-
 src/crypto/rx/RxConfig.cpp                    | 15 ++----
 src/crypto/rx/RxConfig.h                      | 12 ++---
 src/crypto/rx/RxDataset.cpp                   |  7 ++-
 src/crypto/rx/RxDataset.h                     |  4 +-
 src/crypto/rx/RxFix_win.cpp                   |  5 +-
 src/crypto/rx/RxNUMAStorage.cpp               | 17 +++---
 src/crypto/rx/RxNUMAStorage.h                 |  4 +-
 src/hw/dmi/DmiReader.cpp                      |  2 -
 src/hw/msr/Msr.cpp                            |  3 +-
 src/net/JobResults.cpp                        |  2 -
 133 files changed, 437 insertions(+), 833 deletions(-)

diff --git a/src/backend/common/Threads.cpp b/src/backend/common/Threads.cpp
index 00af22451..3b71035b6 100644
--- a/src/backend/common/Threads.cpp
+++ b/src/backend/common/Threads.cpp
@@ -104,7 +104,7 @@ xmrig::String xmrig::Threads<T>::profileName(const Algorithm &algorithm, bool st
         return String();
     }
 
-    const String name = algorithm.name();
+    String name = algorithm.name();
     if (has(name)) {
         return name;
     }
@@ -122,7 +122,7 @@ xmrig::String xmrig::Threads<T>::profileName(const Algorithm &algorithm, bool st
     }
 
     if (name.contains("/")) {
-        const String base = name.split('/').at(0);
+        String base = name.split('/').at(0);
         if (has(base)) {
             return base;
         }
diff --git a/src/backend/common/Workers.cpp b/src/backend/common/Workers.cpp
index a70affe66..72d02e954 100644
--- a/src/backend/common/Workers.cpp
+++ b/src/backend/common/Workers.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/common/Workers.h"
 #include "backend/common/Hashrate.h"
 #include "backend/common/interfaces/IBackend.h"
@@ -200,7 +199,7 @@ void *xmrig::Workers<T>::onReady(void *arg)
 
 
 template<class T>
-void xmrig::Workers<T>::start(const std::vector<T> &data, bool sleep)
+void xmrig::Workers<T>::start(const std::vector<T> &data, bool /*sleep*/)
 {
     for (const auto &item : data) {
         m_workers.push_back(new Thread<T>(d_ptr->backend, m_workers.size(), item));
diff --git a/src/backend/common/Workers.h b/src/backend/common/Workers.h
index 664912431..76909dd59 100644
--- a/src/backend/common/Workers.h
+++ b/src/backend/common/Workers.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp
index 23c86bfd8..d5ada9adc 100644
--- a/src/backend/cpu/CpuBackend.cpp
+++ b/src/backend/cpu/CpuBackend.cpp
@@ -136,7 +136,7 @@ private:
 class CpuBackendPrivate
 {
 public:
-    inline CpuBackendPrivate(Controller *controller) : controller(controller)   {}
+    inline explicit CpuBackendPrivate(Controller *controller) : controller(controller)   {}
 
 
     inline void start()
@@ -159,7 +159,7 @@ public:
     }
 
 
-    size_t ways()
+    size_t ways() const
     {
         std::lock_guard<std::mutex> lock(mutex);
 
@@ -167,7 +167,7 @@ public:
     }
 
 
-    rapidjson::Value hugePages(int version, rapidjson::Document &doc)
+    rapidjson::Value hugePages(int version, rapidjson::Document &doc) const
     {
         HugePagesInfo pages;
 
diff --git a/src/backend/cpu/CpuConfig.cpp b/src/backend/cpu/CpuConfig.cpp
index 9ab332722..dc3330abb 100644
--- a/src/backend/cpu/CpuConfig.cpp
+++ b/src/backend/cpu/CpuConfig.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/cpu/CpuConfig.h"
 #include "3rdparty/rapidjson/document.h"
 #include "backend/cpu/CpuConfig_gen.h"
@@ -54,7 +53,7 @@ const char *CpuConfig::kAstroBWTAVX2        = "astrobwt-avx2";
 
 extern template class Threads<CpuThreads>;
 
-}
+} // namespace xmrig
 
 
 bool xmrig::CpuConfig::isHwAES() const
diff --git a/src/backend/cpu/CpuThreads.cpp b/src/backend/cpu/CpuThreads.cpp
index d9ae61b15..5b56a0bfc 100644
--- a/src/backend/cpu/CpuThreads.cpp
+++ b/src/backend/cpu/CpuThreads.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <algorithm>
 
 
@@ -80,7 +73,7 @@ static inline int64_t getAffinity(uint64_t index, int64_t affinity)
 }
 
 
-}
+} // namespace xmrig
 
 
 xmrig::CpuThreads::CpuThreads(const rapidjson::Value &value)
@@ -89,7 +82,7 @@ xmrig::CpuThreads::CpuThreads(const rapidjson::Value &value)
         for (auto &v : value.GetArray()) {
             CpuThread thread(v);
             if (thread.isValid()) {
-                add(std::move(thread));
+                add(thread);
             }
         }
     }
diff --git a/src/backend/cpu/CpuThreads.h b/src/backend/cpu/CpuThreads.h
index e87f52306..21a1aa0d8 100644
--- a/src/backend/cpu/CpuThreads.h
+++ b/src/backend/cpu/CpuThreads.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -47,7 +41,7 @@ public:
     inline bool isEmpty() const                             { return m_data.empty(); }
     inline const std::vector<CpuThread> &data() const       { return m_data; }
     inline size_t count() const                             { return m_data.size(); }
-    inline void add(CpuThread &&thread)                     { m_data.push_back(thread); }
+    inline void add(const CpuThread &thread)                { m_data.push_back(thread); }
     inline void add(int64_t affinity, uint32_t intensity)   { add(CpuThread(affinity, intensity)); }
     inline void reserve(size_t capacity)                    { m_data.reserve(capacity); }
 
diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp
index 085bccb6c..c5a15f9b7 100644
--- a/src/backend/cpu/CpuWorker.cpp
+++ b/src/backend/cpu/CpuWorker.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <cassert>
 #include <thread>
 #include <mutex>
@@ -302,8 +301,9 @@ void xmrig::CpuWorker<N>::start()
             {
 #               ifdef XMRIG_ALGO_ASTROBWT
                 if (job.algorithm().family() == Algorithm::ASTROBWT) {
-                    if (!astrobwt::astrobwt_dero(m_job.blob(), job.size(), m_ctx[0]->memory, m_hash, m_astrobwtMaxSize, m_astrobwtAVX2))
+                    if (!astrobwt::astrobwt_dero(m_job.blob(), job.size(), m_ctx[0]->memory, m_hash, m_astrobwtMaxSize, m_astrobwtAVX2)) {
                         valid = false;
+                    }
                 }
                 else
 #               endif
diff --git a/src/backend/cpu/CpuWorker.h b/src/backend/cpu/CpuWorker.h
index 785763af7..487b20cc0 100644
--- a/src/backend/cpu/CpuWorker.h
+++ b/src/backend/cpu/CpuWorker.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/cpu/platform/BasicCpuInfo.cpp b/src/backend/cpu/platform/BasicCpuInfo.cpp
index 9771ca452..93b128092 100644
--- a/src/backend/cpu/platform/BasicCpuInfo.cpp
+++ b/src/backend/cpu/platform/BasicCpuInfo.cpp
@@ -196,7 +196,7 @@ xmrig::BasicCpuInfo::BasicCpuInfo() :
     }
 
 #   ifdef XMRIG_FEATURE_ASM
-    if (hasAES()) {
+    if (m_flags.test(FLAG_AES)) {
         char vendor[13] = { 0 };
         int32_t data[4] = { 0 };
 
diff --git a/src/backend/cpu/platform/HwlocCpuInfo.h b/src/backend/cpu/platform/HwlocCpuInfo.h
index 69b67a305..390c7d3f3 100644
--- a/src/backend/cpu/platform/HwlocCpuInfo.h
+++ b/src/backend/cpu/platform/HwlocCpuInfo.h
@@ -65,7 +65,7 @@ protected:
 
 private:
     CpuThreads allThreads(const Algorithm &algorithm, uint32_t limit) const;
-    void processTopLevelCache(hwloc_obj_t obj, const Algorithm &algorithm, CpuThreads &threads, size_t limit) const;
+    void processTopLevelCache(hwloc_obj_t cache, const Algorithm &algorithm, CpuThreads &threads, size_t limit) const;
     void setThreads(size_t threads);
 
     static uint32_t m_features;
diff --git a/src/backend/cuda/CudaBackend.cpp b/src/backend/cuda/CudaBackend.cpp
index 59eaab1ff..0ef59981e 100644
--- a/src/backend/cuda/CudaBackend.cpp
+++ b/src/backend/cuda/CudaBackend.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <mutex>
 #include <string>
 
@@ -138,7 +131,7 @@ private:
 class CudaBackendPrivate
 {
 public:
-    inline CudaBackendPrivate(Controller *controller) :
+    inline explicit CudaBackendPrivate(Controller *controller) :
         controller(controller)
     {
         init(controller->config()->cuda());
diff --git a/src/backend/cuda/CudaBackend.h b/src/backend/cuda/CudaBackend.h
index 379e9bf18..00de003b8 100644
--- a/src/backend/cuda/CudaBackend.h
+++ b/src/backend/cuda/CudaBackend.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/cuda/CudaConfig.cpp b/src/backend/cuda/CudaConfig.cpp
index 77bc1749f..65084f241 100644
--- a/src/backend/cuda/CudaConfig.cpp
+++ b/src/backend/cuda/CudaConfig.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/cuda/CudaConfig.h"
 #include "3rdparty/rapidjson/document.h"
 #include "backend/common/Tags.h"
@@ -50,7 +43,7 @@ static const char *kNvml        = "nvml";
 extern template class Threads<CudaThreads>;
 
 
-}
+} // namespace xmrig
 
 
 rapidjson::Value xmrig::CudaConfig::toJSON(rapidjson::Document &doc) const
@@ -118,7 +111,7 @@ void xmrig::CudaConfig::read(const rapidjson::Value &value)
     if (value.IsObject()) {
         m_enabled   = Json::getBool(value, kEnabled, m_enabled);
         m_loader    = Json::getString(value, kLoader);
-        m_bfactor   = std::min(Json::getUint(value, kBfactorHint, m_bfactor), 12u);
+        m_bfactor   = std::min(Json::getUint(value, kBfactorHint, m_bfactor), 12U);
         m_bsleep    = Json::getUint(value, kBsleepHint, m_bsleep);
 
         setDevicesHint(Json::getString(value, kDevicesHint));
diff --git a/src/backend/cuda/CudaConfig.h b/src/backend/cuda/CudaConfig.h
index 1c2f2e03e..a80235169 100644
--- a/src/backend/cuda/CudaConfig.h
+++ b/src/backend/cuda/CudaConfig.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/cuda/CudaThread.cpp b/src/backend/cuda/CudaThread.cpp
index fff58eaa9..007f18021 100644
--- a/src/backend/cuda/CudaThread.cpp
+++ b/src/backend/cuda/CudaThread.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/cuda/CudaThread.h"
 #include "3rdparty/rapidjson/document.h"
 #include "backend/cuda/wrappers/CudaLib.h"
@@ -54,7 +47,7 @@ xmrig::CudaThread::CudaThread(const rapidjson::Value &value)
     m_index     = Json::getUint(value, kIndex);
     m_threads   = Json::getInt(value, kThreads);
     m_blocks    = Json::getInt(value, kBlocks);
-    m_bfactor   = std::min(Json::getUint(value, kBFactor, m_bfactor), 12u);
+    m_bfactor   = std::min(Json::getUint(value, kBFactor, m_bfactor), 12U);
     m_bsleep    = Json::getUint(value, kBSleep, m_bsleep);
     m_affinity  = Json::getUint64(value, kAffinity, m_affinity);
 
diff --git a/src/backend/cuda/CudaThread.h b/src/backend/cuda/CudaThread.h
index 75110bfdc..cd30aa7ac 100644
--- a/src/backend/cuda/CudaThread.h
+++ b/src/backend/cuda/CudaThread.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/cuda/CudaThreads.cpp b/src/backend/cuda/CudaThreads.cpp
index b5696350e..863e2927f 100644
--- a/src/backend/cuda/CudaThreads.cpp
+++ b/src/backend/cuda/CudaThreads.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/cuda/CudaThreads.h"
 #include "3rdparty/rapidjson/document.h"
 #include "base/io/json/Json.h"
@@ -37,7 +30,7 @@ xmrig::CudaThreads::CudaThreads(const rapidjson::Value &value)
         for (auto &v : value.GetArray()) {
             CudaThread thread(v);
             if (thread.isValid()) {
-                add(std::move(thread));
+                add(thread);
             }
         }
     }
diff --git a/src/backend/cuda/CudaThreads.h b/src/backend/cuda/CudaThreads.h
index eb6d54ee2..98a4abc29 100644
--- a/src/backend/cuda/CudaThreads.h
+++ b/src/backend/cuda/CudaThreads.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -46,7 +40,7 @@ public:
     inline bool isEmpty() const                              { return m_data.empty(); }
     inline const std::vector<CudaThread> &data() const       { return m_data; }
     inline size_t count() const                              { return m_data.size(); }
-    inline void add(CudaThread &&thread)                     { m_data.push_back(thread); }
+    inline void add(const CudaThread &thread)                { m_data.push_back(thread); }
     inline void reserve(size_t capacity)                     { m_data.reserve(capacity); }
 
     inline bool operator!=(const CudaThreads &other) const   { return !isEqual(other); }
diff --git a/src/backend/cuda/runners/CudaKawPowRunner.cpp b/src/backend/cuda/runners/CudaKawPowRunner.cpp
index a03dd8c8e..7b8a72362 100644
--- a/src/backend/cuda/runners/CudaKawPowRunner.cpp
+++ b/src/backend/cuda/runners/CudaKawPowRunner.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/cuda/runners/CudaKawPowRunner.h"
 #include "3rdparty/libethash/data_sizes.h"
 #include "backend/cuda/CudaLaunchData.h"
@@ -66,7 +59,7 @@ bool xmrig::CudaKawPowRunner::set(const Job &job, uint8_t *blob)
 
     const uint64_t start_ms = Chrono::steadyMSecs();
 
-    const bool result = CudaLib::kawPowPrepare(m_ctx, cache.data(), cache.size(), cache.l1_cache(), cache.dag_size(epoch), height, dag_sizes);
+    const bool result = CudaLib::kawPowPrepare(m_ctx, cache.data(), cache.size(), cache.l1_cache(), KPCache::dag_size(epoch), height, dag_sizes);
     if (!result) {
         LOG_ERR("%s " YELLOW("KawPow") RED(" failed to initialize DAG: ") RED_BOLD("%s"), Tags::nvidia(), CudaLib::lastError(m_ctx));
     }
diff --git a/src/backend/cuda/runners/CudaKawPowRunner.h b/src/backend/cuda/runners/CudaKawPowRunner.h
index ecd7642df..f49876e11 100644
--- a/src/backend/cuda/runners/CudaKawPowRunner.h
+++ b/src/backend/cuda/runners/CudaKawPowRunner.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/cuda/wrappers/NvmlLib.cpp b/src/backend/cuda/wrappers/NvmlLib.cpp
index cb05bdc17..841d19026 100644
--- a/src/backend/cuda/wrappers/NvmlLib.cpp
+++ b/src/backend/cuda/wrappers/NvmlLib.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <stdexcept>
 #include <uv.h>
 
@@ -117,7 +110,7 @@ bool xmrig::NvmlLib::assign(std::vector<CudaDevice> &devices)
     }
 
     for (uint32_t i = 0; i < count; i++) {
-        nvmlDevice_t nvmlDevice;
+        nvmlDevice_t nvmlDevice = nullptr;
         if (pNvmlDeviceGetHandleByIndex(i, &nvmlDevice) != NVML_SUCCESS) {
             continue;
         }
diff --git a/src/backend/cuda/wrappers/NvmlLib.h b/src/backend/cuda/wrappers/NvmlLib.h
index 85b80d0ca..79fc3a7fd 100644
--- a/src/backend/cuda/wrappers/NvmlLib.h
+++ b/src/backend/cuda/wrappers/NvmlLib.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/OclBackend.cpp b/src/backend/opencl/OclBackend.cpp
index d0d04f37d..18b8892a1 100644
--- a/src/backend/opencl/OclBackend.cpp
+++ b/src/backend/opencl/OclBackend.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <mutex>
 #include <string>
 
@@ -134,7 +127,7 @@ private:
 class OclBackendPrivate
 {
 public:
-    inline OclBackendPrivate(Controller *controller) :
+    inline explicit OclBackendPrivate(Controller *controller) :
         controller(controller)
     {
         init(controller->config()->cl());
diff --git a/src/backend/opencl/OclBackend.h b/src/backend/opencl/OclBackend.h
index 1c196bae6..64236ec99 100644
--- a/src/backend/opencl/OclBackend.h
+++ b/src/backend/opencl/OclBackend.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/OclCache.cpp b/src/backend/opencl/OclCache.cpp
index e93019c88..6f3d670a9 100644
--- a/src/backend/opencl/OclCache.cpp
+++ b/src/backend/opencl/OclCache.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <fstream>
 #include <map>
 #include <mutex>
@@ -50,7 +43,7 @@ static cl_program createFromSource(const IOclRunner *runner)
 {
     LOG_INFO("%s GPU " WHITE_BOLD("#%zu") " " YELLOW_BOLD("compiling..."), ocl_tag(), runner->data().device.index());
 
-    cl_int ret;
+    cl_int ret = 0;
     cl_device_id device = runner->data().device.id();
     const char *source  = runner->source();
     const uint64_t ts   = Chrono::steadyMSecs();
@@ -89,8 +82,8 @@ static cl_program createFromBinary(const IOclRunner *runner, const std::string &
     auto data_ptr           = s.data();
     cl_device_id device     = runner->data().device.id();
 
-    cl_int clStatus;
-    cl_int ret;
+    cl_int clStatus = 0;
+    cl_int ret      = 0;
     cl_program program = OclLib::createProgramWithBinary(runner->ctx(), 1, &device, &bin_size, reinterpret_cast<const unsigned char **>(&data_ptr), &clStatus, &ret);
     if (ret != CL_SUCCESS) {
         return nullptr;
diff --git a/src/backend/opencl/OclCache.h b/src/backend/opencl/OclCache.h
index 5f17bfca9..942ce39c2 100644
--- a/src/backend/opencl/OclCache.h
+++ b/src/backend/opencl/OclCache.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/OclConfig.cpp b/src/backend/opencl/OclConfig.cpp
index 4f7349adc..06a9b2831 100644
--- a/src/backend/opencl/OclConfig.cpp
+++ b/src/backend/opencl/OclConfig.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/opencl/OclConfig.h"
 #include "3rdparty/rapidjson/document.h"
 #include "backend/common/Tags.h"
@@ -55,7 +48,7 @@ static const char *kAdl         = "adl";
 extern template class Threads<OclThreads>;
 
 
-}
+} // namespace xmrig
 
 
 #ifndef XMRIG_OS_APPLE
diff --git a/src/backend/opencl/OclConfig.h b/src/backend/opencl/OclConfig.h
index 0aeca97ad..7002a46d2 100644
--- a/src/backend/opencl/OclConfig.h
+++ b/src/backend/opencl/OclConfig.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/OclThread.cpp b/src/backend/opencl/OclThread.cpp
index 77f6ef11b..c04d5e12a 100644
--- a/src/backend/opencl/OclThread.cpp
+++ b/src/backend/opencl/OclThread.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/opencl/OclThread.h"
 #include "3rdparty/rapidjson/document.h"
 #include "base/io/json/Json.h"
@@ -56,15 +49,15 @@ xmrig::OclThread::OclThread(const rapidjson::Value &value)
     }
 
     m_index         = Json::getUint(value, kIndex);
-    m_worksize      = std::max(std::min(Json::getUint(value, kWorksize), 512u), 1u);
-    m_unrollFactor  = std::max(std::min(Json::getUint(value, kUnroll, m_unrollFactor), 128u), 1u);
+    m_worksize      = std::max(std::min(Json::getUint(value, kWorksize), 512U), 1U);
+    m_unrollFactor  = std::max(std::min(Json::getUint(value, kUnroll, m_unrollFactor), 128U), 1U);
 
     setIntensity(Json::getUint(value, kIntensity));
 
     const auto &si = Json::getArray(value, kStridedIndex);
     if (si.IsArray() && si.Size() >= 2) {
-        m_stridedIndex = std::min(si[0].GetUint(), 2u);
-        m_memChunk     = std::min(si[1].GetUint(), 18u);
+        m_stridedIndex = std::min(si[0].GetUint(), 2U);
+        m_memChunk     = std::min(si[1].GetUint(), 18U);
     }
     else {
         m_stridedIndex = 0;
diff --git a/src/backend/opencl/OclThread.h b/src/backend/opencl/OclThread.h
index 0c3f03e23..5d2e6658d 100644
--- a/src/backend/opencl/OclThread.h
+++ b/src/backend/opencl/OclThread.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/OclWorker.cpp b/src/backend/opencl/OclWorker.cpp
index 727a64c9c..d2563d8ff 100644
--- a/src/backend/opencl/OclWorker.cpp
+++ b/src/backend/opencl/OclWorker.cpp
@@ -85,7 +85,7 @@ xmrig::OclWorker::OclWorker(size_t id, const OclLaunchData &data) :
 
     case Algorithm::ARGON2:
 #       ifdef XMRIG_ALGO_ARGON2
-        m_runner = nullptr; // TODO OclArgon2Runner
+        m_runner = nullptr;
 #       endif
         break;
 
diff --git a/src/backend/opencl/kernels/kawpow/KawPow_CalculateDAGKernel.cpp b/src/backend/opencl/kernels/kawpow/KawPow_CalculateDAGKernel.cpp
index 0130a37d6..ec1ed6f46 100644
--- a/src/backend/opencl/kernels/kawpow/KawPow_CalculateDAGKernel.cpp
+++ b/src/backend/opencl/kernels/kawpow/KawPow_CalculateDAGKernel.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "KawPow_CalculateDAGKernel.h"
 #include "backend/opencl/wrappers/OclLib.h"
 #include "crypto/kawpow/KPCache.h"
@@ -37,8 +30,8 @@ void xmrig::KawPow_CalculateDAGKernel::enqueue(cl_command_queue queue, size_t th
 void xmrig::KawPow_CalculateDAGKernel::setArgs(uint32_t start, cl_mem g_light, cl_mem g_dag, uint32_t dag_words, uint32_t light_words)
 {
     setArg(0, sizeof(start), &start);
-    setArg(1, sizeof(g_light), &g_light);
-    setArg(2, sizeof(g_dag), &g_dag);
+    setArg(1, sizeof(cl_mem), &g_light);
+    setArg(2, sizeof(cl_mem), &g_dag);
 
     const uint32_t isolate = 1;
     setArg(3, sizeof(isolate), &isolate);
diff --git a/src/backend/opencl/kernels/kawpow/KawPow_CalculateDAGKernel.h b/src/backend/opencl/kernels/kawpow/KawPow_CalculateDAGKernel.h
index 042b768f0..b5ceee8bd 100644
--- a/src/backend/opencl/kernels/kawpow/KawPow_CalculateDAGKernel.h
+++ b/src/backend/opencl/kernels/kawpow/KawPow_CalculateDAGKernel.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/runners/OclAstroBWTRunner.cpp b/src/backend/opencl/runners/OclAstroBWTRunner.cpp
index fb9ecb22b..7470fd62d 100644
--- a/src/backend/opencl/runners/OclAstroBWTRunner.cpp
+++ b/src/backend/opencl/runners/OclAstroBWTRunner.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/opencl/runners/OclAstroBWTRunner.h"
 #include "backend/opencl/kernels/astrobwt/AstroBWT_FilterKernel.h"
 #include "backend/opencl/kernels/astrobwt/AstroBWT_FindSharesKernel.h"
@@ -125,8 +118,9 @@ void xmrig::OclAstroBWTRunner::run(uint32_t nonce, uint32_t *hashOutput)
 
     m_sha3_initial_kernel->enqueue(m_queue, m_batch_size1);
 
-    for (uint32_t i = 0; i < m_batch_size1; ++i)
+    for (uint32_t i = 0; i < m_batch_size1; ++i) {
         m_bwt_data_sizes_host[i] = STAGE1_SIZE;
+    }
 
     enqueueWriteBuffer(m_bwt_data_sizes, CL_FALSE, 0, m_batch_size1 * sizeof(uint32_t), m_bwt_data_sizes_host);
 
diff --git a/src/backend/opencl/runners/OclAstroBWTRunner.h b/src/backend/opencl/runners/OclAstroBWTRunner.h
index 5b337e9c5..d1204f920 100644
--- a/src/backend/opencl/runners/OclAstroBWTRunner.h
+++ b/src/backend/opencl/runners/OclAstroBWTRunner.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/runners/OclKawPowRunner.cpp b/src/backend/opencl/runners/OclKawPowRunner.cpp
index fcc0058b8..275e3f9a7 100644
--- a/src/backend/opencl/runners/OclKawPowRunner.cpp
+++ b/src/backend/opencl/runners/OclKawPowRunner.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/opencl/runners/OclKawPowRunner.h"
 #include "backend/common/Tags.h"
 #include "3rdparty/libethash/ethash_internal.h"
@@ -172,12 +165,12 @@ void OclKawPowRunner::set(const Job &job, uint8_t *blob)
     const uint64_t target = job.target();
     const uint32_t hack_false = 0;
 
-    OclLib::setKernelArg(m_searchKernel, 0, sizeof(m_dag), &m_dag);
-    OclLib::setKernelArg(m_searchKernel, 1, sizeof(m_input), &m_input);
+    OclLib::setKernelArg(m_searchKernel, 0, sizeof(cl_mem), &m_dag);
+    OclLib::setKernelArg(m_searchKernel, 1, sizeof(cl_mem), &m_input);
     OclLib::setKernelArg(m_searchKernel, 2, sizeof(target), &target);
     OclLib::setKernelArg(m_searchKernel, 3, sizeof(hack_false), &hack_false);
-    OclLib::setKernelArg(m_searchKernel, 4, sizeof(m_output), &m_output);
-    OclLib::setKernelArg(m_searchKernel, 5, sizeof(m_stop), &m_stop);
+    OclLib::setKernelArg(m_searchKernel, 4, sizeof(cl_mem), &m_output);
+    OclLib::setKernelArg(m_searchKernel, 5, sizeof(cl_mem), &m_stop);
 
     m_blob = blob;
     enqueueWriteBuffer(m_input, CL_TRUE, 0, BLOB_SIZE, m_blob);
diff --git a/src/backend/opencl/runners/OclKawPowRunner.h b/src/backend/opencl/runners/OclKawPowRunner.h
index a88414e53..d236ce1ea 100644
--- a/src/backend/opencl/runners/OclKawPowRunner.h
+++ b/src/backend/opencl/runners/OclKawPowRunner.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/runners/OclRxJitRunner.cpp b/src/backend/opencl/runners/OclRxJitRunner.cpp
index 84d84c49a..c106c94d5 100644
--- a/src/backend/opencl/runners/OclRxJitRunner.cpp
+++ b/src/backend/opencl/runners/OclRxJitRunner.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -105,7 +99,7 @@ bool xmrig::OclRxJitRunner::loadAsmProgram()
     uint32_t elf_header_flags = 0;
     const uint32_t elf_header_flags_offset = 0x30;
 
-    size_t bin_size;
+    size_t bin_size = 0;
     if (OclLib::getProgramInfo(m_program, CL_PROGRAM_BINARY_SIZES, sizeof(bin_size), &bin_size) != CL_SUCCESS) {
         return false;
     }
@@ -120,8 +114,8 @@ bool xmrig::OclRxJitRunner::loadAsmProgram()
         elf_header_flags = *reinterpret_cast<uint32_t*>((binary_data.data() + elf_header_flags_offset));
     }
 
-    size_t len;
-    unsigned char *binary;
+    size_t len              = 0;
+    unsigned char *binary   = nullptr;
 
     switch (m_gcn_version) {
     case 14:
@@ -143,8 +137,8 @@ bool xmrig::OclRxJitRunner::loadAsmProgram()
         *reinterpret_cast<uint32_t*>(binary + elf_header_flags_offset) = elf_header_flags;
     }
 
-    cl_int status;
-    cl_int ret;
+    cl_int status   = 0;
+    cl_int ret      = 0;
     cl_device_id device = data().device.id();
 
     m_asmProgram = OclLib::createProgramWithBinary(ctx(), 1, &device, &len, (const unsigned char**) &binary, &status, &ret);
diff --git a/src/backend/opencl/runners/OclRxJitRunner.h b/src/backend/opencl/runners/OclRxJitRunner.h
index 9e43363c3..2dca1c1b0 100644
--- a/src/backend/opencl/runners/OclRxJitRunner.h
+++ b/src/backend/opencl/runners/OclRxJitRunner.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/runners/OclRxVmRunner.cpp b/src/backend/opencl/runners/OclRxVmRunner.cpp
index 3a30d5610..f0737dd78 100644
--- a/src/backend/opencl/runners/OclRxVmRunner.cpp
+++ b/src/backend/opencl/runners/OclRxVmRunner.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,7 +17,6 @@
  */
 
 #include "backend/opencl/runners/OclRxVmRunner.h"
-
 #include "backend/opencl/kernels/rx/Blake2bHashRegistersKernel.h"
 #include "backend/opencl/kernels/rx/ExecuteVmKernel.h"
 #include "backend/opencl/kernels/rx/HashAesKernel.h"
@@ -76,7 +69,7 @@ void xmrig::OclRxVmRunner::build()
 
 void xmrig::OclRxVmRunner::execute(uint32_t iteration)
 {
-    const uint32_t bfactor        = std::min(data().thread.bfactor(), 8u);
+    const uint32_t bfactor        = std::min(data().thread.bfactor(), 8U);
     const uint32_t num_iterations = RxAlgo::programIterations(m_algorithm) >> bfactor;
 
     m_init_vm->enqueue(m_queue, m_intensity, iteration);
diff --git a/src/backend/opencl/runners/OclRxVmRunner.h b/src/backend/opencl/runners/OclRxVmRunner.h
index 8d044e744..fee59ee67 100644
--- a/src/backend/opencl/runners/OclRxVmRunner.h
+++ b/src/backend/opencl/runners/OclRxVmRunner.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/runners/tools/OclCnR.cpp b/src/backend/opencl/runners/tools/OclCnR.cpp
index c27a58f03..5b3e55004 100644
--- a/src/backend/opencl/runners/tools/OclCnR.cpp
+++ b/src/backend/opencl/runners/tools/OclCnR.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,7 +17,6 @@
  */
 
 #include "backend/opencl/runners/tools/OclCnR.h"
-
 #include "backend/opencl/cl/cn/cryptonight_r_cl.h"
 #include "backend/opencl/interfaces/IOclRunner.h"
 #include "backend/opencl/OclCache.h"
@@ -62,7 +55,7 @@ public:
     inline bool isExpired(uint64_t offset) const                                    { return m_offset + OclCnR::kHeightChunkSize < offset; }
     inline bool match(const Algorithm &algo, uint64_t offset, uint32_t index) const { return m_algo == algo && m_offset == offset && m_index == index; }
     inline bool match(const IOclRunner &runner, uint64_t offset) const              { return match(runner.algorithm(), offset, runner.deviceIndex()); }
-    inline void release()                                                           { OclLib::release(program); }
+    inline void release() const                                                     { OclLib::release(program); }
 
     cl_program program;
 
@@ -165,7 +158,7 @@ public:
             return program;
         }
 
-        cl_int ret;
+        cl_int ret = 0;
         const std::string source = getSource(offset);
         cl_device_id device      = runner.data().device.id();
         const char *s            = source.c_str();
@@ -190,7 +183,7 @@ public:
     }
 
 private:
-    std::string getCode(const V4_Instruction *code, int code_size) const
+    static std::string getCode(const V4_Instruction *code, int code_size)
     {
         std::stringstream s;
 
@@ -231,7 +224,7 @@ private:
     }
 
 
-    std::string getSource(uint64_t offset) const
+    static std::string getSource(uint64_t offset)
     {
         std::string source(cryptonight_r_defines_cl);
 
diff --git a/src/backend/opencl/runners/tools/OclKawPow.cpp b/src/backend/opencl/runners/tools/OclKawPow.cpp
index 15feeb80c..40b2b9795 100644
--- a/src/backend/opencl/runners/tools/OclKawPow.cpp
+++ b/src/backend/opencl/runners/tools/OclKawPow.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -66,7 +60,7 @@ public:
     inline bool isExpired(uint64_t period) const                                                       { return m_period + 1 < period; }
     inline bool match(const Algorithm &algo, uint64_t period, uint32_t worksize, uint32_t index) const { return m_algo == algo && m_period == period && m_worksize == worksize && m_index == index; }
     inline bool match(const IOclRunner &runner, uint64_t period, uint32_t worksize) const              { return match(runner.algorithm(), period, worksize, runner.deviceIndex()); }
-    inline void release()                                                                              { OclLib::release(kernel); OclLib::release(program); }
+    inline void release() const                                                                        { OclLib::release(kernel); OclLib::release(program); }
 
     cl_program program;
     cl_kernel kernel;
@@ -173,7 +167,7 @@ public:
             return kernel;
         }
 
-        cl_int ret;
+        cl_int ret = 0;
         const std::string source = getSource(period);
         cl_device_id device      = runner.data().device.id();
         const char *s            = source.c_str();
@@ -224,7 +218,7 @@ private:
     } kiss99_t;
 
 
-    std::string getSource(uint64_t prog_seed) const
+    static std::string getSource(uint64_t prog_seed)
     {
         std::stringstream ret;
 
@@ -252,7 +246,7 @@ private:
         }
 
         for (int i = KPHash::REGS - 1; i > 0; i--) {
-            int j;
+            int j = 0;
             j = rnd() % (i + 1);
             std::swap(mix_seq_dst[i], mix_seq_dst[j]);
             j = rnd() % (i + 1);
@@ -277,7 +271,11 @@ private:
                 int src_rnd = rnd() % ((KPHash::REGS - 1) * KPHash::REGS);
                 int src1 = src_rnd % KPHash::REGS; // 0 <= src1 < KPHash::REGS
                 int src2 = src_rnd / KPHash::REGS; // 0 <= src2 < KPHash::REGS - 1
-                if (src2 >= src1) ++src2; // src2 is now any reg other than src1
+
+                if (src2 >= src1) {
+                    ++src2; // src2 is now any reg other than src1
+                }
+
                 std::string src1_str = "mix[" + std::to_string(src1) + "]";
                 std::string src2_str = "mix[" + std::to_string(src2) + "]";
                 uint32_t r1 = rnd();
diff --git a/src/backend/opencl/runners/tools/OclKawPow.h b/src/backend/opencl/runners/tools/OclKawPow.h
index 8d072680e..cf2fc949e 100644
--- a/src/backend/opencl/runners/tools/OclKawPow.h
+++ b/src/backend/opencl/runners/tools/OclKawPow.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/runners/tools/OclSharedData.cpp b/src/backend/opencl/runners/tools/OclSharedData.cpp
index 38e6a6769..57b360556 100644
--- a/src/backend/opencl/runners/tools/OclSharedData.cpp
+++ b/src/backend/opencl/runners/tools/OclSharedData.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/opencl/runners/tools/OclSharedData.h"
 #include "backend/opencl/wrappers/OclLib.h"
 #include "base/io/log/Log.h"
@@ -62,7 +55,7 @@ cl_mem xmrig::OclSharedData::createBuffer(cl_context context, size_t size, size_
 }
 
 
-uint64_t xmrig::OclSharedData::adjustDelay(size_t id)
+uint64_t xmrig::OclSharedData::adjustDelay(size_t /*id*/)
 {
     if (m_threads < 2) {
         return 0;
@@ -103,7 +96,7 @@ uint64_t xmrig::OclSharedData::adjustDelay(size_t id)
 }
 
 
-uint64_t xmrig::OclSharedData::resumeDelay(size_t id)
+uint64_t xmrig::OclSharedData::resumeDelay(size_t /*id*/)
 {
     if (m_threads < 2) {
         return 0;
@@ -187,7 +180,7 @@ void xmrig::OclSharedData::createDataset(cl_context ctx, const Job &job, bool ho
         return;
     }
 
-    cl_int ret;
+    cl_int ret = 0;
 
     if (host) {
         auto dataset = Rx::dataset(job, 0);
diff --git a/src/backend/opencl/runners/tools/OclSharedData.h b/src/backend/opencl/runners/tools/OclSharedData.h
index 393ed3b8a..7d55f2428 100644
--- a/src/backend/opencl/runners/tools/OclSharedData.h
+++ b/src/backend/opencl/runners/tools/OclSharedData.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/wrappers/AdlLib.cpp b/src/backend/opencl/wrappers/AdlLib.cpp
index d3b3bef4c..9df91524b 100644
--- a/src/backend/opencl/wrappers/AdlLib.cpp
+++ b/src/backend/opencl/wrappers/AdlLib.cpp
@@ -1,7 +1,7 @@
 /* XMRig
- * Copyright 2008-2018 Advanced Micro Devices, Inc.
- * Copyright 2018-2020 SChernykh                    <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig                        <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2008-2018 Advanced Micro Devices, Inc.
+ * Copyright (c) 2018-2021 SChernykh                    <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig                        <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <stdexcept>
 #include <uv.h>
 
@@ -81,7 +80,7 @@ bool AdlLib::m_ready               = false;
 
 static void * __stdcall ADL_Main_Memory_Alloc(int iSize)
 {
-    return malloc(iSize);
+    return malloc(iSize); // NOLINT(cppcoreguidelines-no-malloc, hicpp-no-malloc)
 }
 
 
@@ -191,7 +190,9 @@ AdlHealth xmrig::AdlLib::health(const OclDevice &device)
         return {};
     }
 
-    int supported, enabled, version;
+    int supported   = 0;
+    int enabled     = 0;
+    int version     = 0;
     AdlHealth health;
 
     for (const auto &adapter : adapters) {
diff --git a/src/backend/opencl/wrappers/AdlLib.h b/src/backend/opencl/wrappers/AdlLib.h
index 70cb6d4f5..efdf1a6a6 100644
--- a/src/backend/opencl/wrappers/AdlLib.h
+++ b/src/backend/opencl/wrappers/AdlLib.h
@@ -1,7 +1,7 @@
 /* XMRig
- * Copyright 2008-2018 Advanced Micro Devices, Inc.
- * Copyright 2018-2020 SChernykh                    <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig                        <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2008-2018 Advanced Micro Devices, Inc.
+ * Copyright (c) 2018-2021 SChernykh                    <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig                        <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/wrappers/OclDevice.cpp b/src/backend/opencl/wrappers/OclDevice.cpp
index 87bf21b0f..a3fcdd03b 100644
--- a/src/backend/opencl/wrappers/OclDevice.cpp
+++ b/src/backend/opencl/wrappers/OclDevice.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "backend/opencl/wrappers/OclDevice.h"
 #include "3rdparty/rapidjson/document.h"
 #include "backend/opencl/OclGenerator.h"
@@ -39,6 +32,7 @@
 #include <algorithm>
 
 
+// NOLINTNEXTLINE(modernize-use-using)
 typedef union
 {
     struct { cl_uint type; cl_uint data[5]; } raw;
diff --git a/src/backend/opencl/wrappers/OclDevice.h b/src/backend/opencl/wrappers/OclDevice.h
index ebe96d5ae..b698ad3a5 100644
--- a/src/backend/opencl/wrappers/OclDevice.h
+++ b/src/backend/opencl/wrappers/OclDevice.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/backend/opencl/wrappers/OclLib.cpp b/src/backend/opencl/wrappers/OclLib.cpp
index 4794f36bf..c970e70c0 100644
--- a/src/backend/opencl/wrappers/OclLib.cpp
+++ b/src/backend/opencl/wrappers/OclLib.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <thread>
 #include <stdexcept>
 #include <uv.h>
@@ -273,7 +266,7 @@ xmrig::String xmrig::OclLib::defaultLoader()
 
 cl_command_queue xmrig::OclLib::createCommandQueue(cl_context context, cl_device_id device, cl_int *errcode_ret) noexcept
 {
-    cl_command_queue result;
+    cl_command_queue result = nullptr;
 
 #   if defined(CL_VERSION_2_0)
     if (pCreateCommandQueueWithProperties) {
@@ -300,7 +293,7 @@ cl_command_queue xmrig::OclLib::createCommandQueue(cl_context context, cl_device
 
 cl_command_queue xmrig::OclLib::createCommandQueue(cl_context context, cl_device_id device)
 {
-    cl_int ret;
+    cl_int ret = 0;
     cl_command_queue queue = createCommandQueue(context, device, &ret);
     if (ret != CL_SUCCESS) {
         throw std::runtime_error(OclError::toString(ret));
@@ -327,7 +320,7 @@ cl_context xmrig::OclLib::createContext(const cl_context_properties *properties,
 
 cl_context xmrig::OclLib::createContext(const std::vector<cl_device_id> &ids)
 {
-    cl_int ret;
+    cl_int ret = 0;
     return createContext(nullptr, static_cast<cl_uint>(ids.size()), ids.data(), nullptr, nullptr, &ret);
 }
 
@@ -615,7 +608,7 @@ cl_kernel xmrig::OclLib::createKernel(cl_program program, const char *kernel_nam
 
 cl_kernel xmrig::OclLib::createKernel(cl_program program, const char *kernel_name)
 {
-    cl_int ret;
+    cl_int ret = 0;
     cl_kernel kernel = createKernel(program, kernel_name, &ret);
     if (ret != CL_SUCCESS) {
         throw std::runtime_error(OclError::toString(ret));
@@ -627,7 +620,7 @@ cl_kernel xmrig::OclLib::createKernel(cl_program program, const char *kernel_nam
 
 cl_mem xmrig::OclLib::createBuffer(cl_context context, cl_mem_flags flags, size_t size, void *host_ptr)
 {
-    cl_int ret;
+    cl_int ret = 0;
     cl_mem mem = createBuffer(context, flags, size, host_ptr, &ret);
     if (ret != CL_SUCCESS) {
         throw std::runtime_error(OclError::toString(ret));
@@ -671,7 +664,7 @@ cl_mem xmrig::OclLib::createSubBuffer(cl_mem buffer, cl_mem_flags flags, size_t
 
 cl_mem xmrig::OclLib::createSubBuffer(cl_mem buffer, cl_mem_flags flags, size_t offset, size_t size)
 {
-    cl_int ret;
+    cl_int ret = 0;
     cl_mem mem = createSubBuffer(buffer, flags, offset, size, &ret);
     if (ret != CL_SUCCESS) {
         throw std::runtime_error(OclError::toString(ret));
@@ -737,8 +730,8 @@ cl_program xmrig::OclLib::retain(cl_program program) noexcept
 
 cl_uint xmrig::OclLib::getNumPlatforms() noexcept
 {
-    cl_uint count = 0;
-    cl_int ret;
+    cl_uint count   = 0;
+    cl_int ret      = 0;
 
     if ((ret = OclLib::getPlatformIDs(0, nullptr, &count)) != CL_SUCCESS) {
         LOG_ERR("Error %s when calling clGetPlatformIDs for number of platforms.", OclError::toString(ret));
diff --git a/src/backend/opencl/wrappers/OclLib.h b/src/backend/opencl/wrappers/OclLib.h
index b4db1d8d8..c2f4194e1 100644
--- a/src/backend/opencl/wrappers/OclLib.h
+++ b/src/backend/opencl/wrappers/OclLib.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/api/Api.cpp b/src/base/api/Api.cpp
index 53e0ebb11..54666efda 100644
--- a/src/base/api/Api.cpp
+++ b/src/base/api/Api.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <uv.h>
 
 
@@ -198,7 +197,7 @@ void xmrig::Api::genId(const String &id)
         return;
     }
 
-    uv_interface_address_t *interfaces;
+    uv_interface_address_t *interfaces = nullptr;
     int count = 0;
 
     if (uv_interface_addresses(&interfaces, &count) < 0) {
diff --git a/src/base/api/Httpd.cpp b/src/base/api/Httpd.cpp
index 817da9207..d2d3db4a7 100644
--- a/src/base/api/Httpd.cpp
+++ b/src/base/api/Httpd.cpp
@@ -95,6 +95,7 @@ bool xmrig::Httpd::start()
     m_port = static_cast<uint16_t>(rc);
 
 #   ifdef _WIN32
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast, performance-no-int-to-ptr)
     HRSRC src = FindResource(nullptr, MAKEINTRESOURCE(1), RT_ICON);
     if (src != nullptr) {
         HGLOBAL res = LoadResource(nullptr, src);
diff --git a/src/base/api/requests/HttpApiRequest.cpp b/src/base/api/requests/HttpApiRequest.cpp
index 60d42b649..de43f752e 100644
--- a/src/base/api/requests/HttpApiRequest.cpp
+++ b/src/base/api/requests/HttpApiRequest.cpp
@@ -45,6 +45,9 @@ static inline const char *rpcError(int code) {
 
     case IApiRequest::RPC_INVALID_PARAMS:
         return "Invalid params";
+
+    default:
+        break;
     }
 
     if (code >= 400 && code < 600) {
diff --git a/src/base/io/Console.cpp b/src/base/io/Console.cpp
index 5af7e4a43..30ff34a34 100644
--- a/src/base/io/Console.cpp
+++ b/src/base/io/Console.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/io/Console.h"
 #include "base/kernel/interfaces/IConsoleListener.h"
 #include "base/tools/Handle.h"
@@ -50,7 +49,7 @@ xmrig::Console::~Console()
 }
 
 
-bool xmrig::Console::isSupported() const
+bool xmrig::Console::isSupported()
 {
     const uv_handle_type type = uv_guess_handle(0);
     return type == UV_TTY || type == UV_NAMED_PIPE;
diff --git a/src/base/io/Console.h b/src/base/io/Console.h
index 65523b94c..129bf8173 100644
--- a/src/base/io/Console.h
+++ b/src/base/io/Console.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ public:
     ~Console();
 
 private:
-    bool isSupported() const;
+    static bool isSupported();
 
     static void onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf);
     static void onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
diff --git a/src/base/io/Env.cpp b/src/base/io/Env.cpp
index 6abb88070..2a0d4ad7c 100644
--- a/src/base/io/Env.cpp
+++ b/src/base/io/Env.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/io/Env.h"
 #include "base/kernel/Process.h"
 #include "version.h"
@@ -61,7 +60,7 @@ static void createVariables()
     variables.insert({ "XMRIG_DATA_DIR", Process::location(Process::DataLocation) });
 
     String hostname = "HOSTNAME";
-    if (!getenv(hostname)) {
+    if (!getenv(hostname)) { // NOLINT(concurrency-mt-unsafe)
         variables.insert({ std::move(hostname), Env::hostname() });
     }
 }
@@ -137,7 +136,7 @@ xmrig::String xmrig::Env::get(const String &name, const std::map<String, String>
     }
 #   endif
 
-    return static_cast<const char *>(getenv(name));
+    return static_cast<const char *>(getenv(name)); // NOLINT(concurrency-mt-unsafe)
 }
 
 
diff --git a/src/base/io/json/Json.cpp b/src/base/io/json/Json.cpp
index 99a087e9c..720f75152 100644
--- a/src/base/io/json/Json.cpp
+++ b/src/base/io/json/Json.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/io/json/Json.h"
 #include "3rdparty/rapidjson/document.h"
 
@@ -30,7 +29,7 @@ namespace xmrig {
 
 static const rapidjson::Value kNullValue;
 
-}
+} // namespace xmrig
 
 
 bool xmrig::Json::getBool(const rapidjson::Value &obj, const char *key, bool defaultValue)
diff --git a/src/base/io/json/Json.h b/src/base/io/json/Json.h
index e5d0fcc6d..36ee4c802 100644
--- a/src/base/io/json/Json.h
+++ b/src/base/io/json/Json.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/io/json/JsonChain.cpp b/src/base/io/json/JsonChain.cpp
index 0a4a1857d..9f9204648 100644
--- a/src/base/io/json/JsonChain.cpp
+++ b/src/base/io/json/JsonChain.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/io/json/JsonChain.h"
 #include "3rdparty/rapidjson/error/en.h"
 #include "base/io/json/Json.h"
@@ -27,7 +26,7 @@ namespace xmrig {
 
 static const rapidjson::Value kNullValue;
 
-}
+} // namespace xmrig
 
 
 xmrig::JsonChain::JsonChain() = default;
@@ -58,8 +57,8 @@ bool xmrig::JsonChain::addFile(const char *fileName)
     if (doc.HasParseError()) {
         const size_t offset = doc.GetErrorOffset();
 
-        size_t line;
-        size_t pos;
+        size_t line = 0;
+        size_t pos  = 0;
         std::vector<std::string> s;
 
         if (Json::convertOffset(fileName, offset, line, pos, s)) {
diff --git a/src/base/io/log/Log.cpp b/src/base/io/log/Log.cpp
index e144858a0..891b93b67 100644
--- a/src/base/io/log/Log.cpp
+++ b/src/base/io/log/Log.cpp
@@ -1,7 +1,7 @@
 /* XMRig
  * Copyright (c) 2019      Spudz76     <https://github.com/Spudz76>
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #ifdef WIN32
 #   include <winsock2.h>
 #   include <windows.h>
@@ -103,7 +102,7 @@ public:
         endl(size);
 
         std::string txt(m_buf);
-        size_t i;
+        size_t i = 0;
         while ((i = txt.find(CSI)) != std::string::npos) {
             txt.erase(i, txt.find('m', i) - i + 1);
         }
@@ -232,7 +231,7 @@ void xmrig::Log::print(const char *fmt, ...)
         return;
     }
 
-    va_list args;
+    va_list args = nullptr;
     va_start(args, fmt);
 
     d->print(NONE, fmt, args);
@@ -247,7 +246,7 @@ void xmrig::Log::print(Level level, const char *fmt, ...)
         return;
     }
 
-    va_list args;
+    va_list args = nullptr;
     va_start(args, fmt);
 
     d->print(level, fmt, args);
diff --git a/src/base/io/log/Log.h b/src/base/io/log/Log.h
index 6da2e8927..d4c0a3197 100644
--- a/src/base/io/log/Log.h
+++ b/src/base/io/log/Log.h
@@ -1,7 +1,7 @@
 /* XMRig
  * Copyright (c) 2019      Spudz76     <https://github.com/Spudz76>
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/io/log/backends/ConsoleLog.cpp b/src/base/io/log/backends/ConsoleLog.cpp
index cc3081fdd..a18fe7d9f 100644
--- a/src/base/io/log/backends/ConsoleLog.cpp
+++ b/src/base/io/log/backends/ConsoleLog.cpp
@@ -1,7 +1,7 @@
 /* XMRig
  * Copyright (c) 2019      Spudz76     <https://github.com/Spudz76>
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/io/log/backends/ConsoleLog.h"
 #include "base/io/log/Log.h"
 #include "base/kernel/config/Title.h"
@@ -47,7 +46,7 @@ xmrig::ConsoleLog::ConsoleLog(const Title &title)
     m_stream = reinterpret_cast<uv_stream_t*>(m_tty);
 
     HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
-    if (handle != INVALID_HANDLE_VALUE) {
+    if (handle != INVALID_HANDLE_VALUE) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast, performance-no-int-to-ptr)
         DWORD mode = 0;
         if (GetConsoleMode(handle, &mode)) {
            mode &= ~ENABLE_QUICK_EDIT_MODE;
@@ -91,7 +90,7 @@ void xmrig::ConsoleLog::print(uint64_t, int, const char *line, size_t, size_t si
 }
 
 
-bool xmrig::ConsoleLog::isSupported() const
+bool xmrig::ConsoleLog::isSupported()
 {
     const uv_handle_type type = uv_guess_handle(1);
     return type == UV_TTY || type == UV_NAMED_PIPE;
diff --git a/src/base/io/log/backends/ConsoleLog.h b/src/base/io/log/backends/ConsoleLog.h
index 019177630..2d248a921 100644
--- a/src/base/io/log/backends/ConsoleLog.h
+++ b/src/base/io/log/backends/ConsoleLog.h
@@ -1,7 +1,7 @@
 /* XMRig
  * Copyright (c) 2019      Spudz76     <https://github.com/Spudz76>
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -47,7 +47,7 @@ protected:
     void print(uint64_t timestamp, int level, const char *line, size_t offset, size_t size, bool colors) override;
 
 private:
-    bool isSupported() const;
+    static bool isSupported();
 
     uv_tty_t *m_tty = nullptr;
 
diff --git a/src/base/kernel/Base.cpp b/src/base/kernel/Base.cpp
index 698dc10cf..0e0121862 100644
--- a/src/base/kernel/Base.cpp
+++ b/src/base/kernel/Base.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <cassert>
 #include <memory>
 
@@ -76,7 +69,7 @@ public:
     XMRIG_DISABLE_COPY_MOVE_DEFAULT(BasePrivate)
 
 
-    inline BasePrivate(Process *process)
+    inline explicit BasePrivate(Process *process)
     {
         Log::init();
 
@@ -97,7 +90,7 @@ public:
     }
 
 
-    inline bool read(const JsonChain &chain, std::unique_ptr<Config> &config)
+    inline static bool read(const JsonChain &chain, std::unique_ptr<Config> &config)
     {
         config = std::unique_ptr<Config>(new Config());
 
@@ -125,7 +118,7 @@ public:
 
 
 private:
-    inline Config *load(Process *process)
+    inline static Config *load(Process *process)
     {
         JsonChain chain;
         ConfigTransform transform;
diff --git a/src/base/kernel/Base.h b/src/base/kernel/Base.h
index f113c0f70..53eaaea3b 100644
--- a/src/base/kernel/Base.h
+++ b/src/base/kernel/Base.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/kernel/Entry.cpp b/src/base/kernel/Entry.cpp
index a50cb6342..c257ad243 100644
--- a/src/base/kernel/Entry.cpp
+++ b/src/base/kernel/Entry.cpp
@@ -109,7 +109,7 @@ static int exportTopology(const Process &)
 {
     const String path = Process::location(Process::ExeLocation, "topology.xml");
 
-    hwloc_topology_t topology;
+    hwloc_topology_t topology = nullptr;
     hwloc_topology_init(&topology);
     hwloc_topology_load(topology);
 
diff --git a/src/base/kernel/Platform_win.cpp b/src/base/kernel/Platform_win.cpp
index 75f810419..76d81ae56 100644
--- a/src/base/kernel/Platform_win.cpp
+++ b/src/base/kernel/Platform_win.cpp
@@ -30,7 +30,7 @@
 
 static inline OSVERSIONINFOEX winOsVersion()
 {
-    typedef NTSTATUS (NTAPI *RtlGetVersionFunction)(LPOSVERSIONINFO);
+    typedef NTSTATUS (NTAPI *RtlGetVersionFunction)(LPOSVERSIONINFO); // NOLINT(modernize-use-using)
     OSVERSIONINFOEX result = { sizeof(OSVERSIONINFOEX), 0, 0, 0, 0, {'\0'}, 0, 0, 0, 0, 0};
 
     HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
@@ -61,9 +61,9 @@ char *xmrig::Platform::createUserAgent()
 #   endif
 
 #   ifdef __GNUC__
-    length += snprintf(buf + length, max - length, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
+    snprintf(buf + length, max - length, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
 #   elif _MSC_VER
-    length += snprintf(buf + length, max - length, " msvc/%d", MSVC_VERSION);
+    snprintf(buf + length, max - length, " msvc/%d", MSVC_VERSION);
 #   endif
 
     return buf;
diff --git a/src/base/kernel/Process.cpp b/src/base/kernel/Process.cpp
index 6f63b6476..173fc2aa3 100644
--- a/src/base/kernel/Process.cpp
+++ b/src/base/kernel/Process.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <ctime>
 #include <string>
 #include <uv.h>
@@ -80,7 +79,7 @@ static std::string getPath(Process::Location location)
             return {};
         }
 
-        const auto path = std::string(pathBuf, size);
+        auto path       = std::string(pathBuf, size);
         const auto pos  = path.rfind(*XMRIG_DIR_SEPARATOR);
 
         if (pos != std::string::npos) {
diff --git a/src/base/kernel/Process.h b/src/base/kernel/Process.h
index 754e7a567..9af498f59 100644
--- a/src/base/kernel/Process.h
+++ b/src/base/kernel/Process.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/kernel/config/BaseConfig.h b/src/base/kernel/config/BaseConfig.h
index 37d4641f0..fae6d981c 100644
--- a/src/base/kernel/config/BaseConfig.h
+++ b/src/base/kernel/config/BaseConfig.h
@@ -86,7 +86,7 @@ public:
     bool read(const IJsonReader &reader, const char *fileName) override;
     bool save() override;
 
-    void printVersions();
+    static void printVersions();
 
 protected:
     bool m_autoSave         = true;
@@ -110,7 +110,7 @@ protected:
 #   endif
 
 private:
-    void setVerbose(const rapidjson::Value &value);
+    static void setVerbose(const rapidjson::Value &value);
 };
 
 
diff --git a/src/base/kernel/config/BaseTransform.cpp b/src/base/kernel/config/BaseTransform.cpp
index 6d4f38d01..c924dd59a 100644
--- a/src/base/kernel/config/BaseTransform.cpp
+++ b/src/base/kernel/config/BaseTransform.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <cstdio>
 
 
@@ -48,14 +47,14 @@ void xmrig::BaseTransform::load(JsonChain &chain, Process *process, IConfigTrans
 {
     using namespace rapidjson;
 
-    int key;
+    int key     = 0;
     int argc    = process->arguments().argc();
     char **argv = process->arguments().argv();
 
     Document doc(kObjectType);
 
     while (true) {
-        key = getopt_long(argc, argv, short_options, options, nullptr);
+        key = getopt_long(argc, argv, short_options, options, nullptr); // NOLINT(concurrency-mt-unsafe)
         if (key < 0) {
             break;
         }
diff --git a/src/base/net/dns/DnsConfig.cpp b/src/base/net/dns/DnsConfig.cpp
index c2446c2a6..f9ec7e28b 100644
--- a/src/base/net/dns/DnsConfig.cpp
+++ b/src/base/net/dns/DnsConfig.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/dns/DnsConfig.h"
 #include "3rdparty/rapidjson/document.h"
 #include "base/io/json/Json.h"
diff --git a/src/base/net/dns/DnsConfig.h b/src/base/net/dns/DnsConfig.h
index d42c8ad8b..605e837be 100644
--- a/src/base/net/dns/DnsConfig.h
+++ b/src/base/net/dns/DnsConfig.h
@@ -34,7 +34,7 @@ public:
     static const char *kTTL;
 
     DnsConfig() = default;
-    DnsConfig(const rapidjson::Value &object);
+    DnsConfig(const rapidjson::Value &value);
 
     inline bool isIPv6() const  { return m_ipv6; }
     inline uint32_t ttl() const { return m_ttl * 1000U; }
diff --git a/src/base/net/dns/DnsRecords.cpp b/src/base/net/dns/DnsRecords.cpp
index fb232f276..072f96729 100644
--- a/src/base/net/dns/DnsRecords.cpp
+++ b/src/base/net/dns/DnsRecords.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <uv.h>
 
 
@@ -36,11 +35,11 @@ const xmrig::DnsRecord &xmrig::DnsRecords::get(DnsRecord::Type prefered) const
     const size_t ipv6 = m_ipv6.size();
 
     if (ipv6 && (prefered == DnsRecord::AAAA || Dns::config().isIPv6() || !ipv4)) {
-        return m_ipv6[ipv6 == 1 ? 0 : static_cast<size_t>(rand()) % ipv6];
+        return m_ipv6[ipv6 == 1 ? 0 : static_cast<size_t>(rand()) % ipv6]; // NOLINT(concurrency-mt-unsafe, cert-msc30-c, cert-msc50-cpp)
     }
 
     if (ipv4) {
-        return m_ipv4[ipv4 == 1 ? 0 : static_cast<size_t>(rand()) % ipv4];
+        return m_ipv4[ipv4 == 1 ? 0 : static_cast<size_t>(rand()) % ipv4]; // NOLINT(concurrency-mt-unsafe, cert-msc30-c, cert-msc50-cpp)
     }
 
     return defaultRecord;
diff --git a/src/base/net/http/Fetch.cpp b/src/base/net/http/Fetch.cpp
index f0be4dfd4..d387a8cc5 100644
--- a/src/base/net/http/Fetch.cpp
+++ b/src/base/net/http/Fetch.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/http/Fetch.h"
 #include "3rdparty/rapidjson/document.h"
 #include "3rdparty/rapidjson/stringbuffer.h"
@@ -106,7 +105,7 @@ void xmrig::fetch(const char *tag, FetchRequest &&req, const std::weak_ptr<IHttp
     }
 #   endif
 
-    HttpClient *client;
+    HttpClient *client = nullptr;
 #   ifdef XMRIG_FEATURE_TLS
     if (req.tls) {
         client = new HttpsClient(tag, std::move(req), listener);
diff --git a/src/base/net/http/Http.cpp b/src/base/net/http/Http.cpp
index 0aef85dc8..b6b1e8580 100644
--- a/src/base/net/http/Http.cpp
+++ b/src/base/net/http/Http.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/http/Http.h"
 #include "3rdparty/rapidjson/document.h"
 #include "base/io/json/Json.h"
@@ -39,7 +32,7 @@ const char *Http::kRestricted = "restricted";
 const char *Http::kToken      = "access-token";
 
 
-}
+} // namespace xmrig
 
 
 xmrig::Http::Http() :
diff --git a/src/base/net/http/Http.h b/src/base/net/http/Http.h
index 7ee179cd0..f7a59bec1 100644
--- a/src/base/net/http/Http.h
+++ b/src/base/net/http/Http.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #ifndef XMRIG_HTTP_H
 #define XMRIG_HTTP_H
 
diff --git a/src/base/net/https/HttpsClient.cpp b/src/base/net/https/HttpsClient.cpp
index 8b4149170..e901b1e0c 100644
--- a/src/base/net/https/HttpsClient.cpp
+++ b/src/base/net/https/HttpsClient.cpp
@@ -170,7 +170,7 @@ bool xmrig::HttpsClient::verifyFingerprint(X509 *cert)
     }
 
     unsigned char md[EVP_MAX_MD_SIZE];
-    unsigned int dlen;
+    unsigned int dlen = 0;
 
     if (X509_digest(cert, digest, md, &dlen) != 1) {
         return false;
@@ -189,7 +189,7 @@ void xmrig::HttpsClient::flush(bool close)
     }
 
     char *data        = nullptr;
-    const size_t size = BIO_get_mem_data(m_write, &data);
+    const size_t size = BIO_get_mem_data(m_write, &data); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
     std::string body(data, size);
 
     (void) BIO_reset(m_write);
diff --git a/src/base/net/https/HttpsContext.cpp b/src/base/net/https/HttpsContext.cpp
index 3b0ff79af..c45bf0f30 100644
--- a/src/base/net/https/HttpsContext.cpp
+++ b/src/base/net/https/HttpsContext.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/https/HttpsContext.h"
 #include "3rdparty/llhttp/llhttp.h"
 #include "base/net/tls/TlsContext.h"
@@ -61,7 +60,7 @@ bool xmrig::HttpsContext::write(BIO *bio)
     }
 
     char *data        = nullptr;
-    const size_t size = BIO_get_mem_data(bio, &data);
+    const size_t size = BIO_get_mem_data(bio, &data); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
     std::string body(data, size);
 
     (void) BIO_reset(bio);
diff --git a/src/base/net/https/HttpsServer.cpp b/src/base/net/https/HttpsServer.cpp
index 3a6fb0801..782c47443 100644
--- a/src/base/net/https/HttpsServer.cpp
+++ b/src/base/net/https/HttpsServer.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <uv.h>
 
 
@@ -53,7 +52,7 @@ void xmrig::HttpsServer::onConnection(uv_stream_t *stream, uint16_t)
     auto ctx = new HttpsContext(m_tls, m_listener);
     uv_accept(stream, ctx->stream());
 
-    uv_read_start(ctx->stream(), NetBuffer::onAlloc, onRead);
+    uv_read_start(ctx->stream(), NetBuffer::onAlloc, onRead); // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
 }
 
 
diff --git a/src/base/net/https/HttpsServer.h b/src/base/net/https/HttpsServer.h
index 2b5ecd002..ad21162f3 100644
--- a/src/base/net/https/HttpsServer.h
+++ b/src/base/net/https/HttpsServer.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #ifndef XMRIG_HTTPSSERVER_H
 #define XMRIG_HTTPSSERVER_H
 
diff --git a/src/base/net/stratum/AutoClient.cpp b/src/base/net/stratum/AutoClient.cpp
index 4a82b2bf8..e3896b584 100644
--- a/src/base/net/stratum/AutoClient.cpp
+++ b/src/base/net/stratum/AutoClient.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/stratum/AutoClient.h"
 #include "3rdparty/rapidjson/document.h"
 #include "base/io/json/Json.h"
@@ -31,7 +30,7 @@ xmrig::AutoClient::AutoClient(int id, const char *agent, IClientListener *listen
 bool xmrig::AutoClient::handleResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error)
 {
     if (m_mode == DEFAULT_MODE) {
-        return Client::handleResponse(id, result, error);
+        return Client::handleResponse(id, result, error); // NOLINT(bugprone-parent-virtual-call)
     }
 
     return EthStratumClient::handleResponse(id, result, error);
@@ -73,7 +72,7 @@ bool xmrig::AutoClient::parseLogin(const rapidjson::Value &result, int *code)
 int64_t xmrig::AutoClient::submit(const JobResult &result)
 {
     if (m_mode == DEFAULT_MODE) {
-        return Client::submit(result);
+        return Client::submit(result); // NOLINT(bugprone-parent-virtual-call)
     }
 
     return EthStratumClient::submit(result);
@@ -83,7 +82,7 @@ int64_t xmrig::AutoClient::submit(const JobResult &result)
 void xmrig::AutoClient::parseNotification(const char *method, const rapidjson::Value &params, const rapidjson::Value &error)
 {
     if (m_mode == DEFAULT_MODE) {
-        return Client::parseNotification(method, params, error);
+        return Client::parseNotification(method, params, error); // NOLINT(bugprone-parent-virtual-call)
     }
 
     return EthStratumClient::parseNotification(method, params, error);
diff --git a/src/base/net/stratum/AutoClient.h b/src/base/net/stratum/AutoClient.h
index a97700fbc..c74d51911 100644
--- a/src/base/net/stratum/AutoClient.h
+++ b/src/base/net/stratum/AutoClient.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp
index 46c9ac2ff..db6ceca38 100644
--- a/src/base/net/stratum/Client.cpp
+++ b/src/base/net/stratum/Client.cpp
@@ -349,32 +349,6 @@ bool xmrig::Client::close()
 }
 
 
-bool xmrig::Client::isCriticalError(const char *message)
-{
-    if (!message) {
-        return false;
-    }
-
-    if (strncasecmp(message, "Unauthenticated", 15) == 0) {
-        return true;
-    }
-
-    if (strncasecmp(message, "your IP is banned", 17) == 0) {
-        return true;
-    }
-
-    if (strncasecmp(message, "IP Address currently banned", 27) == 0) {
-        return true;
-    }
-
-    if (strncasecmp(message, "Invalid job id", 14) == 0) {
-        return true;
-    }
-
-    return false;
-}
-
-
 bool xmrig::Client::parseJob(const rapidjson::Value &params, int *code)
 {
     if (!params.IsObject()) {
@@ -465,7 +439,7 @@ bool xmrig::Client::send(BIO *bio)
 {
 #   ifdef XMRIG_FEATURE_TLS
     uv_buf_t buf;
-    buf.len = BIO_get_mem_data(bio, &buf.base);
+    buf.len = BIO_get_mem_data(bio, &buf.base); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
 
     if (buf.len == 0) {
         return true;
@@ -959,6 +933,32 @@ void xmrig::Client::startTimeout()
 }
 
 
+bool xmrig::Client::isCriticalError(const char *message)
+{
+    if (!message) {
+        return false;
+    }
+
+    if (strncasecmp(message, "Unauthenticated", 15) == 0) {
+        return true;
+    }
+
+    if (strncasecmp(message, "your IP is banned", 17) == 0) {
+        return true;
+    }
+
+    if (strncasecmp(message, "IP Address currently banned", 27) == 0) {
+        return true;
+    }
+
+    if (strncasecmp(message, "Invalid job id", 14) == 0) {
+        return true;
+    }
+
+    return false;
+}
+
+
 void xmrig::Client::onClose(uv_handle_t *handle)
 {
     auto client = getClient(handle->data);
diff --git a/src/base/net/stratum/Client.h b/src/base/net/stratum/Client.h
index a74be296d..e1f5d7562 100644
--- a/src/base/net/stratum/Client.h
+++ b/src/base/net/stratum/Client.h
@@ -96,7 +96,6 @@ private:
     class Socks5;
     class Tls;
 
-    bool isCriticalError(const char *message);
     bool parseJob(const rapidjson::Value &params, int *code);
     bool send(BIO *bio);
     bool verifyAlgorithm(const Algorithm &algorithm, const char *algo) const;
@@ -119,6 +118,7 @@ private:
     inline void setExtension(Extension ext, bool enable) noexcept   { m_extensions.set(ext, enable); }
     template<Extension ext> inline bool has() const noexcept        { return m_extensions.test(ext); }
 
+    static bool isCriticalError(const char *message);
     static void onClose(uv_handle_t *handle);
     static void onConnect(uv_connect_t *req, int status);
     static void onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp
index 50a3a2324..57a8d6f0f 100644
--- a/src/base/net/stratum/DaemonClient.cpp
+++ b/src/base/net/stratum/DaemonClient.cpp
@@ -73,7 +73,7 @@ static constexpr size_t kZMQGreetingSize1 = 11;
 static const char kZMQHandshake[] = "\4\x19\5READY\xbSocket-Type\0\0\0\3SUB";
 static const char kZMQSubscribe[] = "\0\x18\1json-minimal-chain_main";
 
-}
+} // namespace xmrig
 
 
 xmrig::DaemonClient::DaemonClient(int id, IClientListener *listener) :
@@ -823,9 +823,9 @@ void xmrig::DaemonClient::ZMQParse()
 
     size_t msg_size = 0;
 
-    char* data = m_ZMQRecvBuf.data();
+    char *data   = m_ZMQRecvBuf.data();
     size_t avail = m_ZMQRecvBuf.size();
-    bool more;
+    bool more    = false;
 
     do {
         if (avail < 1) {
diff --git a/src/base/net/stratum/EthStratumClient.cpp b/src/base/net/stratum/EthStratumClient.cpp
index 40c57f299..94d856351 100644
--- a/src/base/net/stratum/EthStratumClient.cpp
+++ b/src/base/net/stratum/EthStratumClient.cpp
@@ -272,7 +272,7 @@ void xmrig::EthStratumClient::setExtraNonce(const rapidjson::Value &nonce)
 }
 
 
-const char *xmrig::EthStratumClient::errorMessage(const rapidjson::Value &error) const
+const char *xmrig::EthStratumClient::errorMessage(const rapidjson::Value &error)
 {
     if (error.IsArray() && error.GetArray().Size() > 1) {
         auto &value = error.GetArray()[1];
diff --git a/src/base/net/stratum/EthStratumClient.h b/src/base/net/stratum/EthStratumClient.h
index d6b34fb84..7f3c59e03 100644
--- a/src/base/net/stratum/EthStratumClient.h
+++ b/src/base/net/stratum/EthStratumClient.h
@@ -48,7 +48,8 @@ protected:
     void setExtraNonce(const rapidjson::Value &nonce);
 
 private:
-    const char *errorMessage(const rapidjson::Value &error) const;
+    static const char *errorMessage(const rapidjson::Value &error);
+
     void authorize();
     void onAuthorizeResponse(const rapidjson::Value &result, bool success, uint64_t elapsed);
     void onSubscribeResponse(const rapidjson::Value &result, bool success, uint64_t elapsed);
diff --git a/src/base/net/stratum/Pool.cpp b/src/base/net/stratum/Pool.cpp
index 367e66677..9a27d7c0a 100644
--- a/src/base/net/stratum/Pool.cpp
+++ b/src/base/net/stratum/Pool.cpp
@@ -82,7 +82,7 @@ const char *Pool::kSpendSecretKey         = "spend-secret-key";
 const char *Pool::kNicehashHost           = "nicehash.com";
 
 
-}
+} // namespace xmrig
 
 
 xmrig::Pool::Pool(const char *url) :
diff --git a/src/base/net/stratum/Pools.cpp b/src/base/net/stratum/Pools.cpp
index d2ab30126..d70075ac0 100644
--- a/src/base/net/stratum/Pools.cpp
+++ b/src/base/net/stratum/Pools.cpp
@@ -230,6 +230,9 @@ void xmrig::Pools::setProxyDonate(int value)
     case PROXY_DONATE_AUTO:
     case PROXY_DONATE_ALWAYS:
         m_proxyDonate = static_cast<ProxyDonate>(value);
+
+    default:
+        break;
     }
 }
 
diff --git a/src/base/net/stratum/Socks5.cpp b/src/base/net/stratum/Socks5.cpp
index 9577906fe..d61aa3292 100644
--- a/src/base/net/stratum/Socks5.cpp
+++ b/src/base/net/stratum/Socks5.cpp
@@ -59,13 +59,13 @@ void xmrig::Client::Socks5::handshake()
 }
 
 
-bool xmrig::Client::Socks5::isIPv4(const String &host, sockaddr_storage *addr) const
+bool xmrig::Client::Socks5::isIPv4(const String &host, sockaddr_storage *addr)
 {
     return uv_ip4_addr(host.data(), 0, reinterpret_cast<sockaddr_in *>(addr)) == 0;
 }
 
 
-bool xmrig::Client::Socks5::isIPv6(const String &host, sockaddr_storage *addr) const
+bool xmrig::Client::Socks5::isIPv6(const String &host, sockaddr_storage *addr)
 {
    return uv_ip6_addr(host.data(), 0, reinterpret_cast<sockaddr_in6 *>(addr)) == 0;
 }
diff --git a/src/base/net/stratum/Socks5.h b/src/base/net/stratum/Socks5.h
index 74ff66437..286ae1832 100644
--- a/src/base/net/stratum/Socks5.h
+++ b/src/base/net/stratum/Socks5.h
@@ -44,8 +44,9 @@ private:
         Ready
     };
 
-    bool isIPv4(const String &host, sockaddr_storage *addr) const;
-    bool isIPv6(const String &host, sockaddr_storage *addr) const;
+    static bool isIPv4(const String &host, sockaddr_storage *addr);
+    static bool isIPv6(const String &host, sockaddr_storage *addr);
+
     void connect();
 
     Client *m_client;
diff --git a/src/base/net/stratum/Tls.cpp b/src/base/net/stratum/Tls.cpp
index a4ccd8537..46ba45116 100644
--- a/src/base/net/stratum/Tls.cpp
+++ b/src/base/net/stratum/Tls.cpp
@@ -1,13 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/stratum/Tls.h"
 #include "base/io/log/Log.h"
 #include "base/net/stratum/Client.h"
@@ -177,7 +170,7 @@ bool xmrig::Client::Tls::verifyFingerprint(X509 *cert)
     }
 
     unsigned char md[EVP_MAX_MD_SIZE];
-    unsigned int dlen;
+    unsigned int dlen = 0;
 
     if (X509_digest(cert, digest, md, &dlen) != 1) {
         return false;
diff --git a/src/base/net/stratum/Tls.h b/src/base/net/stratum/Tls.h
index 38cf2f9e6..cfdda9340 100644
--- a/src/base/net/stratum/Tls.h
+++ b/src/base/net/stratum/Tls.h
@@ -1,12 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/net/stratum/Url.cpp b/src/base/net/stratum/Url.cpp
index 5e2bdb9ac..7904a445d 100644
--- a/src/base/net/stratum/Url.cpp
+++ b/src/base/net/stratum/Url.cpp
@@ -1,13 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2019      Howard Chu  <https://github.com/hyc>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/stratum/Url.h"
 
 
@@ -49,7 +41,7 @@ static const char kDaemonHttp[]            = "daemon+http://";
 static const char kDaemonHttps[]           = "daemon+https://";
 #endif
 
-}
+} // namespace xmrig
 
 
 xmrig::Url::Url(const char *url)
diff --git a/src/base/net/stratum/Url.h b/src/base/net/stratum/Url.h
index 647612c11..6882631e2 100644
--- a/src/base/net/stratum/Url.h
+++ b/src/base/net/stratum/Url.h
@@ -1,13 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2019      Howard Chu  <https://github.com/hyc>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/net/stratum/benchmark/BenchClient.cpp b/src/base/net/stratum/benchmark/BenchClient.cpp
index 05a2c8551..a2f6d44b0 100644
--- a/src/base/net/stratum/benchmark/BenchClient.cpp
+++ b/src/base/net/stratum/benchmark/BenchClient.cpp
@@ -249,7 +249,7 @@ uint64_t xmrig::BenchClient::referenceHash() const
 }
 
 
-void xmrig::BenchClient::printExit()
+void xmrig::BenchClient::printExit() const
 {
     LOG_INFO("%s " WHITE_BOLD("press ") MAGENTA_BOLD("Ctrl+C") WHITE_BOLD(" to exit"), tag());
 }
diff --git a/src/base/net/stratum/benchmark/BenchClient.h b/src/base/net/stratum/benchmark/BenchClient.h
index a4cedf612..c29e42e00 100644
--- a/src/base/net/stratum/benchmark/BenchClient.h
+++ b/src/base/net/stratum/benchmark/BenchClient.h
@@ -90,7 +90,7 @@ private:
 
     bool setSeed(const char *seed);
     uint64_t referenceHash() const;
-    void printExit();
+    void printExit() const;
     void start();
 
 #   ifdef XMRIG_FEATURE_HTTP
diff --git a/src/base/net/tls/TlsConfig.cpp b/src/base/net/tls/TlsConfig.cpp
index 2e3dc903b..32fe9670c 100644
--- a/src/base/net/tls/TlsConfig.cpp
+++ b/src/base/net/tls/TlsConfig.cpp
@@ -1,13 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/tls/TlsConfig.h"
 #include "3rdparty/rapidjson/document.h"
 #include "base/io/json/Json.h"
diff --git a/src/base/net/tls/TlsConfig.h b/src/base/net/tls/TlsConfig.h
index c5407f321..945eb0e8f 100644
--- a/src/base/net/tls/TlsConfig.h
+++ b/src/base/net/tls/TlsConfig.h
@@ -1,13 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -54,7 +48,7 @@ public:
     };
 
     TlsConfig() = default;
-    TlsConfig(const rapidjson::Value &object);
+    TlsConfig(const rapidjson::Value &value);
 
     inline bool isEnabled() const                    { return m_enabled && isValid(); }
     inline bool isValid() const                      { return !m_cert.isEmpty() && !m_key.isEmpty(); }
diff --git a/src/base/net/tls/TlsContext.cpp b/src/base/net/tls/TlsContext.cpp
index 03c79e654..e36217975 100644
--- a/src/base/net/tls/TlsContext.cpp
+++ b/src/base/net/tls/TlsContext.cpp
@@ -1,13 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -23,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/tls/TlsContext.h"
 #include "base/io/Env.h"
 #include "base/io/log/Log.h"
@@ -223,7 +216,7 @@ bool xmrig::TlsContext::setDH(const char *dhparam)
         dh = get_dh2048();
     }
 
-    const int rc = SSL_CTX_set_tmp_dh(m_ctx, dh);
+    const int rc = SSL_CTX_set_tmp_dh(m_ctx, dh); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
 
     DH_free(dh);
 
diff --git a/src/base/net/tls/TlsContext.h b/src/base/net/tls/TlsContext.h
index b87798c5b..9a9b3cb1c 100644
--- a/src/base/net/tls/TlsContext.h
+++ b/src/base/net/tls/TlsContext.h
@@ -1,13 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/net/tls/TlsGen.cpp b/src/base/net/tls/TlsGen.cpp
index c56ef2a21..428920537 100644
--- a/src/base/net/tls/TlsGen.cpp
+++ b/src/base/net/tls/TlsGen.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/net/tls/TlsGen.h"
 
 
@@ -41,6 +40,8 @@ static EVP_PKEY *generate_pkey()
 
     auto exponent = BN_new();
     auto rsa      = RSA_new();
+
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
     if (!exponent || !rsa || !BN_set_word(exponent, RSA_F4) || !RSA_generate_key_ex(rsa, 2048, exponent, nullptr) || !EVP_PKEY_assign_RSA(pkey, rsa)) {
         EVP_PKEY_free(pkey);
         BN_free(exponent);
diff --git a/src/base/net/tls/TlsGen.h b/src/base/net/tls/TlsGen.h
index 9386e88ca..c471c8ca6 100644
--- a/src/base/net/tls/TlsGen.h
+++ b/src/base/net/tls/TlsGen.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/tools/Cvt.cpp b/src/base/tools/Cvt.cpp
index 0d1c89fb0..3f083a266 100644
--- a/src/base/tools/Cvt.cpp
+++ b/src/base/tools/Cvt.cpp
@@ -36,9 +36,9 @@ namespace xmrig {
 static char *cvt_bin2hex(char *const hex, const size_t hex_maxlen, const unsigned char *const bin, const size_t bin_len)
 {
     size_t       i = 0U;
-    unsigned int x;
-    int          b;
-    int          c;
+    unsigned int x = 0U;
+    int          b = 0;
+    int          c = 0;
 
     if (bin_len >= SIZE_MAX / 2 || hex_maxlen < bin_len * 2U) {
         return nullptr; /* LCOV_EXCL_LINE */
@@ -70,17 +70,17 @@ static std::mt19937 randomEngine(randomDevice());
 
 static int cvt_hex2bin(unsigned char *const bin, const size_t bin_maxlen, const char *const hex, const size_t hex_len, const char *const ignore, size_t *const bin_len, const char **const hex_end)
 {
-    size_t        bin_pos = 0U;
-    size_t        hex_pos = 0U;
-    int           ret     = 0;
-    unsigned char c;
-    unsigned char c_acc = 0U;
-    unsigned char c_alpha0;
-    unsigned char c_alpha;
-    unsigned char c_num0;
-    unsigned char c_num;
-    unsigned char c_val;
-    unsigned char state = 0U;
+    size_t        bin_pos   = 0U;
+    size_t        hex_pos   = 0U;
+    int           ret       = 0;
+    unsigned char c         = 0U;
+    unsigned char c_acc     = 0U;
+    unsigned char c_alpha0  = 0U;
+    unsigned char c_alpha   = 0U;
+    unsigned char c_num0    = 0U;
+    unsigned char c_num     = 0U;
+    unsigned char c_val     = 0U;
+    unsigned char state     = 0U;
 
     while (hex_pos < hex_len) {
         c        = (unsigned char) hex[hex_pos];
@@ -193,13 +193,13 @@ bool xmrig::Cvt::fromHex(uint8_t *bin, size_t bin_maxlen, const char *hex, size_
 }
 
 
-bool xmrig::Cvt::fromHex(uint8_t *bin, size_t max, const rapidjson::Value &value)
+bool xmrig::Cvt::fromHex(uint8_t *bin, size_t bin_maxlen, const rapidjson::Value &value)
 {
     if (!value.IsString()) {
         return false;
     }
 
-    return fromHex(bin, max, value.GetString(), value.GetStringLength());
+    return fromHex(bin, bin_maxlen, value.GetString(), value.GetStringLength());
 }
 
 
diff --git a/src/base/tools/cryptonote/BlockTemplate.cpp b/src/base/tools/cryptonote/BlockTemplate.cpp
index db3d0496b..4f6d27333 100644
--- a/src/base/tools/cryptonote/BlockTemplate.cpp
+++ b/src/base/tools/cryptonote/BlockTemplate.cpp
@@ -78,7 +78,9 @@ void xmrig::BlockTemplate::calculateMerkleTreeHash()
         keccak(h, kHashSize * 2, m_rootHash, kHashSize);
     }
     else {
-        size_t i, j, cnt;
+        size_t i    = 0;
+        size_t j    = 0;
+        size_t cnt  = 0;
 
         for (i = 0, cnt = 1; cnt <= count; ++i, cnt <<= 1) {}
 
@@ -261,7 +263,7 @@ bool xmrig::BlockTemplate::parse(bool hashes)
             break;
 
         default:
-            return false; // TODO: handle other tags
+            return false; // TODO(SChernykh): handle other tags
         }
     }
 
diff --git a/src/base/tools/cryptonote/WalletAddress.cpp b/src/base/tools/cryptonote/WalletAddress.cpp
index 8ea2bbd64..9f5a562dc 100644
--- a/src/base/tools/cryptonote/WalletAddress.cpp
+++ b/src/base/tools/cryptonote/WalletAddress.cpp
@@ -183,7 +183,7 @@ rapidjson::Value xmrig::WalletAddress::toAPI(rapidjson::Document &doc) const
 
 const xmrig::WalletAddress::TagInfo &xmrig::WalletAddress::tagInfo(uint64_t tag)
 {
-    static TagInfo dummy = { Coin::INVALID, MAINNET, PUBLIC, 0 };
+    static TagInfo dummy = { Coin::INVALID, MAINNET, PUBLIC, 0, 0 };
     static const std::map<uint64_t, TagInfo> tags = {
         { 18,       { Coin::MONERO,     MAINNET,    PUBLIC,         18081,  18082 } },
         { 19,       { Coin::MONERO,     MAINNET,    INTEGRATED,     18081,  18082 } },
diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp
index 9242b6ee3..626175aeb 100644
--- a/src/core/Controller.cpp
+++ b/src/core/Controller.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "core/Controller.h"
 #include "backend/cpu/Cpu.h"
 #include "core/config/Config.h"
@@ -100,7 +99,7 @@ xmrig::Network *xmrig::Controller::network() const
 }
 
 
-void xmrig::Controller::execCommand(char command)
+void xmrig::Controller::execCommand(char command) const
 {
     miner()->execCommand(command);
     network()->execCommand(command);
diff --git a/src/core/Controller.h b/src/core/Controller.h
index 13704f151..42ae04ff5 100644
--- a/src/core/Controller.h
+++ b/src/core/Controller.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -49,7 +49,7 @@ public:
 
     Miner *miner() const;
     Network *network() const;
-    void execCommand(char command);
+    void execCommand(char command) const;
 
 private:
     std::shared_ptr<Miner> m_miner;
diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp
index 81b426eb4..2b6c1dbee 100644
--- a/src/core/Miner.cpp
+++ b/src/core/Miner.cpp
@@ -78,7 +78,7 @@ public:
     XMRIG_DISABLE_COPY_MOVE_DEFAULT(MinerPrivate)
 
 
-    inline MinerPrivate(Controller *controller) : controller(controller) {}
+    inline explicit MinerPrivate(Controller *controller) : controller(controller) {}
 
 
     inline ~MinerPrivate()
@@ -329,7 +329,7 @@ public:
 
 
 #   ifdef XMRIG_ALGO_RANDOMX
-    inline bool initRX() { return Rx::init(job, controller->config()->rx(), controller->config()->cpu()); }
+    inline bool initRX() const { return Rx::init(job, controller->config()->rx(), controller->config()->cpu()); }
 #   endif
 
 
diff --git a/src/core/config/Config.cpp b/src/core/config/Config.cpp
index fa5760185..6150607d0 100644
--- a/src/core/config/Config.cpp
+++ b/src/core/config/Config.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <algorithm>
 #include <cinttypes>
 #include <cstring>
@@ -112,7 +111,7 @@ public:
     }
 };
 
-}
+} // namespace xmrig
 
 
 xmrig::Config::Config() :
diff --git a/src/core/config/ConfigTransform.cpp b/src/core/config/ConfigTransform.cpp
index 1c4f4244a..20a89eda6 100644
--- a/src/core/config/ConfigTransform.cpp
+++ b/src/core/config/ConfigTransform.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "core/config/ConfigTransform.h"
 #include "base/kernel/interfaces/IConfig.h"
 #include "base/net/stratum/Pool.h"
@@ -354,6 +353,9 @@ void xmrig::ConfigTransform::transformBenchmark(rapidjson::Document &doc, int ke
 
     case IConfig::UserKey: /* --user */
         return set(doc, BenchConfig::kBenchmark, BenchConfig::kUser, arg);
+
+    default:
+        break;
     }
 }
 #endif
diff --git a/src/crypto/astrobwt/AstroBWT.cpp b/src/crypto/astrobwt/AstroBWT.cpp
index 3e3622530..26b802251 100644
--- a/src/crypto/astrobwt/AstroBWT.cpp
+++ b/src/crypto/astrobwt/AstroBWT.cpp
@@ -1,16 +1,10 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik              <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler                   <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones              <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466                 <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee                <jayddee246@gmail.com>
- * Copyright 2017-2019 XMR-Stak                 <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett              <https://github.com/vtnerd>
- * Copyright 2018-2019 tevador                  <tevador@gmail.com>
- * Copyright 2000      Transmeta Corporation    <https://github.com/intel/msr-tools>
- * Copyright 2004-2008 H. Peter Anvin           <https://github.com/intel/msr-tools>
- * Copyright 2018-2020 SChernykh                <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig                    <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett              <https://github.com/vtnerd>
+ * Copyright (c) 2018-2019 tevador                  <tevador@gmail.com>
+ * Copyright (c) 2000      Transmeta Corporation    <https://github.com/intel/msr-tools>
+ * Copyright (c) 2004-2008 H. Peter Anvin           <https://github.com/intel/msr-tools>
+ * Copyright (c) 2018-2021 SChernykh                <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig                    <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -26,7 +20,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "crypto/astrobwt/AstroBWT.h"
 #include "backend/cpu/Cpu.h"
 #include "base/crypto/sha3.h"
@@ -123,11 +116,13 @@ void sort_indices(int N, const uint8_t* v, uint64_t* indices, uint64_t* tmp_indi
 		const uint64_t value_a = a >> 21;
 		const uint64_t value_b = b >> 21;
 
-		if (value_a < value_b)
+		if (value_a < value_b) {
 			return true;
+		}
 
-		if (value_a > value_b)
+		if (value_a > value_b) {
 			return false;
+		}
 
 		const uint64_t data_a = bswap_64(*reinterpret_cast<const uint64_t*>(v + (a % (1 << 21)) + 5));
 		const uint64_t data_b = bswap_64(*reinterpret_cast<const uint64_t*>(v + (b % (1 << 21)) + 5));
@@ -146,8 +141,11 @@ void sort_indices(int N, const uint8_t* v, uint64_t* indices, uint64_t* tmp_indi
 			{
 				indices[j + 1] = prev_t;
 				--j;
-				if (j < 0)
+
+				if (j < 0) {
 					break;
+				}
+
 				prev_t = indices[j];
 			} while (smaller(t, prev_t));
 			indices[j + 1] = t;
@@ -181,8 +179,9 @@ bool xmrig::astrobwt::astrobwt_dero(const void* input_data, uint32_t input_size,
 
 	{
 		const uint8_t* tmp = stage1_output - 1;
-		for (int i = 0; i <= STAGE1_SIZE; ++i)
+		for (int i = 0; i <= STAGE1_SIZE; ++i) {
 			stage1_result[i] = tmp[indices[i] & ((1 << 21) - 1)];
+		}
 	}
 
 #ifdef ASTROBWT_AVX2
@@ -193,8 +192,9 @@ bool xmrig::astrobwt::astrobwt_dero(const void* input_data, uint32_t input_size,
 		sha3_HashBuffer(256, SHA3_FLAGS_NONE, stage1_result, STAGE1_SIZE + 1, key, sizeof(key));
 
 	const int stage2_size = STAGE1_SIZE + (*(uint32_t*)(key) & 0xfffff);
-	if (stage2_size > stage2_max_size)
+	if (stage2_size > stage2_max_size) {
 		return false;
+	}
 
 	Salsa20_XORKeyStream(key, stage2_output, stage2_size);
 
@@ -204,6 +204,7 @@ bool xmrig::astrobwt::astrobwt_dero(const void* input_data, uint32_t input_size,
 		const uint8_t* tmp = stage2_output - 1;
 		int i = 0;
 		const int n = ((stage2_size + 1) / 4) * 4;
+
 		for (; i < n; i += 4)
 		{
 			stage2_result[i + 0] = tmp[indices[i + 0] & ((1 << 21) - 1)];
@@ -211,8 +212,10 @@ bool xmrig::astrobwt::astrobwt_dero(const void* input_data, uint32_t input_size,
 			stage2_result[i + 2] = tmp[indices[i + 2] & ((1 << 21) - 1)];
 			stage2_result[i + 3] = tmp[indices[i + 3] & ((1 << 21) - 1)];
 		}
-		for (; i <= stage2_size; ++i)
+
+		for (; i <= stage2_size; ++i) {
 			stage2_result[i] = tmp[indices[i] & ((1 << 21) - 1)];
+		}
 	}
 
 #ifdef ASTROBWT_AVX2
diff --git a/src/crypto/astrobwt/AstroBWT.h b/src/crypto/astrobwt/AstroBWT.h
index 4e526060d..236b3ce47 100644
--- a/src/crypto/astrobwt/AstroBWT.h
+++ b/src/crypto/astrobwt/AstroBWT.h
@@ -1,16 +1,10 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik              <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler                   <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones              <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466                 <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee                <jayddee246@gmail.com>
- * Copyright 2017-2019 XMR-Stak                 <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett              <https://github.com/vtnerd>
- * Copyright 2018-2019 tevador                  <tevador@gmail.com>
- * Copyright 2000      Transmeta Corporation    <https://github.com/intel/msr-tools>
- * Copyright 2004-2008 H. Peter Anvin           <https://github.com/intel/msr-tools>
- * Copyright 2018-2020 SChernykh                <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig                    <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett              <https://github.com/vtnerd>
+ * Copyright (c) 2018-2019 tevador                  <tevador@gmail.com>
+ * Copyright (c) 2000      Transmeta Corporation    <https://github.com/intel/msr-tools>
+ * Copyright (c) 2004-2008 H. Peter Anvin           <https://github.com/intel/msr-tools>
+ * Copyright (c) 2018-2021 SChernykh                <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig                    <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -26,7 +20,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/crypto/Algorithm.h"
 
 
diff --git a/src/crypto/cn/CnCtx.cpp b/src/crypto/cn/CnCtx.cpp
index e92fe22ec..c0dc8b344 100644
--- a/src/crypto/cn/CnCtx.cpp
+++ b/src/crypto/cn/CnCtx.cpp
@@ -1,13 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2019 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -36,8 +30,8 @@
 void xmrig::CnCtx::create(cryptonight_ctx **ctx, uint8_t *memory, size_t size, size_t count)
 {
     for (size_t i = 0; i < count; ++i) {
-        cryptonight_ctx *c = static_cast<cryptonight_ctx *>(_mm_malloc(sizeof(cryptonight_ctx), 4096));
-        c->memory          = memory + (i * size);
+        auto *c     = static_cast<cryptonight_ctx *>(_mm_malloc(sizeof(cryptonight_ctx), 4096));
+        c->memory   = memory + (i * size);
 
         c->generated_code              = reinterpret_cast<cn_mainloop_fun_ms_abi>(VirtualMemory::allocateExecutableMemory(0x4000, false));
         c->generated_code_data.algo    = Algorithm::INVALID;
diff --git a/src/crypto/cn/CnCtx.h b/src/crypto/cn/CnCtx.h
index 7939bf4e9..c90430d19 100644
--- a/src/crypto/cn/CnCtx.h
+++ b/src/crypto/cn/CnCtx.h
@@ -1,13 +1,7 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2019 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/crypto/common/Nonce.cpp b/src/crypto/common/Nonce.cpp
index bd23e54c1..e2e51c2c2 100644
--- a/src/crypto/common/Nonce.cpp
+++ b/src/crypto/common/Nonce.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "crypto/common/Nonce.h"
 
 
@@ -48,7 +41,8 @@ bool xmrig::Nonce::next(uint8_t index, uint32_t *nonce, uint32_t reserveCount, u
         if (mask < counter) {
             return false;
         }
-        else if (mask - counter <= reserveCount - 1) {
+
+        if (mask - counter <= reserveCount - 1) {
             pause(true);
             if (mask - counter < reserveCount - 1) {
                 return false;
@@ -58,10 +52,13 @@ bool xmrig::Nonce::next(uint8_t index, uint32_t *nonce, uint32_t reserveCount, u
             counter = m_nonces[index].fetch_add(reserveCount, std::memory_order_relaxed);
             continue;
         }
+
         *nonce = (nonce[0] & ~mask) | counter;
+
         if (mask > 0xFFFFFFFFULL) {
             nonce[1] = (nonce[1] & (~mask >> 32)) | (counter >> 32);
         }
+
         return true;
     }
 }
diff --git a/src/crypto/common/Nonce.h b/src/crypto/common/Nonce.h
index 7c0e6d49a..5742e67a1 100644
--- a/src/crypto/common/Nonce.h
+++ b/src/crypto/common/Nonce.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/crypto/common/VirtualMemory_hwloc.cpp b/src/crypto/common/VirtualMemory_hwloc.cpp
index e9e23aa9d..d11290081 100644
--- a/src/crypto/common/VirtualMemory_hwloc.cpp
+++ b/src/crypto/common/VirtualMemory_hwloc.cpp
@@ -1,14 +1,8 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018      Lee Clagett <https://github.com/vtnerd>
- * Copyright 2018-2019 SChernykh   <https://github.com/SChernykh>
- * Copyright 2018-2019 tevador     <tevador@gmail.com>
- * Copyright 2016-2019 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018      Lee Clagett <https://github.com/vtnerd>
+ * Copyright (c) 2018-2019 tevador     <tevador@gmail.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp
index c68b8bf1c..e1455dba7 100644
--- a/src/crypto/rx/Rx.cpp
+++ b/src/crypto/rx/Rx.cpp
@@ -45,7 +45,7 @@ static RxPrivate *d_ptr     = nullptr;
 class RxPrivate
 {
 public:
-    inline RxPrivate(IRxListener *listener) : queue(listener) {}
+    inline explicit RxPrivate(IRxListener *listener) : queue(listener) {}
 
     RxQueue queue;
 };
diff --git a/src/crypto/rx/RxConfig.cpp b/src/crypto/rx/RxConfig.cpp
index ae6215dfe..1cd6f6eb1 100644
--- a/src/crypto/rx/RxConfig.cpp
+++ b/src/crypto/rx/RxConfig.cpp
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -22,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "crypto/rx/RxConfig.h"
 #include "3rdparty/rapidjson/document.h"
 #include "backend/cpu/Cpu.h"
@@ -81,7 +74,7 @@ static_assert (kMsrArraySize == ICpuInfo::MSR_MOD_MAX, "kMsrArraySize and MSR_MO
 #endif
 
 
-}
+} // namespace xmrig
 
 
 bool xmrig::RxConfig::read(const rapidjson::Value &value)
@@ -286,7 +279,7 @@ void xmrig::RxConfig::readMSR(const rapidjson::Value &value)
 #endif
 
 
-xmrig::RxConfig::Mode xmrig::RxConfig::readMode(const rapidjson::Value &value) const
+xmrig::RxConfig::Mode xmrig::RxConfig::readMode(const rapidjson::Value &value)
 {
     if (value.IsUint()) {
         return static_cast<Mode>(std::min(value.GetUint(), ModeMax - 1));
diff --git a/src/crypto/rx/RxConfig.h b/src/crypto/rx/RxConfig.h
index ea1bf6859..e4fb4f89c 100644
--- a/src/crypto/rx/RxConfig.h
+++ b/src/crypto/rx/RxConfig.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -111,7 +105,7 @@ private:
 
     bool m_cacheQoS = false;
 
-    Mode readMode(const rapidjson::Value &value) const;
+    static Mode readMode(const rapidjson::Value &value);
 
     bool m_oneGbPages     = false;
     bool m_rdmsr          = true;
diff --git a/src/crypto/rx/RxDataset.cpp b/src/crypto/rx/RxDataset.cpp
index b47285a3d..86b3a3f6d 100644
--- a/src/crypto/rx/RxDataset.cpp
+++ b/src/crypto/rx/RxDataset.cpp
@@ -1,7 +1,7 @@
 /* XMRig
  * Copyright (c) 2018-2019 tevador     <tevador@gmail.com>
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "crypto/rx/RxDataset.h"
 #include "backend/cpu/Cpu.h"
 #include "base/io/log/Log.h"
@@ -36,7 +35,7 @@
 namespace xmrig {
 
 
-static void init_dataset_wrapper(randomx_dataset *dataset, randomx_cache *cache, unsigned long startItem, unsigned long itemCount, int priority)
+static void init_dataset_wrapper(randomx_dataset *dataset, randomx_cache *cache, uint32_t startItem, uint32_t itemCount, int priority)
 {
     Platform::setThreadPriority(priority);
 
diff --git a/src/crypto/rx/RxDataset.h b/src/crypto/rx/RxDataset.h
index 1621cae14..4f9caadf4 100644
--- a/src/crypto/rx/RxDataset.h
+++ b/src/crypto/rx/RxDataset.h
@@ -1,7 +1,7 @@
 /* XMRig
  * Copyright (c) 2018-2019 tevador     <tevador@gmail.com>
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/crypto/rx/RxFix_win.cpp b/src/crypto/rx/RxFix_win.cpp
index 75538be4d..3d722cd39 100644
--- a/src/crypto/rx/RxFix_win.cpp
+++ b/src/crypto/rx/RxFix_win.cpp
@@ -17,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "crypto/rx/RxFix.h"
 #include "base/io/log/Log.h"
 
@@ -34,7 +33,7 @@ static thread_local std::pair<const void*, const void*> mainLoopBounds = { nullp
 static LONG WINAPI MainLoopHandler(_EXCEPTION_POINTERS *ExceptionInfo)
 {
     if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0xC0000005) {
-        const char* accessType;
+        const char* accessType = nullptr;
         switch (ExceptionInfo->ExceptionRecord->ExceptionInformation[0]) {
         case 0: accessType = "read"; break;
         case 1: accessType = "write"; break;
@@ -47,7 +46,7 @@ static LONG WINAPI MainLoopHandler(_EXCEPTION_POINTERS *ExceptionInfo)
         LOG_VERBOSE(YELLOW_BOLD("[THREAD %u] Exception 0x%08X at 0x%p"), GetCurrentThreadId(), ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress);
     }
 
-    void* p = reinterpret_cast<void*>(ExceptionInfo->ContextRecord->Rip);
+    void* p = reinterpret_cast<void*>(ExceptionInfo->ContextRecord->Rip); // NOLINT(performance-no-int-to-ptr)
     const std::pair<const void*, const void*>& loopBounds = mainLoopBounds;
 
     if ((loopBounds.first <= p) && (p < loopBounds.second)) {
diff --git a/src/crypto/rx/RxNUMAStorage.cpp b/src/crypto/rx/RxNUMAStorage.cpp
index 6bd5627f2..b6345a061 100644
--- a/src/crypto/rx/RxNUMAStorage.cpp
+++ b/src/crypto/rx/RxNUMAStorage.cpp
@@ -1,7 +1,7 @@
 /* XMRig
  * Copyright (c) 2018-2019 tevador     <tevador@gmail.com>
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "crypto/rx/RxNUMAStorage.h"
 #include "backend/cpu/Cpu.h"
 #include "backend/cpu/platform/HwlocCpuInfo.h"
@@ -79,7 +78,7 @@ class RxNUMAStoragePrivate
 public:
     XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxNUMAStoragePrivate)
 
-    inline RxNUMAStoragePrivate(const std::vector<uint32_t> &nodeset) :
+    inline explicit RxNUMAStoragePrivate(const std::vector<uint32_t> &nodeset) :
         m_nodeset(nodeset)
     {
         m_threads.reserve(nodeset.size());
@@ -230,7 +229,7 @@ private:
 
         std::lock_guard<std::mutex> lock(mutex);
         d_ptr->m_datasets.insert({ nodeId, dataset });
-        d_ptr->printAllocStatus(dataset, nodeId, ts);
+        RxNUMAStoragePrivate::printAllocStatus(dataset, nodeId, ts);
     }
 
 
@@ -251,7 +250,7 @@ private:
 
         std::lock_guard<std::mutex> lock(mutex);
         d_ptr->m_cache = cache;
-        d_ptr->printAllocStatus(cache, nodeId, ts);
+        RxNUMAStoragePrivate::printAllocStatus(cache, nodeId, ts);
     }
 
 
@@ -265,7 +264,7 @@ private:
     }
 
 
-    void printAllocStatus(RxDataset *dataset, uint32_t nodeId, uint64_t ts)
+    static void printAllocStatus(RxDataset *dataset, uint32_t nodeId, uint64_t ts)
     {
         const auto pages = dataset->hugePages();
 
@@ -280,7 +279,7 @@ private:
     }
 
 
-    void printAllocStatus(RxCache *cache, uint32_t nodeId, uint64_t ts)
+    static void printAllocStatus(RxCache *cache, uint32_t nodeId, uint64_t ts)
     {
         const auto pages = cache->hugePages();
 
@@ -296,7 +295,7 @@ private:
     }
 
 
-    void printAllocStatus(uint64_t ts)
+    void printAllocStatus(uint64_t ts) const
     {
         auto pages = hugePages();
 
diff --git a/src/crypto/rx/RxNUMAStorage.h b/src/crypto/rx/RxNUMAStorage.h
index 29ec5fb0f..33d5b92ab 100644
--- a/src/crypto/rx/RxNUMAStorage.h
+++ b/src/crypto/rx/RxNUMAStorage.h
@@ -1,7 +1,7 @@
 /* XMRig
  * Copyright (c) 2018-2019 tevador     <tevador@gmail.com>
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/hw/dmi/DmiReader.cpp b/src/hw/dmi/DmiReader.cpp
index 18211759a..1e12603c2 100644
--- a/src/hw/dmi/DmiReader.cpp
+++ b/src/hw/dmi/DmiReader.cpp
@@ -18,7 +18,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "hw/dmi/DmiReader.h"
 #include "3rdparty/fmt/core.h"
 #include "3rdparty/rapidjson/document.h"
@@ -111,7 +110,6 @@ bool xmrig::DmiReader::decode(uint8_t *buf)
         next += 2;
 
         if (static_cast<uint32_t>(next - buf) > m_size) {
-            data = next;
             break;
         }
 
diff --git a/src/hw/msr/Msr.cpp b/src/hw/msr/Msr.cpp
index 1fed87760..47ed424bd 100644
--- a/src/hw/msr/Msr.cpp
+++ b/src/hw/msr/Msr.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "hw/msr/Msr.h"
 #include "base/io/log/Log.h"
 
@@ -58,7 +57,7 @@ std::shared_ptr<xmrig::Msr> xmrig::Msr::get()
 bool xmrig::Msr::write(uint32_t reg, uint64_t value, int32_t cpu, uint64_t mask, bool verbose)
 {
     if (mask != MsrItem::kNoMask) {
-        uint64_t old_value;
+        uint64_t old_value = 0;
         if (rdmsr(reg, cpu, old_value)) {
             value = MsrItem::maskedValue(old_value, value, mask);
         }
diff --git a/src/net/JobResults.cpp b/src/net/JobResults.cpp
index bcf1abd13..19a1dc43c 100644
--- a/src/net/JobResults.cpp
+++ b/src/net/JobResults.cpp
@@ -291,8 +291,6 @@ private:
     }
 #   endif
 
-
-private:
     const bool m_hwAES;
     IJobResultListener *m_listener;
     std::list<JobResult> m_results;

From c27f535768ad314eaa56399336a6e543d9fe3337 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Wed, 25 Aug 2021 18:52:54 +0700
Subject: [PATCH 07/20] Fixed build on Linux.

---
 src/base/io/log/Log.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/base/io/log/Log.cpp b/src/base/io/log/Log.cpp
index 891b93b67..c20f85c7a 100644
--- a/src/base/io/log/Log.cpp
+++ b/src/base/io/log/Log.cpp
@@ -231,7 +231,7 @@ void xmrig::Log::print(const char *fmt, ...)
         return;
     }
 
-    va_list args = nullptr;
+    va_list args{};
     va_start(args, fmt);
 
     d->print(NONE, fmt, args);
@@ -246,7 +246,7 @@ void xmrig::Log::print(Level level, const char *fmt, ...)
         return;
     }
 
-    va_list args = nullptr;
+    va_list args{};
     va_start(args, fmt);
 
     d->print(level, fmt, args);

From df4532d9a134add0b5a6960190db15baf24403ef Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Fri, 27 Aug 2021 12:36:08 +0700
Subject: [PATCH 08/20] Cleanup ARM code.

---
 cmake/cpu.cmake                               | 25 ++++++++++---------
 cmake/flags.cmake                             |  8 +++---
 cmake/os.cmake                                | 23 +++++++----------
 src/backend/cpu/platform/BasicCpuInfo_arm.cpp |  5 ++--
 src/crypto/randomx/randomx.cpp                |  6 ++---
 src/crypto/randomx/randomx.h                  |  4 +--
 6 files changed, 33 insertions(+), 38 deletions(-)

diff --git a/cmake/cpu.cmake b/cmake/cpu.cmake
index a37f10662..14b17763e 100644
--- a/cmake/cpu.cmake
+++ b/cmake/cpu.cmake
@@ -1,9 +1,16 @@
+if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+    set(XMRIG_64_BIT ON)
+    add_definitions(-DXMRIG_64_BIT)
+else()
+    set(XMRIG_64_BIT OFF)
+endif()
+
 if (NOT CMAKE_SYSTEM_PROCESSOR)
     message(WARNING "CMAKE_SYSTEM_PROCESSOR not defined")
 endif()
 
-if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$" AND CMAKE_SIZEOF_VOID_P EQUAL 8)
-    add_definitions(/DRAPIDJSON_SSE2)
+if (XMRIG_64_BIT AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$")
+    add_definitions(-DRAPIDJSON_SSE2)
 else()
     set(WITH_SSE4_1 OFF)
 endif()
@@ -17,31 +24,25 @@ if (NOT ARM_TARGET)
 endif()
 
 if (ARM_TARGET AND ARM_TARGET GREATER 6)
-    set(XMRIG_ARM     ON)
-    add_definitions(/DXMRIG_ARM)
+    set(XMRIG_ARM ON)
+    add_definitions(-DXMRIG_ARM=${ARM_TARGET})
 
     message(STATUS "Use ARM_TARGET=${ARM_TARGET} (${CMAKE_SYSTEM_PROCESSOR})")
 
     include(CheckCXXCompilerFlag)
 
     if (ARM_TARGET EQUAL 8)
-        set(XMRIG_ARMv8 ON)
-        add_definitions(/DXMRIG_ARMv8)
-
         CHECK_CXX_COMPILER_FLAG(-march=armv8-a+crypto XMRIG_ARM_CRYPTO)
 
         if (XMRIG_ARM_CRYPTO)
-            add_definitions(/DXMRIG_ARM_CRYPTO)
+            add_definitions(-DXMRIG_ARM_CRYPTO)
             set(ARM8_CXX_FLAGS "-march=armv8-a+crypto")
         else()
             set(ARM8_CXX_FLAGS "-march=armv8-a")
         endif()
-    elseif (ARM_TARGET EQUAL 7)
-        set(XMRIG_ARMv7 ON)
-        add_definitions(/DXMRIG_ARMv7)
     endif()
 endif()
 
 if (WITH_SSE4_1)
-    add_definitions(/DXMRIG_FEATURE_SSE4_1)
+    add_definitions(-DXMRIG_FEATURE_SSE4_1)
 endif()
diff --git a/cmake/flags.cmake b/cmake/flags.cmake
index cb4e0610c..ff5943a13 100644
--- a/cmake/flags.cmake
+++ b/cmake/flags.cmake
@@ -22,10 +22,10 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fexceptions -fno-rtti -Wno-strict-aliasing -Wno-class-memaccess")
     set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -s")
 
-    if (XMRIG_ARMv8)
+    if (ARM_TARGET EQUAL 8)
         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARM8_CXX_FLAGS}")
         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARM8_CXX_FLAGS} -flax-vector-conversions")
-    elseif (XMRIG_ARMv7)
+    elseif (ARM_TARGET EQUAL 7)
         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon")
         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon -flax-vector-conversions")
     else()
@@ -80,10 +80,10 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fexceptions -fno-rtti -Wno-missing-braces")
     set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -funroll-loops -fmerge-all-constants")
 
-    if (XMRIG_ARMv8)
+    if (ARM_TARGET EQUAL 8)
         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARM8_CXX_FLAGS}")
         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARM8_CXX_FLAGS}")
-    elseif (XMRIG_ARMv7)
+    elseif (ARM_TARGET EQUAL 7)
         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=${CMAKE_SYSTEM_PROCESSOR}")
         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon -march=${CMAKE_SYSTEM_PROCESSOR}")
     else()
diff --git a/cmake/os.cmake b/cmake/os.cmake
index 6516c1f7a..02a787dff 100644
--- a/cmake/os.cmake
+++ b/cmake/os.cmake
@@ -1,7 +1,3 @@
-if (CMAKE_SIZEOF_VOID_P EQUAL 8)
-    add_definitions(/DXMRIG_64_BIT)
-endif()
-
 if (WIN32)
     set(XMRIG_OS_WIN ON)
 elseif (APPLE)
@@ -26,32 +22,31 @@ endif()
 
 
 if (XMRIG_OS_WIN)
-    add_definitions(/DWIN32)
-    add_definitions(/DXMRIG_OS_WIN)
+    add_definitions(-DWIN32 -DXMRIG_OS_WIN)
 elseif(XMRIG_OS_APPLE)
-    add_definitions(/DXMRIG_OS_APPLE)
+    add_definitions(-DXMRIG_OS_APPLE)
 
     if (XMRIG_OS_IOS)
-        add_definitions(/DXMRIG_OS_IOS)
+        add_definitions(-DXMRIG_OS_IOS)
     else()
-        add_definitions(/DXMRIG_OS_MACOS)
+        add_definitions(-DXMRIG_OS_MACOS)
     endif()
 
     if (XMRIG_ARM)
         set(WITH_SECURE_JIT ON)
     endif()
 elseif(XMRIG_OS_UNIX)
-    add_definitions(/DXMRIG_OS_UNIX)
+    add_definitions(-DXMRIG_OS_UNIX)
 
     if (XMRIG_OS_ANDROID)
-        add_definitions(/DXMRIG_OS_ANDROID)
+        add_definitions(-DXMRIG_OS_ANDROID)
     elseif (XMRIG_OS_LINUX)
-        add_definitions(/DXMRIG_OS_LINUX)
+        add_definitions(-DXMRIG_OS_LINUX)
     elseif (XMRIG_OS_FREEBSD)
-        add_definitions(/DXMRIG_OS_FREEBSD)
+        add_definitions(-DXMRIG_OS_FREEBSD)
     endif()
 endif()
 
 if (WITH_SECURE_JIT)
-    add_definitions(/DXMRIG_SECURE_JIT)
+    add_definitions(-DXMRIG_SECURE_JIT)
 endif()
diff --git a/src/backend/cpu/platform/BasicCpuInfo_arm.cpp b/src/backend/cpu/platform/BasicCpuInfo_arm.cpp
index ad24e10b0..b90138d83 100644
--- a/src/backend/cpu/platform/BasicCpuInfo_arm.cpp
+++ b/src/backend/cpu/platform/BasicCpuInfo_arm.cpp
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/tools/String.h"
 
 
@@ -63,7 +62,7 @@ xmrig::BasicCpuInfo::BasicCpuInfo() :
         m_units[i] = i;
     }
 
-#   ifdef XMRIG_ARMv8
+#   if (XMRIG_ARM == 8)
     memcpy(m_brand, "ARMv8", 5);
 #   else
     memcpy(m_brand, "ARMv7", 5);
@@ -128,7 +127,7 @@ rapidjson::Value xmrig::BasicCpuInfo::toJSON(rapidjson::Document &doc) const
     out.AddMember("msr",        "none", allocator);
     out.AddMember("assembly",   "none", allocator);
 
-#   ifdef XMRIG_ARMv8
+#   if (XMRIG_ARM == 8)
     out.AddMember("arch", "aarch64", allocator);
 #   else
     out.AddMember("arch", "aarch32", allocator);
diff --git a/src/crypto/randomx/randomx.cpp b/src/crypto/randomx/randomx.cpp
index fe438b61e..ed351e44e 100644
--- a/src/crypto/randomx/randomx.cpp
+++ b/src/crypto/randomx/randomx.cpp
@@ -37,7 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #if defined(_M_X64) || defined(__x86_64__)
 #include "crypto/randomx/jit_compiler_x86_static.hpp"
-#elif defined(XMRIG_ARMv8)
+#elif (XMRIG_ARM == 8)
 #include "crypto/randomx/jit_compiler_a64_static.hpp"
 #endif
 
@@ -180,7 +180,7 @@ RandomX_ConfigurationBase::RandomX_ConfigurationBase()
 #	endif
 }
 
-#ifdef XMRIG_ARMv8
+#if (XMRIG_ARM == 8)
 static uint32_t Log2(size_t value) { return (value > 1) ? (Log2(value / 2) + 1) : 0; }
 #endif
 
@@ -254,7 +254,7 @@ typedef void(randomx::JitCompilerX86::* InstructionGeneratorX86_2)(const randomx
 		memcpy(randomx::JitCompilerX86::engine + k, &p, sizeof(p)); \
 	} while (0)
 
-#elif defined(XMRIG_ARMv8)
+#elif (XMRIG_ARM == 8)
 
 	Log2_ScratchpadL1 = Log2(ScratchpadL1_Size);
 	Log2_ScratchpadL2 = Log2(ScratchpadL2_Size);
diff --git a/src/crypto/randomx/randomx.h b/src/crypto/randomx/randomx.h
index 937a0844c..91efcb28a 100644
--- a/src/crypto/randomx/randomx.h
+++ b/src/crypto/randomx/randomx.h
@@ -132,13 +132,13 @@ struct RandomX_ConfigurationBase
 	uint32_t ScratchpadL3Mask_Calculated;
 	uint32_t ScratchpadL3Mask64_Calculated;
 
-#if defined(XMRIG_ARMv8)
+#	if (XMRIG_ARM == 8)
 	uint32_t Log2_ScratchpadL1;
 	uint32_t Log2_ScratchpadL2;
 	uint32_t Log2_ScratchpadL3;
 	uint32_t Log2_DatasetBaseSize;
 	uint32_t Log2_CacheSize;
-#endif
+#	endif
 };
 
 struct RandomX_ConfigurationMonero : public RandomX_ConfigurationBase {};

From 234de96784c50539e6286f4568e36a5e28b848ca Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Fri, 27 Aug 2021 18:51:59 +0700
Subject: [PATCH 09/20] Update rapidjson.

---
 src/3rdparty/rapidjson/allocators.h          | 520 ++++++++++--
 src/3rdparty/rapidjson/cursorstreamwrapper.h |   2 +-
 src/3rdparty/rapidjson/document.h            | 541 ++++++++++--
 src/3rdparty/rapidjson/encodedstream.h       |   2 +-
 src/3rdparty/rapidjson/encodings.h           |   2 +-
 src/3rdparty/rapidjson/error/en.h            |  50 +-
 src/3rdparty/rapidjson/error/error.h         |  57 +-
 src/3rdparty/rapidjson/filereadstream.h      |   2 +-
 src/3rdparty/rapidjson/filewritestream.h     |   2 +-
 src/3rdparty/rapidjson/fwd.h                 |   4 +-
 src/3rdparty/rapidjson/internal/biginteger.h |  19 +-
 src/3rdparty/rapidjson/internal/clzll.h      |  71 ++
 src/3rdparty/rapidjson/internal/diyfp.h      |  20 +-
 src/3rdparty/rapidjson/internal/dtoa.h       |  12 +-
 src/3rdparty/rapidjson/internal/ieee754.h    |   2 +-
 src/3rdparty/rapidjson/internal/itoa.h       |   2 +-
 src/3rdparty/rapidjson/internal/meta.h       |   2 +-
 src/3rdparty/rapidjson/internal/pow10.h      |   2 +-
 src/3rdparty/rapidjson/internal/regex.h      |   9 +-
 src/3rdparty/rapidjson/internal/stack.h      |   2 +-
 src/3rdparty/rapidjson/internal/strfunc.h    |  16 +-
 src/3rdparty/rapidjson/internal/strtod.h     |  17 +-
 src/3rdparty/rapidjson/internal/swap.h       |   2 +-
 src/3rdparty/rapidjson/istreamwrapper.h      |   2 +-
 src/3rdparty/rapidjson/memorybuffer.h        |   2 +-
 src/3rdparty/rapidjson/memorystream.h        |   2 +-
 src/3rdparty/rapidjson/ostreamwrapper.h      |   2 +-
 src/3rdparty/rapidjson/pointer.h             |  80 +-
 src/3rdparty/rapidjson/prettywriter.h        |   6 +-
 src/3rdparty/rapidjson/rapidjson.h           | 107 ++-
 src/3rdparty/rapidjson/reader.h              | 114 +--
 src/3rdparty/rapidjson/readme.md             | 210 +++++
 src/3rdparty/rapidjson/schema.h              | 822 +++++++++++++------
 src/3rdparty/rapidjson/stream.h              |   2 +-
 src/3rdparty/rapidjson/stringbuffer.h        |   2 +-
 src/3rdparty/rapidjson/uri.h                 | 466 +++++++++++
 src/3rdparty/rapidjson/writer.h              |  17 +-
 src/base/io/json/JsonChain.cpp               |   1 -
 src/base/io/json/JsonChain.h                 |   4 +-
 src/base/io/json/JsonRequest.cpp             |   5 +-
 src/base/io/json/JsonRequest.h               |   4 +-
 src/base/io/json/Json_unix.cpp               |   5 +-
 src/base/io/json/Json_win.cpp                |   5 +-
 src/base/kernel/interfaces/IJsonReader.h     |   4 +-
 src/base/kernel/interfaces/ILogBackend.h     |   4 +-
 src/base/tools/Chrono.h                      |   4 +-
 src/base/tools/Object.h                      |   4 +-
 src/base/tools/String.cpp                    |   5 +-
 src/base/tools/String.h                      |   4 +-
 49 files changed, 2673 insertions(+), 568 deletions(-)
 create mode 100644 src/3rdparty/rapidjson/internal/clzll.h
 create mode 100644 src/3rdparty/rapidjson/readme.md
 create mode 100644 src/3rdparty/rapidjson/uri.h

diff --git a/src/3rdparty/rapidjson/allocators.h b/src/3rdparty/rapidjson/allocators.h
index cc67c8971..12bc5bafc 100644
--- a/src/3rdparty/rapidjson/allocators.h
+++ b/src/3rdparty/rapidjson/allocators.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -16,6 +16,13 @@
 #define RAPIDJSON_ALLOCATORS_H_
 
 #include "rapidjson.h"
+#include "internal/meta.h"
+
+#include <memory>
+
+#if RAPIDJSON_HAS_CXX11
+#include <type_traits>
+#endif
 
 RAPIDJSON_NAMESPACE_BEGIN
 
@@ -77,19 +84,26 @@ public:
     static const bool kNeedFree = true;
     void* Malloc(size_t size) { 
         if (size) //  behavior of malloc(0) is implementation defined.
-            return std::malloc(size);
+            return RAPIDJSON_MALLOC(size);
         else
             return NULL; // standardize to returning NULL.
     }
     void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
         (void)originalSize;
         if (newSize == 0) {
-            std::free(originalPtr);
+            RAPIDJSON_FREE(originalPtr);
             return NULL;
         }
-        return std::realloc(originalPtr, newSize);
+        return RAPIDJSON_REALLOC(originalPtr, newSize);
+    }
+    static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); }
+
+    bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
+        return true;
+    }
+    bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
+        return false;
     }
-    static void Free(void *ptr) { std::free(ptr); }
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -113,16 +127,64 @@ public:
 */
 template <typename BaseAllocator = CrtAllocator>
 class MemoryPoolAllocator {
+    //! Chunk header for perpending to each chunk.
+    /*! Chunks are stored as a singly linked list.
+    */
+    struct ChunkHeader {
+        size_t capacity;    //!< Capacity of the chunk in bytes (excluding the header itself).
+        size_t size;        //!< Current size of allocated memory in bytes.
+        ChunkHeader *next;  //!< Next chunk in the linked list.
+    };
+
+    struct SharedData {
+        ChunkHeader *chunkHead;  //!< Head of the chunk linked-list. Only the head chunk serves allocation.
+        BaseAllocator* ownBaseAllocator; //!< base allocator created by this object.
+        size_t refcount;
+        bool ownBuffer;
+    };
+
+    static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData));
+    static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader));
+
+    static inline ChunkHeader *GetChunkHead(SharedData *shared)
+    {
+        return reinterpret_cast<ChunkHeader*>(reinterpret_cast<uint8_t*>(shared) + SIZEOF_SHARED_DATA);
+    }
+    static inline uint8_t *GetChunkBuffer(SharedData *shared)
+    {
+        return reinterpret_cast<uint8_t*>(shared->chunkHead) + SIZEOF_CHUNK_HEADER;
+    }
+
+    static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
+
 public:
     static const bool kNeedFree = false;    //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
+    static const bool kRefCounted = true;   //!< Tell users that this allocator is reference counted on copy
 
     //! Constructor with chunkSize.
     /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
         \param baseAllocator The allocator for allocating memory chunks.
     */
+    explicit
     MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : 
-        chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
+        chunk_capacity_(chunkSize),
+        baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()),
+        shared_(static_cast<SharedData*>(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0))
     {
+        RAPIDJSON_ASSERT(baseAllocator_ != 0);
+        RAPIDJSON_ASSERT(shared_ != 0);
+        if (baseAllocator) {
+            shared_->ownBaseAllocator = 0;
+        }
+        else {
+            shared_->ownBaseAllocator = baseAllocator_;
+        }
+        shared_->chunkHead = GetChunkHead(shared_);
+        shared_->chunkHead->capacity = 0;
+        shared_->chunkHead->size = 0;
+        shared_->chunkHead->next = 0;
+        shared_->ownBuffer = true;
+        shared_->refcount = 1;
     }
 
     //! Constructor with user-supplied buffer.
@@ -136,41 +198,101 @@ public:
         \param baseAllocator The allocator for allocating memory chunks.
     */
     MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
-        chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
+        chunk_capacity_(chunkSize),
+        baseAllocator_(baseAllocator),
+        shared_(static_cast<SharedData*>(AlignBuffer(buffer, size)))
     {
-        RAPIDJSON_ASSERT(buffer != 0);
-        RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
-        chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
-        chunkHead_->capacity = size - sizeof(ChunkHeader);
-        chunkHead_->size = 0;
-        chunkHead_->next = 0;
+        RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER);
+        shared_->chunkHead = GetChunkHead(shared_);
+        shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER;
+        shared_->chunkHead->size = 0;
+        shared_->chunkHead->next = 0;
+        shared_->ownBaseAllocator = 0;
+        shared_->ownBuffer = false;
+        shared_->refcount = 1;
     }
 
+    MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT :
+        chunk_capacity_(rhs.chunk_capacity_),
+        baseAllocator_(rhs.baseAllocator_),
+        shared_(rhs.shared_)
+    {
+        RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+        ++shared_->refcount;
+    }
+    MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT
+    {
+        RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
+        ++rhs.shared_->refcount;
+        this->~MemoryPoolAllocator();
+        baseAllocator_ = rhs.baseAllocator_;
+        chunk_capacity_ = rhs.chunk_capacity_;
+        shared_ = rhs.shared_;
+        return *this;
+    }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT :
+        chunk_capacity_(rhs.chunk_capacity_),
+        baseAllocator_(rhs.baseAllocator_),
+        shared_(rhs.shared_)
+    {
+        RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
+        rhs.shared_ = 0;
+    }
+    MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT
+    {
+        RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
+        this->~MemoryPoolAllocator();
+        baseAllocator_ = rhs.baseAllocator_;
+        chunk_capacity_ = rhs.chunk_capacity_;
+        shared_ = rhs.shared_;
+        rhs.shared_ = 0;
+        return *this;
+    }
+#endif
+
     //! Destructor.
     /*! This deallocates all memory chunks, excluding the user-supplied buffer.
     */
-    ~MemoryPoolAllocator() {
+    ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT {
+        if (!shared_) {
+            // do nothing if moved
+            return;
+        }
+        if (shared_->refcount > 1) {
+            --shared_->refcount;
+            return;
+        }
         Clear();
-        RAPIDJSON_DELETE(ownBaseAllocator_);
+        BaseAllocator *a = shared_->ownBaseAllocator;
+        if (shared_->ownBuffer) {
+            baseAllocator_->Free(shared_);
+        }
+        RAPIDJSON_DELETE(a);
     }
 
-    //! Deallocates all memory chunks, excluding the user-supplied buffer.
-    void Clear() {
-        while (chunkHead_ && chunkHead_ != userBuffer_) {
-            ChunkHeader* next = chunkHead_->next;
-            baseAllocator_->Free(chunkHead_);
-            chunkHead_ = next;
+    //! Deallocates all memory chunks, excluding the first/user one.
+    void Clear() RAPIDJSON_NOEXCEPT {
+        RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+        for (;;) {
+            ChunkHeader* c = shared_->chunkHead;
+            if (!c->next) {
+                break;
+            }
+            shared_->chunkHead = c->next;
+            baseAllocator_->Free(c);
         }
-        if (chunkHead_ && chunkHead_ == userBuffer_)
-            chunkHead_->size = 0; // Clear user buffer
+        shared_->chunkHead->size = 0;
     }
 
     //! Computes the total capacity of allocated memory chunks.
     /*! \return total capacity in bytes.
     */
-    size_t Capacity() const {
+    size_t Capacity() const RAPIDJSON_NOEXCEPT {
+        RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
         size_t capacity = 0;
-        for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
+        for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
             capacity += c->capacity;
         return capacity;
     }
@@ -178,25 +300,35 @@ public:
     //! Computes the memory blocks allocated.
     /*! \return total used bytes.
     */
-    size_t Size() const {
+    size_t Size() const RAPIDJSON_NOEXCEPT {
+        RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
         size_t size = 0;
-        for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
+        for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
             size += c->size;
         return size;
     }
 
+    //! Whether the allocator is shared.
+    /*! \return true or false.
+    */
+    bool Shared() const RAPIDJSON_NOEXCEPT {
+        RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+        return shared_->refcount > 1;
+    }
+
     //! Allocates a memory block. (concept Allocator)
     void* Malloc(size_t size) {
+        RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
         if (!size)
             return NULL;
 
         size = RAPIDJSON_ALIGN(size);
-        if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
+        if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity))
             if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
                 return NULL;
 
-        void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
-        chunkHead_->size += size;
+        void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size;
+        shared_->chunkHead->size += size;
         return buffer;
     }
 
@@ -205,6 +337,7 @@ public:
         if (originalPtr == 0)
             return Malloc(newSize);
 
+        RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
         if (newSize == 0)
             return NULL;
 
@@ -216,10 +349,10 @@ public:
             return originalPtr;
 
         // Simply expand it if it is the last allocation and there is sufficient space
-        if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
+        if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) {
             size_t increment = static_cast<size_t>(newSize - originalSize);
-            if (chunkHead_->size + increment <= chunkHead_->capacity) {
-                chunkHead_->size += increment;
+            if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) {
+                shared_->chunkHead->size += increment;
                 return originalPtr;
             }
         }
@@ -235,50 +368,325 @@ public:
     }
 
     //! Frees a memory block (concept Allocator)
-    static void Free(void *ptr) { (void)ptr; } // Do nothing
+    static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing
+
+    //! Compare (equality) with another MemoryPoolAllocator
+    bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
+        RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+        RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
+        return shared_ == rhs.shared_;
+    }
+    //! Compare (inequality) with another MemoryPoolAllocator
+    bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
+        return !operator==(rhs);
+    }
 
 private:
-    //! Copy constructor is not permitted.
-    MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
-    //! Copy assignment operator is not permitted.
-    MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
-
     //! Creates a new chunk.
     /*! \param capacity Capacity of the chunk in bytes.
         \return true if success.
     */
     bool AddChunk(size_t capacity) {
         if (!baseAllocator_)
-            ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
-        if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
+            shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
+        if (ChunkHeader* chunk = static_cast<ChunkHeader*>(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) {
             chunk->capacity = capacity;
             chunk->size = 0;
-            chunk->next = chunkHead_;
-            chunkHead_ =  chunk;
+            chunk->next = shared_->chunkHead;
+            shared_->chunkHead = chunk;
             return true;
         }
         else
             return false;
     }
 
-    static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
+    static inline void* AlignBuffer(void* buf, size_t &size)
+    {
+        RAPIDJSON_NOEXCEPT_ASSERT(buf != 0);
+        const uintptr_t mask = sizeof(void*) - 1;
+        const uintptr_t ubuf = reinterpret_cast<uintptr_t>(buf);
+        if (RAPIDJSON_UNLIKELY(ubuf & mask)) {
+            const uintptr_t abuf = (ubuf + mask) & ~mask;
+            RAPIDJSON_ASSERT(size >= abuf - ubuf);
+            buf = reinterpret_cast<void*>(abuf);
+            size -= abuf - ubuf;
+        }
+        return buf;
+    }
 
-    //! Chunk header for perpending to each chunk.
-    /*! Chunks are stored as a singly linked list.
-    */
-    struct ChunkHeader {
-        size_t capacity;    //!< Capacity of the chunk in bytes (excluding the header itself).
-        size_t size;        //!< Current size of allocated memory in bytes.
-        ChunkHeader *next;  //!< Next chunk in the linked list.
+    size_t chunk_capacity_;     //!< The minimum capacity of chunk when they are allocated.
+    BaseAllocator* baseAllocator_;  //!< base allocator for allocating memory chunks.
+    SharedData *shared_;        //!< The shared data of the allocator
+};
+
+namespace internal {
+    template<typename, typename = void>
+    struct IsRefCounted :
+        public FalseType
+    { };
+    template<typename T>
+    struct IsRefCounted<T, typename internal::EnableIfCond<T::kRefCounted>::Type> :
+        public TrueType
+    { };
+}
+
+template<typename T, typename A>
+inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n)
+{
+    RAPIDJSON_NOEXCEPT_ASSERT(old_n <= SIZE_MAX / sizeof(T) && new_n <= SIZE_MAX / sizeof(T));
+    return static_cast<T*>(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T)));
+}
+
+template<typename T, typename A>
+inline T *Malloc(A& a, size_t n = 1)
+{
+    return Realloc<T, A>(a, NULL, 0, n);
+}
+
+template<typename T, typename A>
+inline void Free(A& a, T *p, size_t n = 1)
+{
+    static_cast<void>(Realloc<T, A>(a, p, n, 0));
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited
+#endif
+
+template <typename T, typename BaseAllocator = CrtAllocator>
+class StdAllocator :
+    public std::allocator<T>
+{
+    typedef std::allocator<T> allocator_type;
+#if RAPIDJSON_HAS_CXX11
+    typedef std::allocator_traits<allocator_type> traits_type;
+#else
+    typedef allocator_type traits_type;
+#endif
+
+public:
+    typedef BaseAllocator BaseAllocatorType;
+
+    StdAllocator() RAPIDJSON_NOEXCEPT :
+        allocator_type(),
+        baseAllocator_()
+    { }
+
+    StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
+        allocator_type(rhs),
+        baseAllocator_(rhs.baseAllocator_)
+    { }
+
+    template<typename U>
+    StdAllocator(const StdAllocator<U, BaseAllocator>& rhs) RAPIDJSON_NOEXCEPT :
+        allocator_type(rhs),
+        baseAllocator_(rhs.baseAllocator_)
+    { }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT :
+        allocator_type(std::move(rhs)),
+        baseAllocator_(std::move(rhs.baseAllocator_))
+    { }
+#endif
+#if RAPIDJSON_HAS_CXX11
+    using propagate_on_container_move_assignment = std::true_type;
+    using propagate_on_container_swap = std::true_type;
+#endif
+
+    /* implicit */
+    StdAllocator(const BaseAllocator& allocator) RAPIDJSON_NOEXCEPT :
+        allocator_type(),
+        baseAllocator_(allocator)
+    { }
+
+    ~StdAllocator() RAPIDJSON_NOEXCEPT
+    { }
+
+    template<typename U>
+    struct rebind {
+        typedef StdAllocator<U, BaseAllocator> other;
     };
 
-    ChunkHeader *chunkHead_;    //!< Head of the chunk linked-list. Only the head chunk serves allocation.
-    size_t chunk_capacity_;     //!< The minimum capacity of chunk when they are allocated.
-    void *userBuffer_;          //!< User supplied buffer.
-    BaseAllocator* baseAllocator_;  //!< base allocator for allocating memory chunks.
-    BaseAllocator* ownBaseAllocator_;   //!< base allocator created by this object.
+    typedef typename traits_type::size_type         size_type;
+    typedef typename traits_type::difference_type   difference_type;
+
+    typedef typename traits_type::value_type        value_type;
+    typedef typename traits_type::pointer           pointer;
+    typedef typename traits_type::const_pointer     const_pointer;
+
+#if RAPIDJSON_HAS_CXX11
+
+    typedef typename std::add_lvalue_reference<value_type>::type &reference;
+    typedef typename std::add_lvalue_reference<typename std::add_const<value_type>::type>::type &const_reference;
+
+    pointer address(reference r) const RAPIDJSON_NOEXCEPT
+    {
+        return std::addressof(r);
+    }
+    const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
+    {
+        return std::addressof(r);
+    }
+
+    size_type max_size() const RAPIDJSON_NOEXCEPT
+    {
+        return traits_type::max_size(*this);
+    }
+
+    template <typename ...Args>
+    void construct(pointer p, Args&&... args)
+    {
+        traits_type::construct(*this, p, std::forward<Args>(args)...);
+    }
+    void destroy(pointer p)
+    {
+        traits_type::destroy(*this, p);
+    }
+
+#else // !RAPIDJSON_HAS_CXX11
+
+    typedef typename allocator_type::reference       reference;
+    typedef typename allocator_type::const_reference const_reference;
+
+    pointer address(reference r) const RAPIDJSON_NOEXCEPT
+    {
+        return allocator_type::address(r);
+    }
+    const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
+    {
+        return allocator_type::address(r);
+    }
+
+    size_type max_size() const RAPIDJSON_NOEXCEPT
+    {
+        return allocator_type::max_size();
+    }
+
+    void construct(pointer p, const_reference r)
+    {
+        allocator_type::construct(p, r);
+    }
+    void destroy(pointer p)
+    {
+        allocator_type::destroy(p);
+    }
+
+#endif // !RAPIDJSON_HAS_CXX11
+
+    template <typename U>
+    U* allocate(size_type n = 1, const void* = 0)
+    {
+        return RAPIDJSON_NAMESPACE::Malloc<U>(baseAllocator_, n);
+    }
+    template <typename U>
+    void deallocate(U* p, size_type n = 1)
+    {
+        RAPIDJSON_NAMESPACE::Free<U>(baseAllocator_, p, n);
+    }
+
+    pointer allocate(size_type n = 1, const void* = 0)
+    {
+        return allocate<value_type>(n);
+    }
+    void deallocate(pointer p, size_type n = 1)
+    {
+        deallocate<value_type>(p, n);
+    }
+
+#if RAPIDJSON_HAS_CXX11
+    using is_always_equal = std::is_empty<BaseAllocator>;
+#endif
+
+    template<typename U>
+    bool operator==(const StdAllocator<U, BaseAllocator>& rhs) const RAPIDJSON_NOEXCEPT
+    {
+        return baseAllocator_ == rhs.baseAllocator_;
+    }
+    template<typename U>
+    bool operator!=(const StdAllocator<U, BaseAllocator>& rhs) const RAPIDJSON_NOEXCEPT
+    {
+        return !operator==(rhs);
+    }
+
+    //! rapidjson Allocator concept
+    static const bool kNeedFree = BaseAllocator::kNeedFree;
+    static const bool kRefCounted = internal::IsRefCounted<BaseAllocator>::Value;
+    void* Malloc(size_t size)
+    {
+        return baseAllocator_.Malloc(size);
+    }
+    void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
+    {
+        return baseAllocator_.Realloc(originalPtr, originalSize, newSize);
+    }
+    static void Free(void *ptr) RAPIDJSON_NOEXCEPT
+    {
+        BaseAllocator::Free(ptr);
+    }
+
+private:
+    template <typename, typename>
+    friend class StdAllocator; // access to StdAllocator<!T>.*
+
+    BaseAllocator baseAllocator_;
 };
 
+#if !RAPIDJSON_HAS_CXX17 // std::allocator<void> deprecated in C++17
+template <typename BaseAllocator>
+class StdAllocator<void, BaseAllocator> :
+    public std::allocator<void>
+{
+    typedef std::allocator<void> allocator_type;
+
+public:
+    typedef BaseAllocator BaseAllocatorType;
+
+    StdAllocator() RAPIDJSON_NOEXCEPT :
+        allocator_type(),
+        baseAllocator_()
+    { }
+
+    StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
+        allocator_type(rhs),
+        baseAllocator_(rhs.baseAllocator_)
+    { }
+
+    template<typename U>
+    StdAllocator(const StdAllocator<U, BaseAllocator>& rhs) RAPIDJSON_NOEXCEPT :
+        allocator_type(rhs),
+        baseAllocator_(rhs.baseAllocator_)
+    { }
+
+    /* implicit */
+    StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT :
+        allocator_type(),
+        baseAllocator_(baseAllocator)
+    { }
+
+    ~StdAllocator() RAPIDJSON_NOEXCEPT
+    { }
+
+    template<typename U>
+    struct rebind {
+        typedef StdAllocator<U, BaseAllocator> other;
+    };
+
+    typedef typename allocator_type::value_type value_type;
+
+private:
+    template <typename, typename>
+    friend class StdAllocator; // access to StdAllocator<!T>.*
+
+    BaseAllocator baseAllocator_;
+};
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
 RAPIDJSON_NAMESPACE_END
 
 #endif // RAPIDJSON_ENCODINGS_H_
diff --git a/src/3rdparty/rapidjson/cursorstreamwrapper.h b/src/3rdparty/rapidjson/cursorstreamwrapper.h
index 52c11a7c0..fd6513db1 100644
--- a/src/3rdparty/rapidjson/cursorstreamwrapper.h
+++ b/src/3rdparty/rapidjson/cursorstreamwrapper.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 //
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/document.h b/src/3rdparty/rapidjson/document.h
index 9783fe4ac..e2cc60006 100644
--- a/src/3rdparty/rapidjson/document.h
+++ b/src/3rdparty/rapidjson/document.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -24,6 +24,9 @@
 #include "encodedstream.h"
 #include <new>      // placement new
 #include <limits>
+#ifdef __cpp_lib_three_way_comparison
+#include <compare>
+#endif
 
 RAPIDJSON_DIAG_PUSH
 #ifdef __clang__
@@ -39,12 +42,21 @@ RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible lo
 RAPIDJSON_DIAG_OFF(effc++)
 #endif // __GNUC__
 
+#ifdef GetObject
+// see https://github.com/Tencent/rapidjson/issues/1448
+// a former included windows.h might have defined a macro called GetObject, which affects
+// GetObject defined here. This ensures the macro does not get applied
+#pragma push_macro("GetObject")
+#define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED
+#undef GetObject
+#endif
+
 #ifndef RAPIDJSON_NOMEMBERITERATORCLASS
 #include <iterator> // std::random_access_iterator_tag
 #endif
 
-#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
-#include <utility> // std::move
+#if RAPIDJSON_USE_MEMBERSMAP
+#include <map> // std::multimap
 #endif
 
 RAPIDJSON_NAMESPACE_BEGIN
@@ -56,6 +68,48 @@ class GenericValue;
 template <typename Encoding, typename Allocator, typename StackAllocator>
 class GenericDocument;
 
+/*! \def RAPIDJSON_DEFAULT_ALLOCATOR
+    \ingroup RAPIDJSON_CONFIG
+    \brief Allows to choose default allocator.
+
+    User can define this to use CrtAllocator or MemoryPoolAllocator.
+*/
+#ifndef RAPIDJSON_DEFAULT_ALLOCATOR
+#define RAPIDJSON_DEFAULT_ALLOCATOR MemoryPoolAllocator<CrtAllocator>
+#endif
+
+/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR
+    \ingroup RAPIDJSON_CONFIG
+    \brief Allows to choose default stack allocator for Document.
+
+    User can define this to use CrtAllocator or MemoryPoolAllocator.
+*/
+#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR
+#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR CrtAllocator
+#endif
+
+/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY
+    \ingroup RAPIDJSON_CONFIG
+    \brief User defined kDefaultObjectCapacity value.
+
+    User can define this as any natural number.
+*/
+#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY
+// number of objects that rapidjson::Value allocates memory for by default
+#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16
+#endif
+
+/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY
+    \ingroup RAPIDJSON_CONFIG
+    \brief User defined kDefaultArrayCapacity value.
+
+    User can define this as any natural number.
+*/
+#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY
+// number of array elements that rapidjson::Value allocates memory for by default
+#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16
+#endif
+
 //! Name-value pair in a JSON object value.
 /*!
     This class was internal to GenericValue. It used to be a inner struct.
@@ -63,15 +117,45 @@ class GenericDocument;
     https://code.google.com/p/rapidjson/issues/detail?id=64
 */
 template <typename Encoding, typename Allocator> 
-struct GenericMember { 
+class GenericMember {
+public:
     GenericValue<Encoding, Allocator> name;     //!< name of member (must be a string)
     GenericValue<Encoding, Allocator> value;    //!< value of member.
 
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+    //! Move constructor in C++11
+    GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT
+        : name(std::move(rhs.name)),
+          value(std::move(rhs.value))
+    {
+    }
+
+    //! Move assignment in C++11
+    GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT {
+        return *this = static_cast<GenericMember&>(rhs);
+    }
+#endif
+
+    //! Assignment with move semantics.
+    /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment.
+    */
+    GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT {
+        if (RAPIDJSON_LIKELY(this != &rhs)) {
+            name = rhs.name;
+            value = rhs.value;
+        }
+        return *this;
+    }
+
     // swap() for std::sort() and other potential use in STL.
     friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT {
         a.name.Swap(b.name);
         a.value.Swap(b.value);
     }
+
+private:
+    //! Copy constructor is not permitted.
+    GenericMember(const GenericMember& rhs);
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -175,12 +259,16 @@ public:
 
     //! @name relations
     //@{
-    bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; }
-    bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; }
-    bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; }
-    bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; }
-    bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; }
-    bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; }
+    template <bool Const_> bool operator==(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ == that.ptr_; }
+    template <bool Const_> bool operator!=(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ != that.ptr_; }
+    template <bool Const_> bool operator<=(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ <= that.ptr_; }
+    template <bool Const_> bool operator>=(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ >= that.ptr_; }
+    template <bool Const_> bool operator< (const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ < that.ptr_; }
+    template <bool Const_> bool operator> (const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ > that.ptr_; }
+
+#ifdef __cpp_lib_three_way_comparison
+    template <bool Const_> std::strong_ordering operator<=>(const GenericMemberIterator<Const_, Encoding, Allocator>& that) const { return ptr_ <=> that.ptr_; }
+#endif
     //@}
 
     //! @name dereference
@@ -210,12 +298,14 @@ class GenericMemberIterator;
 //! non-const GenericMemberIterator
 template <typename Encoding, typename Allocator>
 class GenericMemberIterator<false,Encoding,Allocator> {
+public:
     //! use plain pointer as iterator type
     typedef GenericMember<Encoding,Allocator>* Iterator;
 };
 //! const GenericMemberIterator
 template <typename Encoding, typename Allocator>
 class GenericMemberIterator<true,Encoding,Allocator> {
+public:
     //! use plain const pointer as iterator type
     typedef const GenericMember<Encoding,Allocator>* Iterator;
 };
@@ -574,7 +664,7 @@ template <bool, typename> class GenericObject;
     \tparam Encoding    Encoding of the value. (Even non-string values need to have the same encoding in a document)
     \tparam Allocator   Allocator type for allocating memory of object, array and string.
 */
-template <typename Encoding, typename Allocator = MemoryPoolAllocator<> > 
+template <typename Encoding, typename Allocator = RAPIDJSON_DEFAULT_ALLOCATOR >
 class GenericValue {
 public:
     //! Name-value pair in an object.
@@ -651,18 +741,8 @@ public:
     template <typename SourceAllocator>
     GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator, bool copyConstStrings = false) {
         switch (rhs.GetType()) {
-        case kObjectType: {
-                SizeType count = rhs.data_.o.size;
-                Member* lm = reinterpret_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
-                const typename GenericValue<Encoding,SourceAllocator>::Member* rm = rhs.GetMembersPointer();
-                for (SizeType i = 0; i < count; i++) {
-                    new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings);
-                    new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings);
-                }
-                data_.f.flags = kObjectFlag;
-                data_.o.size = data_.o.capacity = count;
-                SetMembersPointer(lm);
-            }
+        case kObjectType:
+            DoCopyMembers(rhs, allocator, copyConstStrings);
             break;
         case kArrayType: {
                 SizeType count = rhs.data_.a.size;
@@ -798,25 +878,30 @@ public:
     /*! Need to destruct elements of array, members of object, or copy-string.
     */
     ~GenericValue() {
-        if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
+        // With RAPIDJSON_USE_MEMBERSMAP, the maps need to be destroyed to release
+        // their Allocator if it's refcounted (e.g. MemoryPoolAllocator).
+        if (Allocator::kNeedFree || (RAPIDJSON_USE_MEMBERSMAP+0 &&
+                                     internal::IsRefCounted<Allocator>::Value)) {
             switch(data_.f.flags) {
             case kArrayFlag:
                 {
                     GenericValue* e = GetElementsPointer();
                     for (GenericValue* v = e; v != e + data_.a.size; ++v)
                         v->~GenericValue();
-                    Allocator::Free(e);
+                    if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
+                        Allocator::Free(e);
+                    }
                 }
                 break;
 
             case kObjectFlag:
-                for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
-                    m->~Member();
-                Allocator::Free(GetMembersPointer());
+                DoFreeMembers();
                 break;
 
             case kCopyStringFlag:
-                Allocator::Free(const_cast<Ch*>(GetStringPointer()));
+                if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
+                    Allocator::Free(const_cast<Ch*>(GetStringPointer()));
+                }
                 break;
 
             default:
@@ -835,8 +920,13 @@ public:
     */
     GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
         if (RAPIDJSON_LIKELY(this != &rhs)) {
+            // Can't destroy "this" before assigning "rhs", otherwise "rhs"
+            // could be used after free if it's an sub-Value of "this",
+            // hence the temporary danse.
+            GenericValue temp;
+            temp.RawAssign(rhs);
             this->~GenericValue();
-            RawAssign(rhs);
+            RawAssign(temp);
         }
         return *this;
     }
@@ -1002,6 +1092,7 @@ public:
      */
     template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); }
 
+#ifndef __cpp_lib_three_way_comparison
     //! Equal-to operator with arbitrary types (symmetric version)
     /*! \return (rhs == lhs)
      */
@@ -1012,6 +1103,7 @@ public:
      */
     template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); }
     //@}
+#endif
 
     //!@name Type
     //@{
@@ -1177,10 +1269,7 @@ public:
     */
     GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) {
         RAPIDJSON_ASSERT(IsObject());
-        if (newCapacity > data_.o.capacity) {
-            SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member))));
-            data_.o.capacity = newCapacity;
-        }
+        DoReserveMembers(newCapacity, allocator);
         return *this;
     }
 
@@ -1254,11 +1343,7 @@ public:
     MemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) {
         RAPIDJSON_ASSERT(IsObject());
         RAPIDJSON_ASSERT(name.IsString());
-        MemberIterator member = MemberBegin();
-        for ( ; member != MemberEnd(); ++member)
-            if (name.StringEqual(member->name))
-                break;
-        return member;
+        return DoFindMember(name);
     }
     template <typename SourceAllocator> ConstMemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
 
@@ -1287,14 +1372,7 @@ public:
     GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
         RAPIDJSON_ASSERT(IsObject());
         RAPIDJSON_ASSERT(name.IsString());
-
-        ObjectData& o = data_.o;
-        if (o.size >= o.capacity)
-            MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator);
-        Member* members = GetMembersPointer();
-        members[o.size].name.RawAssign(name);
-        members[o.size].value.RawAssign(value);
-        o.size++;
+        DoAddMember(name, value, allocator);
         return *this;
     }
 
@@ -1428,9 +1506,7 @@ public:
     */
     void RemoveAllMembers() {
         RAPIDJSON_ASSERT(IsObject()); 
-        for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
-            m->~Member();
-        data_.o.size = 0;
+        DoClearMembers();
     }
 
     //! Remove a member in object by its name.
@@ -1474,14 +1550,7 @@ public:
         RAPIDJSON_ASSERT(data_.o.size > 0);
         RAPIDJSON_ASSERT(GetMembersPointer() != 0);
         RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd());
-
-        MemberIterator last(GetMembersPointer() + (data_.o.size - 1));
-        if (data_.o.size > 1 && m != last)
-            *m = *last; // Move the last one to this place
-        else
-            m->~Member(); // Only one left, just destroy
-        --data_.o.size;
-        return m;
+        return DoRemoveMember(m);
     }
 
     //! Remove a member from an object by iterator.
@@ -1513,13 +1582,7 @@ public:
         RAPIDJSON_ASSERT(first >= MemberBegin());
         RAPIDJSON_ASSERT(first <= last);
         RAPIDJSON_ASSERT(last <= MemberEnd());
-
-        MemberIterator pos = MemberBegin() + (first - MemberBegin());
-        for (MemberIterator itr = pos; itr != last; ++itr)
-            itr->~Member();
-        std::memmove(static_cast<void*>(&*pos), &*last, static_cast<size_t>(MemberEnd() - last) * sizeof(Member));
-        data_.o.size -= static_cast<SizeType>(last - first);
-        return pos;
+        return DoEraseMembers(first, last);
     }
 
     //! Erase a member in object by its name.
@@ -1548,7 +1611,9 @@ public:
     }
 
     Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
+    Object GetObj() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
     ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
+    ConstObject GetObj() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
 
     //@}
 
@@ -1770,12 +1835,12 @@ public:
     //!@name String
     //@{
 
-    const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
+    const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return DataString(data_); }
 
     //! Get the length of string.
     /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
     */
-    SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
+    SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return DataStringLength(data_); }
 
     //! Set this value as a string without copying source string.
     /*! This version has better performance with supplied length, and also support string containing null character.
@@ -1886,7 +1951,7 @@ public:
         case kArrayType:
             if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
                 return false;
-            for (const GenericValue* v = Begin(); v != End(); ++v)
+            for (ConstValueIterator v = Begin(); v != End(); ++v)
                 if (RAPIDJSON_UNLIKELY(!v->Accept(handler)))
                     return false;
             return handler.EndArray(data_.a.size);
@@ -1922,25 +1987,26 @@ private:
 
         // Initial flags of different types.
         kNullFlag = kNullType,
-        kTrueFlag = kTrueType | kBoolFlag,
-        kFalseFlag = kFalseType | kBoolFlag,
-        kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag,
-        kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag,
-        kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag,
-        kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag,
-        kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag,
-        kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag,
-        kConstStringFlag = kStringType | kStringFlag,
-        kCopyStringFlag = kStringType | kStringFlag | kCopyFlag,
-        kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag,
+        // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types.
+        kTrueFlag = static_cast<int>(kTrueType) | static_cast<int>(kBoolFlag),
+        kFalseFlag = static_cast<int>(kFalseType) | static_cast<int>(kBoolFlag),
+        kNumberIntFlag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kIntFlag | kInt64Flag),
+        kNumberUintFlag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag),
+        kNumberInt64Flag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kInt64Flag),
+        kNumberUint64Flag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kUint64Flag),
+        kNumberDoubleFlag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kDoubleFlag),
+        kNumberAnyFlag = static_cast<int>(kNumberType) | static_cast<int>(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag),
+        kConstStringFlag = static_cast<int>(kStringType) | static_cast<int>(kStringFlag),
+        kCopyStringFlag = static_cast<int>(kStringType) | static_cast<int>(kStringFlag | kCopyFlag),
+        kShortStringFlag = static_cast<int>(kStringType) | static_cast<int>(kStringFlag | kCopyFlag | kInlineStrFlag),
         kObjectFlag = kObjectType,
         kArrayFlag = kArrayType,
 
         kTypeMask = 0x07
     };
 
-    static const SizeType kDefaultArrayCapacity = 16;
-    static const SizeType kDefaultObjectCapacity = 16;
+    static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY;
+    static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY;
 
     struct Flag {
 #if RAPIDJSON_48BITPOINTER_OPTIMIZATION
@@ -2023,6 +2089,13 @@ private:
         Flag f;
     };  // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION
 
+    static RAPIDJSON_FORCEINLINE const Ch* DataString(const Data& data) {
+        return (data.f.flags & kInlineStrFlag) ? data.ss.str : RAPIDJSON_GETPOINTER(Ch, data.s.str);
+    }
+    static RAPIDJSON_FORCEINLINE SizeType DataStringLength(const Data& data) {
+        return (data.f.flags & kInlineStrFlag) ? data.ss.GetLength() : data.s.length;
+    }
+
     RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); }
     RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); }
     RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); }
@@ -2030,6 +2103,286 @@ private:
     RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); }
     RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); }
 
+#if RAPIDJSON_USE_MEMBERSMAP
+
+    struct MapTraits {
+        struct Less {
+            bool operator()(const Data& s1, const Data& s2) const {
+                SizeType n1 = DataStringLength(s1), n2 = DataStringLength(s2);
+                int cmp = std::memcmp(DataString(s1), DataString(s2), sizeof(Ch) * (n1 < n2 ? n1 : n2));
+                return cmp < 0 || (cmp == 0 && n1 < n2);
+            }
+        };
+        typedef std::pair<const Data, SizeType> Pair;
+        typedef std::multimap<Data, SizeType, Less, StdAllocator<Pair, Allocator> > Map;
+        typedef typename Map::iterator Iterator;
+    };
+    typedef typename MapTraits::Map         Map;
+    typedef typename MapTraits::Less        MapLess;
+    typedef typename MapTraits::Pair        MapPair;
+    typedef typename MapTraits::Iterator    MapIterator;
+
+    //
+    // Layout of the members' map/array, re(al)located according to the needed capacity:
+    //
+    //    {Map*}<>{capacity}<>{Member[capacity]}<>{MapIterator[capacity]}
+    //
+    // (where <> stands for the RAPIDJSON_ALIGN-ment, if needed)
+    //
+
+    static RAPIDJSON_FORCEINLINE size_t GetMapLayoutSize(SizeType capacity) {
+        return RAPIDJSON_ALIGN(sizeof(Map*)) +
+               RAPIDJSON_ALIGN(sizeof(SizeType)) +
+               RAPIDJSON_ALIGN(capacity * sizeof(Member)) +
+               capacity * sizeof(MapIterator);
+    }
+
+    static RAPIDJSON_FORCEINLINE SizeType &GetMapCapacity(Map* &map) {
+        return *reinterpret_cast<SizeType*>(reinterpret_cast<uintptr_t>(&map) +
+                                            RAPIDJSON_ALIGN(sizeof(Map*)));
+    }
+
+    static RAPIDJSON_FORCEINLINE Member* GetMapMembers(Map* &map) {
+        return reinterpret_cast<Member*>(reinterpret_cast<uintptr_t>(&map) +
+                                         RAPIDJSON_ALIGN(sizeof(Map*)) +
+                                         RAPIDJSON_ALIGN(sizeof(SizeType)));
+    }
+
+    static RAPIDJSON_FORCEINLINE MapIterator* GetMapIterators(Map* &map) {
+        return reinterpret_cast<MapIterator*>(reinterpret_cast<uintptr_t>(&map) +
+                                              RAPIDJSON_ALIGN(sizeof(Map*)) +
+                                              RAPIDJSON_ALIGN(sizeof(SizeType)) +
+                                              RAPIDJSON_ALIGN(GetMapCapacity(map) * sizeof(Member)));
+    }
+
+    static RAPIDJSON_FORCEINLINE Map* &GetMap(Member* members) {
+        RAPIDJSON_ASSERT(members != 0);
+        return *reinterpret_cast<Map**>(reinterpret_cast<uintptr_t>(members) -
+                                        RAPIDJSON_ALIGN(sizeof(SizeType)) -
+                                        RAPIDJSON_ALIGN(sizeof(Map*)));
+    }
+
+    // Some compilers' debug mechanisms want all iterators to be destroyed, for their accounting..
+    RAPIDJSON_FORCEINLINE MapIterator DropMapIterator(MapIterator& rhs) {
+#if RAPIDJSON_HAS_CXX11
+        MapIterator ret = std::move(rhs);
+#else
+        MapIterator ret = rhs;
+#endif
+        rhs.~MapIterator();
+        return ret;
+    }
+
+    Map* &DoReallocMap(Map** oldMap, SizeType newCapacity, Allocator& allocator) {
+        Map **newMap = static_cast<Map**>(allocator.Malloc(GetMapLayoutSize(newCapacity)));
+        GetMapCapacity(*newMap) = newCapacity;
+        if (!oldMap) {
+            *newMap = new (allocator.Malloc(sizeof(Map))) Map(MapLess(), allocator);
+        }
+        else {
+            *newMap = *oldMap;
+            size_t count = (*oldMap)->size();
+            std::memcpy(static_cast<void*>(GetMapMembers(*newMap)),
+                        static_cast<void*>(GetMapMembers(*oldMap)),
+                        count * sizeof(Member));
+            MapIterator *oldIt = GetMapIterators(*oldMap),
+                        *newIt = GetMapIterators(*newMap);
+            while (count--) {
+                new (&newIt[count]) MapIterator(DropMapIterator(oldIt[count]));
+            }
+            Allocator::Free(oldMap);
+        }
+        return *newMap;
+    }
+
+    RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) {
+        return GetMapMembers(DoReallocMap(0, capacity, allocator));
+    }
+
+    void DoReserveMembers(SizeType newCapacity, Allocator& allocator) {
+        ObjectData& o = data_.o;
+        if (newCapacity > o.capacity) {
+            Member* oldMembers = GetMembersPointer();
+            Map **oldMap = oldMembers ? &GetMap(oldMembers) : 0,
+                *&newMap = DoReallocMap(oldMap, newCapacity, allocator);
+            RAPIDJSON_SETPOINTER(Member, o.members, GetMapMembers(newMap));
+            o.capacity = newCapacity;
+        }
+    }
+
+    template <typename SourceAllocator>
+    MemberIterator DoFindMember(const GenericValue<Encoding, SourceAllocator>& name) {
+        if (Member* members = GetMembersPointer()) {
+            Map* &map = GetMap(members);
+            MapIterator mit = map->find(reinterpret_cast<const Data&>(name.data_));
+            if (mit != map->end()) {
+                return MemberIterator(&members[mit->second]);
+            }
+        }
+        return MemberEnd();
+    }
+
+    void DoClearMembers() {
+        if (Member* members = GetMembersPointer()) {
+            Map* &map = GetMap(members);
+            MapIterator* mit = GetMapIterators(map);
+            for (SizeType i = 0; i < data_.o.size; i++) {
+                map->erase(DropMapIterator(mit[i]));
+                members[i].~Member();
+            }
+            data_.o.size = 0;
+        }
+    }
+
+    void DoFreeMembers() {
+        if (Member* members = GetMembersPointer()) {
+            GetMap(members)->~Map();
+            for (SizeType i = 0; i < data_.o.size; i++) {
+                members[i].~Member();
+            }
+            if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
+                Map** map = &GetMap(members);
+                Allocator::Free(*map);
+                Allocator::Free(map);
+            }
+        }
+    }
+
+#else // !RAPIDJSON_USE_MEMBERSMAP
+
+    RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) {
+        return Malloc<Member>(allocator, capacity);
+    }
+
+    void DoReserveMembers(SizeType newCapacity, Allocator& allocator) {
+        ObjectData& o = data_.o;
+        if (newCapacity > o.capacity) {
+            Member* newMembers = Realloc<Member>(allocator, GetMembersPointer(), o.capacity, newCapacity);
+            RAPIDJSON_SETPOINTER(Member, o.members, newMembers);
+            o.capacity = newCapacity;
+        }
+    }
+
+    template <typename SourceAllocator>
+    MemberIterator DoFindMember(const GenericValue<Encoding, SourceAllocator>& name) {
+        MemberIterator member = MemberBegin();
+        for ( ; member != MemberEnd(); ++member)
+            if (name.StringEqual(member->name))
+                break;
+        return member;
+    }
+
+    void DoClearMembers() {
+        for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
+            m->~Member();
+        data_.o.size = 0;
+    }
+
+    void DoFreeMembers() {
+        for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
+            m->~Member();
+        Allocator::Free(GetMembersPointer());
+    }
+
+#endif // !RAPIDJSON_USE_MEMBERSMAP
+
+    void DoAddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
+        ObjectData& o = data_.o;
+        if (o.size >= o.capacity)
+            DoReserveMembers(o.capacity ? (o.capacity + (o.capacity + 1) / 2) : kDefaultObjectCapacity, allocator);
+        Member* members = GetMembersPointer();
+        Member* m = members + o.size;
+        m->name.RawAssign(name);
+        m->value.RawAssign(value);
+#if RAPIDJSON_USE_MEMBERSMAP
+        Map* &map = GetMap(members);
+        MapIterator* mit = GetMapIterators(map);
+        new (&mit[o.size]) MapIterator(map->insert(MapPair(m->name.data_, o.size)));
+#endif
+        ++o.size;
+    }
+
+    MemberIterator DoRemoveMember(MemberIterator m) {
+        ObjectData& o = data_.o;
+        Member* members = GetMembersPointer();
+#if RAPIDJSON_USE_MEMBERSMAP
+        Map* &map = GetMap(members);
+        MapIterator* mit = GetMapIterators(map);
+        SizeType mpos = static_cast<SizeType>(&*m - members);
+        map->erase(DropMapIterator(mit[mpos]));
+#endif
+        MemberIterator last(members + (o.size - 1));
+        if (o.size > 1 && m != last) {
+#if RAPIDJSON_USE_MEMBERSMAP
+            new (&mit[mpos]) MapIterator(DropMapIterator(mit[&*last - members]));
+            mit[mpos]->second = mpos;
+#endif
+            *m = *last; // Move the last one to this place
+        }
+        else {
+            m->~Member(); // Only one left, just destroy
+        }
+        --o.size;
+        return m;
+    }
+
+    MemberIterator DoEraseMembers(ConstMemberIterator first, ConstMemberIterator last) {
+        ObjectData& o = data_.o;
+        MemberIterator beg = MemberBegin(),
+                       pos = beg + (first - beg),
+                       end = MemberEnd();
+#if RAPIDJSON_USE_MEMBERSMAP
+        Map* &map = GetMap(GetMembersPointer());
+        MapIterator* mit = GetMapIterators(map);
+#endif
+        for (MemberIterator itr = pos; itr != last; ++itr) {
+#if RAPIDJSON_USE_MEMBERSMAP
+            map->erase(DropMapIterator(mit[itr - beg]));
+#endif
+            itr->~Member();
+        }
+#if RAPIDJSON_USE_MEMBERSMAP
+        if (first != last) {
+            // Move remaining members/iterators
+            MemberIterator next = pos + (last - first);
+            for (MemberIterator itr = pos; next != end; ++itr, ++next) {
+                std::memcpy(static_cast<void*>(&*itr), &*next, sizeof(Member));
+                SizeType mpos = static_cast<SizeType>(itr - beg);
+                new (&mit[mpos]) MapIterator(DropMapIterator(mit[next - beg]));
+                mit[mpos]->second = mpos;
+            }
+        }
+#else
+        std::memmove(static_cast<void*>(&*pos), &*last,
+                     static_cast<size_t>(end - last) * sizeof(Member));
+#endif
+        o.size -= static_cast<SizeType>(last - first);
+        return pos;
+    }
+
+    template <typename SourceAllocator>
+    void DoCopyMembers(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator, bool copyConstStrings) {
+        RAPIDJSON_ASSERT(rhs.GetType() == kObjectType);
+
+        data_.f.flags = kObjectFlag;
+        SizeType count = rhs.data_.o.size;
+        Member* lm = DoAllocMembers(count, allocator);
+        const typename GenericValue<Encoding,SourceAllocator>::Member* rm = rhs.GetMembersPointer();
+#if RAPIDJSON_USE_MEMBERSMAP
+        Map* &map = GetMap(lm);
+        MapIterator* mit = GetMapIterators(map);
+#endif
+        for (SizeType i = 0; i < count; i++) {
+            new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings);
+            new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings);
+#if RAPIDJSON_USE_MEMBERSMAP
+            new (&mit[i]) MapIterator(map->insert(MapPair(lm[i].name.data_, i)));
+#endif
+        }
+        data_.o.size = data_.o.capacity = count;
+        SetMembersPointer(lm);
+    }
+
     // Initialize this value as array with initial data, without calling destructor.
     void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
         data_.f.flags = kArrayFlag;
@@ -2047,9 +2400,16 @@ private:
     void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
         data_.f.flags = kObjectFlag;
         if (count) {
-            Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
+            Member* m = DoAllocMembers(count, allocator);
             SetMembersPointer(m);
             std::memcpy(static_cast<void*>(m), members, count * sizeof(Member));
+#if RAPIDJSON_USE_MEMBERSMAP
+            Map* &map = GetMap(m);
+            MapIterator* mit = GetMapIterators(map);
+            for (SizeType i = 0; i < count; i++) {
+                new (&mit[i]) MapIterator(map->insert(MapPair(m[i].name.data_, i)));
+            }
+#endif
         }
         else
             SetMembersPointer(0);
@@ -2120,7 +2480,7 @@ typedef GenericValue<UTF8<> > Value;
     \tparam StackAllocator Allocator for allocating memory for stack during parsing.
     \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor.  To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue.
 */
-template <typename Encoding, typename Allocator = MemoryPoolAllocator<>, typename StackAllocator = CrtAllocator>
+template <typename Encoding, typename Allocator = RAPIDJSON_DEFAULT_ALLOCATOR, typename StackAllocator = RAPIDJSON_DEFAULT_STACK_ALLOCATOR >
 class GenericDocument : public GenericValue<Encoding, Allocator> {
 public:
     typedef typename Encoding::Ch Ch;                       //!< Character type derived from Encoding.
@@ -2170,6 +2530,13 @@ public:
 #endif
 
     ~GenericDocument() {
+        // Clear the ::ValueType before ownAllocator is destroyed, ~ValueType()
+        // runs last and may access its elements or members which would be freed
+        // with an allocator like MemoryPoolAllocator (CrtAllocator does not
+        // free its data when destroyed, but MemoryPoolAllocator does).
+        if (ownAllocator_) {
+            ValueType::SetNull();
+        }
         Destroy();
     }
 
@@ -2505,6 +2872,7 @@ private:
 //! GenericDocument with UTF8 encoding
 typedef GenericDocument<UTF8<> > Document;
 
+
 //! Helper class for accessing Value of array type.
 /*!
     Instance of this helper class is obtained by \c GenericValue::GetArray().
@@ -2529,6 +2897,7 @@ public:
     GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; }
     ~GenericArray() {}
 
+    operator ValueType&() const { return value_; }
     SizeType Size() const { return value_.Size(); }
     SizeType Capacity() const { return value_.Capacity(); }
     bool Empty() const { return value_.Empty(); }
@@ -2584,6 +2953,7 @@ public:
     GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; }
     ~GenericObject() {}
 
+    operator ValueType&() const { return value_; }
     SizeType MemberCount() const { return value_.MemberCount(); }
     SizeType MemberCapacity() const { return value_.MemberCapacity(); }
     bool ObjectEmpty() const { return value_.ObjectEmpty(); }
@@ -2649,4 +3019,9 @@ private:
 RAPIDJSON_NAMESPACE_END
 RAPIDJSON_DIAG_POP
 
+#ifdef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED
+#pragma pop_macro("GetObject")
+#undef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED
+#endif
+
 #endif // RAPIDJSON_DOCUMENT_H_
diff --git a/src/3rdparty/rapidjson/encodedstream.h b/src/3rdparty/rapidjson/encodedstream.h
index 223601c05..cf046b892 100644
--- a/src/3rdparty/rapidjson/encodedstream.h
+++ b/src/3rdparty/rapidjson/encodedstream.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/encodings.h b/src/3rdparty/rapidjson/encodings.h
index 0b2446795..50ad18bdc 100644
--- a/src/3rdparty/rapidjson/encodings.h
+++ b/src/3rdparty/rapidjson/encodings.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/error/en.h b/src/3rdparty/rapidjson/error/en.h
index 2db838bff..5d2e57b7f 100644
--- a/src/3rdparty/rapidjson/error/en.h
+++ b/src/3rdparty/rapidjson/error/en.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -65,6 +65,54 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
     }
 }
 
+//! Maps error code of validation into error message.
+/*!
+    \ingroup RAPIDJSON_ERRORS
+    \param validateErrorCode Error code obtained from validator.
+    \return the error message.
+    \note User can make a copy of this function for localization.
+        Using switch-case is safer for future modification of error codes.
+*/
+inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) {
+    switch (validateErrorCode) {
+        case kValidateErrors:                           return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred");
+        case kValidateErrorNone:                        return RAPIDJSON_ERROR_STRING("No error.");
+
+        case kValidateErrorMultipleOf:                  return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'.");
+        case kValidateErrorMaximum:                     return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'.");
+        case kValidateErrorExclusiveMaximum:            return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'.");
+        case kValidateErrorMinimum:                     return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'.");
+        case kValidateErrorExclusiveMinimum:            return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'.");
+
+        case kValidateErrorMaxLength:                   return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'.");
+        case kValidateErrorMinLength:                   return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'.");
+        case kValidateErrorPattern:                     return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression.");
+
+        case kValidateErrorMaxItems:                    return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'.");
+        case kValidateErrorMinItems:                    return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'.");
+        case kValidateErrorUniqueItems:                 return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true.");
+        case kValidateErrorAdditionalItems:             return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema.");
+
+        case kValidateErrorMaxProperties:               return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'.");
+        case kValidateErrorMinProperties:               return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'.");
+        case kValidateErrorRequired:                    return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'.");
+        case kValidateErrorAdditionalProperties:        return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema.");
+        case kValidateErrorPatternProperties:           return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema.");
+        case kValidateErrorDependencies:                return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors.");
+
+        case kValidateErrorEnum:                        return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values.");
+        case kValidateErrorType:                        return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'.");
+
+        case kValidateErrorOneOf:                       return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors.");
+        case kValidateErrorOneOfMatch:                  return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf'.");
+        case kValidateErrorAllOf:                       return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors.");
+        case kValidateErrorAnyOf:                       return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors.");
+        case kValidateErrorNot:                         return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'.");
+
+        default:                                        return RAPIDJSON_ERROR_STRING("Unknown error.");
+    }
+}
+
 RAPIDJSON_NAMESPACE_END
 
 #ifdef __clang__
diff --git a/src/3rdparty/rapidjson/error/error.h b/src/3rdparty/rapidjson/error/error.h
index 9311d2f03..6270da11a 100644
--- a/src/3rdparty/rapidjson/error/error.h
+++ b/src/3rdparty/rapidjson/error/error.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -152,6 +152,61 @@ private:
 */
 typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
 
+///////////////////////////////////////////////////////////////////////////////
+// ValidateErrorCode
+
+//! Error codes when validating.
+/*! \ingroup RAPIDJSON_ERRORS
+    \see GenericSchemaValidator
+*/
+enum ValidateErrorCode {
+    kValidateErrors    = -1,                   //!< Top level error code when kValidateContinueOnErrorsFlag set.
+    kValidateErrorNone = 0,                    //!< No error.
+
+    kValidateErrorMultipleOf,                  //!< Number is not a multiple of the 'multipleOf' value.
+    kValidateErrorMaximum,                     //!< Number is greater than the 'maximum' value.
+    kValidateErrorExclusiveMaximum,            //!< Number is greater than or equal to the 'maximum' value.
+    kValidateErrorMinimum,                     //!< Number is less than the 'minimum' value.
+    kValidateErrorExclusiveMinimum,            //!< Number is less than or equal to the 'minimum' value.
+
+    kValidateErrorMaxLength,                   //!< String is longer than the 'maxLength' value.
+    kValidateErrorMinLength,                   //!< String is longer than the 'maxLength' value.
+    kValidateErrorPattern,                     //!< String does not match the 'pattern' regular expression.
+
+    kValidateErrorMaxItems,                    //!< Array is longer than the 'maxItems' value.
+    kValidateErrorMinItems,                    //!< Array is shorter than the 'minItems' value.
+    kValidateErrorUniqueItems,                 //!< Array has duplicate items but 'uniqueItems' is true.
+    kValidateErrorAdditionalItems,             //!< Array has additional items that are not allowed by the schema.
+
+    kValidateErrorMaxProperties,               //!< Object has more members than 'maxProperties' value.
+    kValidateErrorMinProperties,               //!< Object has less members than 'minProperties' value.
+    kValidateErrorRequired,                    //!< Object is missing one or more members required by the schema.
+    kValidateErrorAdditionalProperties,        //!< Object has additional members that are not allowed by the schema.
+    kValidateErrorPatternProperties,           //!< See other errors.
+    kValidateErrorDependencies,                //!< Object has missing property or schema dependencies.
+
+    kValidateErrorEnum,                        //!< Property has a value that is not one of its allowed enumerated values
+    kValidateErrorType,                        //!< Property has a type that is not allowed by the schema..
+
+    kValidateErrorOneOf,                       //!< Property did not match any of the sub-schemas specified by 'oneOf'.
+    kValidateErrorOneOfMatch,                  //!< Property matched more than one of the sub-schemas specified by 'oneOf'.
+    kValidateErrorAllOf,                       //!< Property did not match all of the sub-schemas specified by 'allOf'.
+    kValidateErrorAnyOf,                       //!< Property did not match any of the sub-schemas specified by 'anyOf'.
+    kValidateErrorNot                          //!< Property matched the sub-schema specified by 'not'.
+};
+
+//! Function pointer type of GetValidateError().
+/*! \ingroup RAPIDJSON_ERRORS
+
+    This is the prototype for \c GetValidateError_X(), where \c X is a locale.
+    User can dynamically change locale in runtime, e.g.:
+\code
+    GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever
+    const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode());
+\endcode
+*/
+typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode);
+
 RAPIDJSON_NAMESPACE_END
 
 #ifdef __clang__
diff --git a/src/3rdparty/rapidjson/filereadstream.h b/src/3rdparty/rapidjson/filereadstream.h
index 6b343707a..f8bb43cb0 100644
--- a/src/3rdparty/rapidjson/filereadstream.h
+++ b/src/3rdparty/rapidjson/filereadstream.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/filewritestream.h b/src/3rdparty/rapidjson/filewritestream.h
index 8b48fee19..5d89588c2 100644
--- a/src/3rdparty/rapidjson/filewritestream.h
+++ b/src/3rdparty/rapidjson/filewritestream.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/fwd.h b/src/3rdparty/rapidjson/fwd.h
index e8104e841..d62f77f0e 100644
--- a/src/3rdparty/rapidjson/fwd.h
+++ b/src/3rdparty/rapidjson/fwd.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -102,7 +102,7 @@ class PrettyWriter;
 // document.h
 
 template <typename Encoding, typename Allocator> 
-struct GenericMember;
+class GenericMember;
 
 template <bool Const, typename Encoding, typename Allocator>
 class GenericMemberIterator;
diff --git a/src/3rdparty/rapidjson/internal/biginteger.h b/src/3rdparty/rapidjson/internal/biginteger.h
index a31c8a88d..514a17694 100644
--- a/src/3rdparty/rapidjson/internal/biginteger.h
+++ b/src/3rdparty/rapidjson/internal/biginteger.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -17,7 +17,7 @@
 
 #include "../rapidjson.h"
 
-#if defined(_MSC_VER) && !__INTEL_COMPILER && defined(_M_AMD64)
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64)
 #include <intrin.h> // for _umul128
 #pragma intrinsic(_umul128)
 #endif
@@ -37,7 +37,8 @@ public:
         digits_[0] = u;
     }
 
-    BigInteger(const char* decimals, size_t length) : count_(1) {
+    template<typename Ch>
+    BigInteger(const Ch* decimals, size_t length) : count_(1) {
         RAPIDJSON_ASSERT(length > 0);
         digits_[0] = 0;
         size_t i = 0;
@@ -221,7 +222,8 @@ public:
     bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
 
 private:
-    void AppendDecimal64(const char* begin, const char* end) {
+    template<typename Ch>
+    void AppendDecimal64(const Ch* begin, const Ch* end) {
         uint64_t u = ParseUint64(begin, end);
         if (IsZero())
             *this = u;
@@ -236,11 +238,12 @@ private:
         digits_[count_++] = digit;
     }
 
-    static uint64_t ParseUint64(const char* begin, const char* end) {
+    template<typename Ch>
+    static uint64_t ParseUint64(const Ch* begin, const Ch* end) {
         uint64_t r = 0;
-        for (const char* p = begin; p != end; ++p) {
-            RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
-            r = r * 10u + static_cast<unsigned>(*p - '0');
+        for (const Ch* p = begin; p != end; ++p) {
+            RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9'));
+            r = r * 10u + static_cast<unsigned>(*p - Ch('0'));
         }
         return r;
     }
diff --git a/src/3rdparty/rapidjson/internal/clzll.h b/src/3rdparty/rapidjson/internal/clzll.h
new file mode 100644
index 000000000..8fc5118aa
--- /dev/null
+++ b/src/3rdparty/rapidjson/internal/clzll.h
@@ -0,0 +1,71 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_CLZLL_H_
+#define RAPIDJSON_CLZLL_H_
+
+#include "../rapidjson.h"
+
+#if defined(_MSC_VER) && !defined(UNDER_CE)
+#include <intrin.h>
+#if defined(_WIN64)
+#pragma intrinsic(_BitScanReverse64)
+#else
+#pragma intrinsic(_BitScanReverse)
+#endif
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+inline uint32_t clzll(uint64_t x) {
+    // Passing 0 to __builtin_clzll is UB in GCC and results in an
+    // infinite loop in the software implementation.
+    RAPIDJSON_ASSERT(x != 0);
+
+#if defined(_MSC_VER) && !defined(UNDER_CE)
+    unsigned long r = 0;
+#if defined(_WIN64)
+    _BitScanReverse64(&r, x);
+#else
+    // Scan the high 32 bits.
+    if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
+        return 63 - (r + 32);
+
+    // Scan the low 32 bits.
+    _BitScanReverse(&r, static_cast<uint32_t>(x & 0xFFFFFFFF));
+#endif // _WIN64
+
+    return 63 - r;
+#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll)
+    // __builtin_clzll wrapper
+    return static_cast<uint32_t>(__builtin_clzll(x));
+#else
+    // naive version
+    uint32_t r = 0;
+    while (!(x & (static_cast<uint64_t>(1) << 63))) {
+        x <<= 1;
+        ++r;
+    }
+
+    return r;
+#endif // _MSC_VER
+}
+
+#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_CLZLL_H_
diff --git a/src/3rdparty/rapidjson/internal/diyfp.h b/src/3rdparty/rapidjson/internal/diyfp.h
index b6c2cf561..a40797ec2 100644
--- a/src/3rdparty/rapidjson/internal/diyfp.h
+++ b/src/3rdparty/rapidjson/internal/diyfp.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 //
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -20,11 +20,11 @@
 #define RAPIDJSON_DIYFP_H_
 
 #include "../rapidjson.h"
+#include "clzll.h"
 #include <limits>
 
 #if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER)
 #include <intrin.h>
-#pragma intrinsic(_BitScanReverse64)
 #pragma intrinsic(_umul128)
 #endif
 
@@ -100,22 +100,8 @@ struct DiyFp {
     }
 
     DiyFp Normalize() const {
-        RAPIDJSON_ASSERT(f != 0); // https://stackoverflow.com/a/26809183/291737
-#if defined(_MSC_VER) && defined(_M_AMD64)
-        unsigned long index;
-        _BitScanReverse64(&index, f);
-        return DiyFp(f << (63 - index), e - (63 - index));
-#elif defined(__GNUC__) && __GNUC__ >= 4
-        int s = __builtin_clzll(f);
+        int s = static_cast<int>(clzll(f));
         return DiyFp(f << s, e - s);
-#else
-        DiyFp res = *this;
-        while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
-            res.f <<= 1;
-            res.e--;
-        }
-        return res;
-#endif
     }
 
     DiyFp NormalizeBoundary() const {
diff --git a/src/3rdparty/rapidjson/internal/dtoa.h b/src/3rdparty/rapidjson/internal/dtoa.h
index bf2e9b2e5..9f6ae3b3f 100644
--- a/src/3rdparty/rapidjson/internal/dtoa.h
+++ b/src/3rdparty/rapidjson/internal/dtoa.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -58,7 +58,11 @@ inline int CountDecimalDigit32(uint32_t n) {
 }
 
 inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
-    static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+    static const uint64_t kPow10[] = { 1U, 10U, 100U, 1000U, 10000U, 100000U, 1000000U, 10000000U, 100000000U,
+                                       1000000000U, 10000000000U, 100000000000U, 1000000000000U,
+                                       10000000000000U, 100000000000000U, 1000000000000000U,
+                                       10000000000000000U, 100000000000000000U, 1000000000000000000U,
+                                       10000000000000000000U };
     const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
     const DiyFp wp_w = Mp - W;
     uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
@@ -86,7 +90,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
         uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
         if (tmp <= delta) {
             *K += kappa;
-            GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
+            GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f);
             return;
         }
     }
@@ -103,7 +107,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
         if (p2 < delta) {
             *K += kappa;
             int index = -kappa;
-            GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0));
+            GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0));
             return;
         }
     }
diff --git a/src/3rdparty/rapidjson/internal/ieee754.h b/src/3rdparty/rapidjson/internal/ieee754.h
index c2684ba2a..68c9e9664 100644
--- a/src/3rdparty/rapidjson/internal/ieee754.h
+++ b/src/3rdparty/rapidjson/internal/ieee754.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/internal/itoa.h b/src/3rdparty/rapidjson/internal/itoa.h
index 9b1c45cc1..9fe8c932f 100644
--- a/src/3rdparty/rapidjson/internal/itoa.h
+++ b/src/3rdparty/rapidjson/internal/itoa.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 //
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/internal/meta.h b/src/3rdparty/rapidjson/internal/meta.h
index d401edf85..27092dc0d 100644
--- a/src/3rdparty/rapidjson/internal/meta.h
+++ b/src/3rdparty/rapidjson/internal/meta.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/internal/pow10.h b/src/3rdparty/rapidjson/internal/pow10.h
index 02f475d70..eae1a43ed 100644
--- a/src/3rdparty/rapidjson/internal/pow10.h
+++ b/src/3rdparty/rapidjson/internal/pow10.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/internal/regex.h b/src/3rdparty/rapidjson/internal/regex.h
index 16e355921..6446c403a 100644
--- a/src/3rdparty/rapidjson/internal/regex.h
+++ b/src/3rdparty/rapidjson/internal/regex.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -23,7 +23,6 @@
 RAPIDJSON_DIAG_PUSH
 RAPIDJSON_DIAG_OFF(padded)
 RAPIDJSON_DIAG_OFF(switch-enum)
-RAPIDJSON_DIAG_OFF(implicit-fallthrough)
 #elif defined(_MSC_VER)
 RAPIDJSON_DIAG_PUSH
 RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
@@ -32,9 +31,6 @@ RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
 #ifdef __GNUC__
 RAPIDJSON_DIAG_PUSH
 RAPIDJSON_DIAG_OFF(effc++)
-#if __GNUC__ >= 7
-RAPIDJSON_DIAG_OFF(implicit-fallthrough)
-#endif
 #endif
 
 #ifndef RAPIDJSON_REGEX_VERBOSE
@@ -291,6 +287,7 @@ private:
                     if (!CharacterEscape(ds, &codepoint))
                         return; // Unsupported escape character
                     // fall through to default
+                    RAPIDJSON_DELIBERATE_FALLTHROUGH;
 
                 default: // Pattern character
                     PushOperand(operandStack, codepoint);
@@ -520,6 +517,7 @@ private:
                 else if (!CharacterEscape(ds, &codepoint))
                     return false;
                 // fall through to default
+                RAPIDJSON_DELIBERATE_FALLTHROUGH;
 
             default:
                 switch (step) {
@@ -529,6 +527,7 @@ private:
                         break;
                     }
                     // fall through to step 0 for other characters
+                    RAPIDJSON_DELIBERATE_FALLTHROUGH;
 
                 case 0:
                     {
diff --git a/src/3rdparty/rapidjson/internal/stack.h b/src/3rdparty/rapidjson/internal/stack.h
index 45dca6a8b..73abd706e 100644
--- a/src/3rdparty/rapidjson/internal/stack.h
+++ b/src/3rdparty/rapidjson/internal/stack.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/internal/strfunc.h b/src/3rdparty/rapidjson/internal/strfunc.h
index 226439a76..b698a8f43 100644
--- a/src/3rdparty/rapidjson/internal/strfunc.h
+++ b/src/3rdparty/rapidjson/internal/strfunc.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -45,6 +45,20 @@ inline SizeType StrLen(const wchar_t* s) {
     return SizeType(std::wcslen(s));
 }
 
+//! Custom strcmpn() which works on different character types.
+/*! \tparam Ch Character type (e.g. char, wchar_t, short)
+    \param s1 Null-terminated input string.
+    \param s2 Null-terminated input string.
+    \return 0 if equal
+*/
+template<typename Ch>
+inline int StrCmp(const Ch* s1, const Ch* s2) {
+    RAPIDJSON_ASSERT(s1 != 0);
+    RAPIDJSON_ASSERT(s2 != 0);
+    while(*s1 && (*s1 == *s2)) { s1++; s2++; }
+    return static_cast<unsigned>(*s1) < static_cast<unsigned>(*s2) ? -1 : static_cast<unsigned>(*s1) > static_cast<unsigned>(*s2);
+}
+
 //! Returns number of code points in a encoded string.
 template<typename Encoding>
 bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
diff --git a/src/3rdparty/rapidjson/internal/strtod.h b/src/3rdparty/rapidjson/internal/strtod.h
index dfca22b65..55f0e380b 100644
--- a/src/3rdparty/rapidjson/internal/strtod.h
+++ b/src/3rdparty/rapidjson/internal/strtod.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -128,17 +128,18 @@ inline bool StrtodFast(double d, int p, double* result) {
 }
 
 // Compute an approximation and see if it is within 1/2 ULP
-inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) {
+template<typename Ch>
+inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) {
     uint64_t significand = 0;
     int i = 0;   // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999    
     for (; i < dLen; i++) {
         if (significand  >  RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
-            (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
+            (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5')))
             break;
-        significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
+        significand = significand * 10u + static_cast<unsigned>(decimals[i] - Ch('0'));
     }
     
-    if (i < dLen && decimals[i] >= '5') // Rounding
+    if (i < dLen && decimals[i] >= Ch('5')) // Rounding
         significand++;
 
     int remaining = dLen - i;
@@ -205,7 +206,8 @@ inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result
     return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
 }
 
-inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) {
+template<typename Ch>
+inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) {
     RAPIDJSON_ASSERT(dLen >= 0);
     const BigInteger dInt(decimals, static_cast<unsigned>(dLen));
     Double a(approx);
@@ -223,7 +225,8 @@ inline double StrtodBigInteger(double approx, const char* decimals, int dLen, in
         return a.NextPositiveDouble();
 }
 
-inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
+template<typename Ch>
+inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) {
     RAPIDJSON_ASSERT(d >= 0.0);
     RAPIDJSON_ASSERT(length >= 1);
 
diff --git a/src/3rdparty/rapidjson/internal/swap.h b/src/3rdparty/rapidjson/internal/swap.h
index 666e49f97..2cf92f93a 100644
--- a/src/3rdparty/rapidjson/internal/swap.h
+++ b/src/3rdparty/rapidjson/internal/swap.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 //
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/istreamwrapper.h b/src/3rdparty/rapidjson/istreamwrapper.h
index c4950b9dc..01437ec01 100644
--- a/src/3rdparty/rapidjson/istreamwrapper.h
+++ b/src/3rdparty/rapidjson/istreamwrapper.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/memorybuffer.h b/src/3rdparty/rapidjson/memorybuffer.h
index 39bee1dec..ffbc41ed1 100644
--- a/src/3rdparty/rapidjson/memorybuffer.h
+++ b/src/3rdparty/rapidjson/memorybuffer.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/memorystream.h b/src/3rdparty/rapidjson/memorystream.h
index 1d71d8a4f..77af6c999 100644
--- a/src/3rdparty/rapidjson/memorystream.h
+++ b/src/3rdparty/rapidjson/memorystream.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/ostreamwrapper.h b/src/3rdparty/rapidjson/ostreamwrapper.h
index 6f4667c08..11ed4d33f 100644
--- a/src/3rdparty/rapidjson/ostreamwrapper.h
+++ b/src/3rdparty/rapidjson/ostreamwrapper.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/pointer.h b/src/3rdparty/rapidjson/pointer.h
index 063abab9a..67a9cb076 100644
--- a/src/3rdparty/rapidjson/pointer.h
+++ b/src/3rdparty/rapidjson/pointer.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -16,6 +16,7 @@
 #define RAPIDJSON_POINTER_H_
 
 #include "document.h"
+#include "uri.h"
 #include "internal/itoa.h"
 
 #ifdef __clang__
@@ -80,6 +81,8 @@ class GenericPointer {
 public:
     typedef typename ValueType::EncodingType EncodingType;  //!< Encoding type from Value
     typedef typename ValueType::Ch Ch;                      //!< Character type from Value
+    typedef GenericUri<ValueType, Allocator> UriType;
+
 
     //! A token is the basic units of internal representation.
     /*!
@@ -163,7 +166,7 @@ public:
     GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast<Token*>(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
 
     //! Copy constructor.
-    GenericPointer(const GenericPointer& rhs) : allocator_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+    GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
         *this = rhs;
     }
 
@@ -488,10 +491,11 @@ public:
                     v = &((*v)[t->index]);
                 }
                 else {
-                    typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+                    typename ValueType::MemberIterator m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
                     if (m == v->MemberEnd()) {
                         v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator);
-                        v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end
+                        m = v->MemberEnd();
+                        v = &(--m)->value; // Assumes AddMember() appends at the end
                         exist = false;
                     }
                     else
@@ -519,6 +523,70 @@ public:
 
     //@}
 
+    //!@name Compute URI
+    //@{
+
+    //! Compute the in-scope URI for a subtree.
+    //  For use with JSON pointers into JSON schema documents.
+    /*!
+        \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+        \param rootUri Root URI
+        \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token.
+        \param allocator Allocator for Uris
+        \return Uri if it can be resolved. Otherwise null.
+
+        \note
+        There are only 3 situations when a URI cannot be resolved:
+        1. A value in the path is not an array nor object.
+        2. An object value does not contain the token.
+        3. A token is out of range of an array value.
+
+        Use unresolvedTokenIndex to retrieve the token index.
+    */
+    UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const {
+        static const Ch kIdString[] = { 'i', 'd', '\0' };
+        static const ValueType kIdValue(kIdString, 2);
+        UriType base = UriType(rootUri, allocator);
+        RAPIDJSON_ASSERT(IsValid());
+        ValueType* v = &root;
+        for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+            switch (v->GetType()) {
+                case kObjectType:
+                {
+                    // See if we have an id, and if so resolve with the current base
+                    typename ValueType::MemberIterator m = v->FindMember(kIdValue);
+                    if (m != v->MemberEnd() && (m->value).IsString()) {
+                        UriType here = UriType(m->value, allocator).Resolve(base, allocator);
+                        base = here;
+                    }
+                    m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
+                    if (m == v->MemberEnd())
+                        break;
+                    v = &m->value;
+                }
+                  continue;
+                case kArrayType:
+                    if (t->index == kPointerInvalidIndex || t->index >= v->Size())
+                        break;
+                    v = &((*v)[t->index]);
+                    continue;
+                default:
+                    break;
+            }
+
+            // Error: unresolved token
+            if (unresolvedTokenIndex)
+                *unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
+            return UriType(allocator);
+        }
+        return base;
+    }
+
+    UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const {
+      return GetUri(const_cast<ValueType&>(root), rootUri, unresolvedTokenIndex, allocator);
+    }
+
+
     //!@name Query value
     //@{
 
@@ -543,7 +611,7 @@ public:
             switch (v->GetType()) {
             case kObjectType:
                 {
-                    typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+                    typename ValueType::MemberIterator m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
                     if (m == v->MemberEnd())
                         break;
                     v = &m->value;
@@ -779,7 +847,7 @@ public:
             switch (v->GetType()) {
             case kObjectType:
                 {
-                    typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+                    typename ValueType::MemberIterator m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
                     if (m == v->MemberEnd())
                         return false;
                     v = &m->value;
diff --git a/src/3rdparty/rapidjson/prettywriter.h b/src/3rdparty/rapidjson/prettywriter.h
index c7c29b214..ad3779325 100644
--- a/src/3rdparty/rapidjson/prettywriter.h
+++ b/src/3rdparty/rapidjson/prettywriter.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -60,7 +60,7 @@ public:
 
 
     explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : 
-        Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
+        Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
 
 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
     PrettyWriter(PrettyWriter&& rhs) :
@@ -164,7 +164,7 @@ public:
         (void)memberCount;
         RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
         RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
-        typename Base::Level* level = Base::level_stack_.template Pop<typename Base::Level>(1);
+        auto level = Base::level_stack_.template Pop<typename Base::Level>(1);
         bool empty = level->valueCount == 0;
 
         if (!empty && !level->inLine) {
diff --git a/src/3rdparty/rapidjson/rapidjson.h b/src/3rdparty/rapidjson/rapidjson.h
index 78c8aae0c..a4e895324 100644
--- a/src/3rdparty/rapidjson/rapidjson.h
+++ b/src/3rdparty/rapidjson/rapidjson.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -124,6 +124,19 @@
 #define RAPIDJSON_NAMESPACE_END }
 #endif
 
+///////////////////////////////////////////////////////////////////////////////
+// __cplusplus macro
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+
+#if defined(_MSC_VER)
+#define RAPIDJSON_CPLUSPLUS _MSVC_LANG
+#else
+#define RAPIDJSON_CPLUSPLUS __cplusplus
+#endif
+
+//!@endcond
+
 ///////////////////////////////////////////////////////////////////////////////
 // RAPIDJSON_HAS_STDSTRING
 
@@ -149,6 +162,24 @@
 #include <string>
 #endif // RAPIDJSON_HAS_STDSTRING
 
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_USE_MEMBERSMAP
+
+/*! \def RAPIDJSON_USE_MEMBERSMAP
+    \ingroup RAPIDJSON_CONFIG
+    \brief Enable RapidJSON support for object members handling in a \c std::multimap
+
+    By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object
+    members are stored in a \c std::multimap for faster lookup and deletion times, a
+    trade off with a slightly slower insertion time and a small object allocat(or)ed
+    memory overhead.
+
+    \hideinitializer
+*/
+#ifndef RAPIDJSON_USE_MEMBERSMAP
+#define RAPIDJSON_USE_MEMBERSMAP 0 // not by default
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // RAPIDJSON_NO_INT64DEFINE
 
@@ -403,7 +434,7 @@ RAPIDJSON_NAMESPACE_END
 */
 #ifndef RAPIDJSON_ASSERT
 #include <cassert>
-#define RAPIDJSON_ASSERT(x)
+#define RAPIDJSON_ASSERT(x) assert(x)
 #endif // RAPIDJSON_ASSERT
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -411,7 +442,7 @@ RAPIDJSON_NAMESPACE_END
 
 // Prefer C++11 static_assert, if available
 #ifndef RAPIDJSON_STATIC_ASSERT
-#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 )
+#if RAPIDJSON_CPLUSPLUS >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 )
 #define RAPIDJSON_STATIC_ASSERT(x) \
    static_assert(x, RAPIDJSON_STRINGIFY(x))
 #endif // C++11
@@ -490,6 +521,12 @@ RAPIDJSON_NAMESPACE_END
 #define RAPIDJSON_VERSION_CODE(x,y,z) \
   (((x)*100000) + ((y)*100) + (z))
 
+#if defined(__has_builtin)
+#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x)
+#else
+#define RAPIDJSON_HAS_BUILTIN(x) 0
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
 
@@ -535,8 +572,14 @@ RAPIDJSON_NAMESPACE_END
 ///////////////////////////////////////////////////////////////////////////////
 // C++11 features
 
+#ifndef RAPIDJSON_HAS_CXX11
+#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L)
+#endif
+
 #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
-#if defined(__clang__)
+#if RAPIDJSON_HAS_CXX11
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#elif defined(__clang__)
 #if __has_feature(cxx_rvalue_references) && \
     (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
 #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
@@ -553,8 +596,14 @@ RAPIDJSON_NAMESPACE_END
 #endif
 #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
 
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#include <utility> // std::move
+#endif
+
 #ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
-#if defined(__clang__)
+#if RAPIDJSON_HAS_CXX11
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
+#elif defined(__clang__)
 #define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
 #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
     (defined(_MSC_VER) && _MSC_VER >= 1900) || \
@@ -564,11 +613,13 @@ RAPIDJSON_NAMESPACE_END
 #define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
 #endif
 #endif
+#ifndef RAPIDJSON_NOEXCEPT
 #if RAPIDJSON_HAS_CXX11_NOEXCEPT
 #define RAPIDJSON_NOEXCEPT noexcept
 #else
-#define RAPIDJSON_NOEXCEPT /* noexcept */
+#define RAPIDJSON_NOEXCEPT throw()
 #endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
+#endif
 
 // no automatic detection, yet
 #ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
@@ -591,6 +642,27 @@ RAPIDJSON_NAMESPACE_END
 #endif
 #endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
 
+///////////////////////////////////////////////////////////////////////////////
+// C++17 features
+
+#ifndef RAPIDJSON_HAS_CXX17
+#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L)
+#endif
+
+#if RAPIDJSON_HAS_CXX17
+# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]]
+#elif defined(__has_cpp_attribute)
+# if __has_cpp_attribute(clang::fallthrough)
+#  define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]]
+# elif __has_cpp_attribute(fallthrough)
+#  define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough))
+# else
+#  define RAPIDJSON_DELIBERATE_FALLTHROUGH
+# endif
+#else
+# define RAPIDJSON_DELIBERATE_FALLTHROUGH
+#endif
+
 //!@endcond
 
 //! Assertion (in non-throwing contexts).
@@ -609,16 +681,29 @@ RAPIDJSON_NAMESPACE_END
 
 #ifndef RAPIDJSON_NOEXCEPT_ASSERT
 #ifdef RAPIDJSON_ASSERT_THROWS
-#if RAPIDJSON_HAS_CXX11_NOEXCEPT
-#define RAPIDJSON_NOEXCEPT_ASSERT(x)
-#else
-#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x)
-#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
+#include <cassert>
+#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x)
 #else
 #define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x)
 #endif // RAPIDJSON_ASSERT_THROWS
 #endif // RAPIDJSON_NOEXCEPT_ASSERT
 
+///////////////////////////////////////////////////////////////////////////////
+// malloc/realloc/free
+
+#ifndef RAPIDJSON_MALLOC
+///! customization point for global \c malloc
+#define RAPIDJSON_MALLOC(size) std::malloc(size)
+#endif
+#ifndef RAPIDJSON_REALLOC
+///! customization point for global \c realloc
+#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size)
+#endif
+#ifndef RAPIDJSON_FREE
+///! customization point for global \c free
+#define RAPIDJSON_FREE(ptr) std::free(ptr)
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // new/delete
 
diff --git a/src/3rdparty/rapidjson/reader.h b/src/3rdparty/rapidjson/reader.h
index 44a6bcd30..542b7c00c 100644
--- a/src/3rdparty/rapidjson/reader.h
+++ b/src/3rdparty/rapidjson/reader.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 //
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -20,6 +20,7 @@
 #include "allocators.h"
 #include "stream.h"
 #include "encodedstream.h"
+#include "internal/clzll.h"
 #include "internal/meta.h"
 #include "internal/stack.h"
 #include "internal/strtod.h"
@@ -153,6 +154,7 @@ enum ParseFlag {
     kParseNumbersAsStringsFlag = 64,    //!< Parse all numbers (ints/doubles) as strings.
     kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
     kParseNanAndInfFlag = 256,      //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles.
+    kParseEscapedApostropheFlag = 512,  //!< Allow escaped apostrophe in strings.
     kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS  //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
 };
 
@@ -443,16 +445,16 @@ inline const char *SkipWhitespace_SIMD(const char* p) {
 
         x = vmvnq_u8(x);                       // Negate
         x = vrev64q_u8(x);                     // Rev in 64
-        uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
-        uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+        uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0);   // extract
+        uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1);  // extract
 
         if (low == 0) {
             if (high != 0) {
-                int lz =__builtin_clzll(high);;
+                uint32_t lz = internal::clzll(high);
                 return p + 8 + (lz >> 3);
             }
         } else {
-            int lz = __builtin_clzll(low);;
+            uint32_t lz = internal::clzll(low);
             return p + (lz >> 3);
         }
     }
@@ -479,16 +481,16 @@ inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
 
         x = vmvnq_u8(x);                       // Negate
         x = vrev64q_u8(x);                     // Rev in 64
-        uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
-        uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+        uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0);   // extract
+        uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1);  // extract
 
         if (low == 0) {
             if (high != 0) {
-                int lz = __builtin_clzll(high);
+                uint32_t lz = internal::clzll(high);
                 return p + 8 + (lz >> 3);
             }
         } else {
-            int lz = __builtin_clzll(low);
+            uint32_t lz = internal::clzll(low);
             return p + (lz >> 3);
         }
     }
@@ -990,7 +992,7 @@ private:
 //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
 #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
         static const char escape[256] = {
-            Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/',
+            Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/',
             Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
             0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0,
             0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1013,19 +1015,31 @@ private:
                     is.Take();
                     os.Put(static_cast<typename TEncoding::Ch>(escape[static_cast<unsigned char>(e)]));
                 }
+                else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe
+                    is.Take();
+                    os.Put('\'');
+                }
                 else if (RAPIDJSON_LIKELY(e == 'u')) {    // Unicode
                     is.Take();
                     unsigned codepoint = ParseHex4(is, escapeOffset);
                     RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
-                    if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) {
-                        // Handle UTF-16 surrogate pair
-                        if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u')))
+                    if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) {
+                        // high surrogate, check if followed by valid low surrogate
+                        if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) {
+                            // Handle UTF-16 surrogate pair
+                            if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u')))
+                                RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+                            unsigned codepoint2 = ParseHex4(is, escapeOffset);
+                            RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+                            if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF))
+                                RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+                            codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
+                        }
+                        // single low surrogate
+                        else
+                        {
                             RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
-                        unsigned codepoint2 = ParseHex4(is, escapeOffset);
-                        RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
-                        if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF))
-                            RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
-                        codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
+                        }
                     }
                     TEncoding::Encode(os, codepoint);
                 }
@@ -1244,19 +1258,19 @@ private:
             x = vorrq_u8(x, vcltq_u8(s, s3));
 
             x = vrev64q_u8(x);                     // Rev in 64
-            uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
-            uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+            uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0);   // extract
+            uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1);  // extract
 
             SizeType length = 0;
             bool escaped = false;
             if (low == 0) {
                 if (high != 0) {
-                    unsigned lz = (unsigned)__builtin_clzll(high);;
+                    uint32_t lz = internal::clzll(high);
                     length = 8 + (lz >> 3);
                     escaped = true;
                 }
             } else {
-                unsigned lz = (unsigned)__builtin_clzll(low);;
+                uint32_t lz = internal::clzll(low);
                 length = lz >> 3;
                 escaped = true;
             }
@@ -1314,19 +1328,19 @@ private:
             x = vorrq_u8(x, vcltq_u8(s, s3));
 
             x = vrev64q_u8(x);                     // Rev in 64
-            uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
-            uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+            uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0);   // extract
+            uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1);  // extract
 
             SizeType length = 0;
             bool escaped = false;
             if (low == 0) {
                 if (high != 0) {
-                    unsigned lz = (unsigned)__builtin_clzll(high);
+                    uint32_t lz = internal::clzll(high);
                     length = 8 + (lz >> 3);
                     escaped = true;
                 }
             } else {
-                unsigned lz = (unsigned)__builtin_clzll(low);
+                uint32_t lz = internal::clzll(low);
                 length = lz >> 3;
                 escaped = true;
             }
@@ -1370,17 +1384,17 @@ private:
             x = vorrq_u8(x, vcltq_u8(s, s3));
 
             x = vrev64q_u8(x);                     // Rev in 64
-            uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
-            uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+            uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0);   // extract
+            uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1);  // extract
 
             if (low == 0) {
                 if (high != 0) {
-                    int lz = __builtin_clzll(high);
+                    uint32_t lz = internal::clzll(high);
                     p += 8 + (lz >> 3);
                     break;
                 }
             } else {
-                int lz = __builtin_clzll(low);
+                uint32_t lz = internal::clzll(low);
                 p += lz >> 3;
                 break;
             }
@@ -1390,11 +1404,11 @@ private:
     }
 #endif // RAPIDJSON_NEON
 
-    template<typename InputStream, bool backup, bool pushOnTake>
+    template<typename InputStream, typename StackCharacter, bool backup, bool pushOnTake>
     class NumberStream;
 
-    template<typename InputStream>
-    class NumberStream<InputStream, false, false> {
+    template<typename InputStream, typename StackCharacter>
+    class NumberStream<InputStream, StackCharacter, false, false> {
     public:
         typedef typename InputStream::Ch Ch;
 
@@ -1403,11 +1417,11 @@ private:
         RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); }
         RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); }
         RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
-		  RAPIDJSON_FORCEINLINE void Push(char) {}
+        RAPIDJSON_FORCEINLINE void Push(char) {}
 
         size_t Tell() { return is.Tell(); }
         size_t Length() { return 0; }
-        const char* Pop() { return 0; }
+        const StackCharacter* Pop() { return 0; }
 
     protected:
         NumberStream& operator=(const NumberStream&);
@@ -1415,35 +1429,35 @@ private:
         InputStream& is;
     };
 
-    template<typename InputStream>
-    class NumberStream<InputStream, true, false> : public NumberStream<InputStream, false, false> {
-        typedef NumberStream<InputStream, false, false> Base;
+    template<typename InputStream, typename StackCharacter>
+    class NumberStream<InputStream, StackCharacter, true, false> : public NumberStream<InputStream, StackCharacter, false, false> {
+        typedef NumberStream<InputStream, StackCharacter, false, false> Base;
     public:
         NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {}
 
         RAPIDJSON_FORCEINLINE Ch TakePush() {
-            stackStream.Put(static_cast<char>(Base::is.Peek()));
+            stackStream.Put(static_cast<StackCharacter>(Base::is.Peek()));
             return Base::is.Take();
         }
 
-        RAPIDJSON_FORCEINLINE void Push(char c) {
+        RAPIDJSON_FORCEINLINE void Push(StackCharacter c) {
             stackStream.Put(c);
         }
 
         size_t Length() { return stackStream.Length(); }
 
-        const char* Pop() {
+        const StackCharacter* Pop() {
             stackStream.Put('\0');
             return stackStream.Pop();
         }
 
     private:
-        StackStream<char> stackStream;
+        StackStream<StackCharacter> stackStream;
     };
 
-    template<typename InputStream>
-    class NumberStream<InputStream, true, true> : public NumberStream<InputStream, true, false> {
-        typedef NumberStream<InputStream, true, false> Base;
+    template<typename InputStream, typename StackCharacter>
+    class NumberStream<InputStream, StackCharacter, true, true> : public NumberStream<InputStream, StackCharacter, true, false> {
+        typedef NumberStream<InputStream, StackCharacter, true, false> Base;
     public:
         NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {}
 
@@ -1452,8 +1466,10 @@ private:
 
     template<unsigned parseFlags, typename InputStream, typename Handler>
     void ParseNumber(InputStream& is, Handler& handler) {
+        typedef typename internal::SelectIf<internal::BoolType<(parseFlags & kParseNumbersAsStringsFlag) != 0>, typename TargetEncoding::Ch, char>::Type NumberCharacter;
+
         internal::StreamLocalCopy<InputStream> copy(is);
-        NumberStream<InputStream,
+        NumberStream<InputStream, NumberCharacter,
             ((parseFlags & kParseNumbersAsStringsFlag) != 0) ?
                 ((parseFlags & kParseInsituFlag) == 0) :
                 ((parseFlags & kParseFullPrecisionFlag) != 0),
@@ -1678,10 +1694,10 @@ private:
             }
             else {
                 SizeType numCharsToCopy = static_cast<SizeType>(s.Length());
-                StringStream srcStream(s.Pop());
+                GenericStringStream<UTF8<NumberCharacter>> srcStream(s.Pop());
                 StackStream<typename TargetEncoding::Ch> dstStream(stack_);
                 while (numCharsToCopy--) {
-                    Transcoder<UTF8<>, TargetEncoding>::Transcode(srcStream, dstStream);
+                    Transcoder<UTF8<typename TargetEncoding::Ch>, TargetEncoding>::Transcode(srcStream, dstStream);
                 }
                 dstStream.Put('\0');
                 const typename TargetEncoding::Ch* str = dstStream.Pop();
@@ -1691,7 +1707,7 @@ private:
         }
         else {
            size_t length = s.Length();
-           const char* decimal = s.Pop();  // Pop stack no matter if it will be used or not.
+           const NumberCharacter* decimal = s.Pop();  // Pop stack no matter if it will be used or not.
 
            if (useDouble) {
                int p = exp + expFrac;
diff --git a/src/3rdparty/rapidjson/readme.md b/src/3rdparty/rapidjson/readme.md
new file mode 100644
index 000000000..ac683b051
--- /dev/null
+++ b/src/3rdparty/rapidjson/readme.md
@@ -0,0 +1,210 @@
+![RapidJSON logo](doc/logo/rapidjson.png)
+
+![Release version](https://img.shields.io/badge/release-v1.1.0-blue.svg)
+
+## A fast JSON parser/generator for C++ with both SAX/DOM style API
+
+Tencent is pleased to support the open source community by making RapidJSON available.
+
+Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+
+* [RapidJSON GitHub](https://github.com/Tencent/rapidjson/)
+* RapidJSON Documentation
+  * [English](http://rapidjson.org/)
+  * [简体中文](http://rapidjson.org/zh-cn/)
+  * [GitBook](https://www.gitbook.com/book/miloyip/rapidjson/) with downloadable PDF/EPUB/MOBI, without API reference.
+
+## Build status
+
+| [Linux][lin-link] | [Windows][win-link] | [Coveralls][cov-link] |
+| :---------------: | :-----------------: | :-------------------: |
+| ![lin-badge]      | ![win-badge]        | ![cov-badge]          |
+
+[lin-badge]: https://travis-ci.org/Tencent/rapidjson.svg?branch=master "Travis build status"
+[lin-link]:  https://travis-ci.org/Tencent/rapidjson "Travis build status"
+[win-badge]: https://ci.appveyor.com/api/projects/status/l6qulgqahcayidrf/branch/master?svg=true "AppVeyor build status"
+[win-link]:  https://ci.appveyor.com/project/miloyip/rapidjson-0fdqj/branch/master "AppVeyor build status"
+[cov-badge]: https://coveralls.io/repos/Tencent/rapidjson/badge.svg?branch=master "Coveralls coverage"
+[cov-link]:  https://coveralls.io/r/Tencent/rapidjson?branch=master "Coveralls coverage"
+
+## Introduction
+
+RapidJSON is a JSON parser and generator for C++. It was inspired by [RapidXml](http://rapidxml.sourceforge.net/).
+
+* RapidJSON is **small** but **complete**. It supports both SAX and DOM style API. The SAX parser is only a half thousand lines of code.
+
+* RapidJSON is **fast**. Its performance can be comparable to `strlen()`. It also optionally supports SSE2/SSE4.2 for acceleration.
+
+* RapidJSON is **self-contained** and **header-only**. It does not depend on external libraries such as BOOST. It even does not depend on STL.
+
+* RapidJSON is **memory-friendly**. Each JSON value occupies exactly 16 bytes for most 32/64-bit machines (excluding text string). By default it uses a fast memory allocator, and the parser allocates memory compactly during parsing.
+
+* RapidJSON is **Unicode-friendly**. It supports UTF-8, UTF-16, UTF-32 (LE & BE), and their detection, validation and transcoding internally. For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM. It also supports surrogates and "\u0000" (null character).
+
+More features can be read [here](doc/features.md).
+
+JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in full compliance with RFC7159/ECMA-404, with optional support of relaxed syntax. More information about JSON can be obtained at
+* [Introducing JSON](http://json.org/)
+* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159)
+* [Standard ECMA-404: The JSON Data Interchange Format](https://www.ecma-international.org/publications/standards/Ecma-404.htm)
+
+## Highlights in v1.1 (2016-8-25)
+
+* Added [JSON Pointer](doc/pointer.md)
+* Added [JSON Schema](doc/schema.md)
+* Added [relaxed JSON syntax](doc/dom.md) (comment, trailing comma, NaN/Infinity)
+* Iterating array/object with [C++11 Range-based for loop](doc/tutorial.md)
+* Reduce memory overhead of each `Value` from 24 bytes to 16 bytes in x86-64 architecture.
+
+For other changes please refer to [change log](CHANGELOG.md).
+
+## Compatibility
+
+RapidJSON is cross-platform. Some platform/compiler combinations which have been tested are shown as follows.
+* Visual C++ 2008/2010/2013 on Windows (32/64-bit)
+* GNU C++ 3.8.x on Cygwin
+* Clang 3.4 on Mac OS X (32/64-bit) and iOS
+* Clang 3.4 on Android NDK
+
+Users can build and run the unit tests on their platform/compiler.
+
+## Installation
+
+RapidJSON is a header-only C++ library. Just copy the `include/rapidjson` folder to system or project's include path.
+
+Alternatively, if you are using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager you can download and install rapidjson with CMake integration in a single command:
+* vcpkg install rapidjson
+
+RapidJSON uses following software as its dependencies:
+* [CMake](https://cmake.org/) as a general build tool
+* (optional) [Doxygen](http://www.doxygen.org) to build documentation
+* (optional) [googletest](https://github.com/google/googletest) for unit and performance testing
+
+To generate user documentation and run tests please proceed with the steps below:
+
+1. Execute `git submodule update --init` to get the files of thirdparty submodules (google test).
+2. Create directory called `build` in rapidjson source directory.
+3. Change to `build` directory and run `cmake ..` command to configure your build. Windows users can do the same with cmake-gui application.
+4. On Windows, build the solution found in the build directory. On Linux, run `make` from the build directory.
+
+On successful build you will find compiled test and example binaries in `bin`
+directory. The generated documentation will be available in `doc/html`
+directory of the build tree. To run tests after finished build please run `make
+test` or `ctest` from your build tree. You can get detailed output using `ctest
+-V` command.
+
+It is possible to install library system-wide by running `make install` command
+from the build tree with administrative privileges. This will install all files
+according to system preferences.  Once RapidJSON is installed, it is possible
+to use it from other CMake projects by adding `find_package(RapidJSON)` line to
+your CMakeLists.txt.
+
+## Usage at a glance
+
+This simple example parses a JSON string into a document (DOM), make a simple modification of the DOM, and finally stringify the DOM to a JSON string.
+
+~~~~~~~~~~cpp
+// rapidjson/example/simpledom/simpledom.cpp`
+#include "rapidjson/document.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+#include <iostream>
+
+using namespace rapidjson;
+
+int main() {
+    // 1. Parse a JSON string into DOM.
+    const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
+    Document d;
+    d.Parse(json);
+
+    // 2. Modify it by DOM.
+    Value& s = d["stars"];
+    s.SetInt(s.GetInt() + 1);
+
+    // 3. Stringify the DOM
+    StringBuffer buffer;
+    Writer<StringBuffer> writer(buffer);
+    d.Accept(writer);
+
+    // Output {"project":"rapidjson","stars":11}
+    std::cout << buffer.GetString() << std::endl;
+    return 0;
+}
+~~~~~~~~~~
+
+Note that this example did not handle potential errors.
+
+The following diagram shows the process.
+
+![simpledom](doc/diagram/simpledom.png)
+
+More [examples](https://github.com/Tencent/rapidjson/tree/master/example) are available:
+
+* DOM API
+  * [tutorial](https://github.com/Tencent/rapidjson/blob/master/example/tutorial/tutorial.cpp): Basic usage of DOM API.
+
+* SAX API
+  * [simplereader](https://github.com/Tencent/rapidjson/blob/master/example/simplereader/simplereader.cpp): Dumps all SAX events while parsing a JSON by `Reader`.
+  * [condense](https://github.com/Tencent/rapidjson/blob/master/example/condense/condense.cpp): A command line tool to rewrite a JSON, with all whitespaces removed.
+  * [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp): A command line tool to rewrite a JSON with indents and newlines by `PrettyWriter`.
+  * [capitalize](https://github.com/Tencent/rapidjson/blob/master/example/capitalize/capitalize.cpp): A command line tool to capitalize strings in JSON.
+  * [messagereader](https://github.com/Tencent/rapidjson/blob/master/example/messagereader/messagereader.cpp): Parse a JSON message with SAX API.
+  * [serialize](https://github.com/Tencent/rapidjson/blob/master/example/serialize/serialize.cpp): Serialize a C++ object into JSON with SAX API.
+  * [jsonx](https://github.com/Tencent/rapidjson/blob/master/example/jsonx/jsonx.cpp): Implements a `JsonxWriter` which stringify SAX events into [JSONx](https://www-01.ibm.com/support/knowledgecenter/SS9H2Y_7.1.0/com.ibm.dp.doc/json_jsonx.html) (a kind of XML) format. The example is a command line tool which converts input JSON into JSONx format.
+
+* Schema
+  * [schemavalidator](https://github.com/Tencent/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp) : A command line tool to validate a JSON with a JSON schema.
+
+* Advanced
+  * [prettyauto](https://github.com/Tencent/rapidjson/blob/master/example/prettyauto/prettyauto.cpp): A modified version of [pretty](https://github.com/Tencent/rapidjson/blob/master/example/pretty/pretty.cpp) to automatically handle JSON with any UTF encodings.
+  * [parsebyparts](https://github.com/Tencent/rapidjson/blob/master/example/parsebyparts/parsebyparts.cpp): Implements an `AsyncDocumentParser` which can parse JSON in parts, using C++11 thread.
+  * [filterkey](https://github.com/Tencent/rapidjson/blob/master/example/filterkey/filterkey.cpp): A command line tool to remove all values with user-specified key.
+  * [filterkeydom](https://github.com/Tencent/rapidjson/blob/master/example/filterkeydom/filterkeydom.cpp): Same tool as above, but it demonstrates how to use a generator to populate a `Document`.
+
+## Contributing
+
+RapidJSON welcomes contributions. When contributing, please follow the code below.
+
+### Issues
+
+Feel free to submit issues and enhancement requests.
+
+Please help us by providing **minimal reproducible examples**, because source code is easier to let other people understand what happens.
+For crash problems on certain platforms, please bring stack dump content with the detail of the OS, compiler, etc.
+
+Please try breakpoint debugging first, tell us what you found, see if we can start exploring based on more information been prepared.
+
+### Workflow
+
+In general, we follow the "fork-and-pull" Git workflow.
+
+ 1. **Fork** the repo on GitHub
+ 2. **Clone** the project to your own machine
+ 3. **Checkout** a new branch on your fork, start developing on the branch
+ 4. **Test** the change before commit, Make sure the changes pass all the tests, including `unittest` and `preftest`, please add test case for each new feature or bug-fix if needed.
+ 5. **Commit** changes to your own branch
+ 6. **Push** your work back up to your fork
+ 7. Submit a **Pull request** so that we can review your changes
+
+NOTE: Be sure to merge the latest from "upstream" before making a pull request!
+
+### Copyright and Licensing
+
+You can copy and paste the license summary from below.
+
+```
+Tencent is pleased to support the open source community by making RapidJSON available.
+
+Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+
+Licensed under the MIT License (the "License"); you may not use this file except
+in compliance with the License. You may obtain a copy of the License at
+
+http://opensource.org/licenses/MIT
+
+Unless required by applicable law or agreed to in writing, software distributed 
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
+CONDITIONS OF ANY KIND, either express or implied. See the License for the 
+specific language governing permissions and limitations under the License.
+```
diff --git a/src/3rdparty/rapidjson/schema.h b/src/3rdparty/rapidjson/schema.h
index 26ae94748..f0759ffcf 100644
--- a/src/3rdparty/rapidjson/schema.h
+++ b/src/3rdparty/rapidjson/schema.h
@@ -18,6 +18,8 @@
 #include "document.h"
 #include "pointer.h"
 #include "stringbuffer.h"
+#include "error/en.h"
+#include "uri.h"
 #include <cmath> // abs, floor
 
 #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
@@ -113,13 +115,36 @@ inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar
 #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
 #endif
 
-#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
+#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
 RAPIDJSON_MULTILINEMACRO_BEGIN\
-    context.invalidKeyword = keyword.GetString();\
-    RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
+    context.invalidCode = code;\
+    context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
+    RAPIDJSON_INVALID_KEYWORD_VERBOSE(context.invalidKeyword);\
     return false;\
 RAPIDJSON_MULTILINEMACRO_END
 
+///////////////////////////////////////////////////////////////////////////////
+// ValidateFlag
+
+/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS
+    \ingroup RAPIDJSON_CONFIG
+    \brief User-defined kValidateDefaultFlags definition.
+
+    User can define this as any \c ValidateFlag combinations.
+*/
+#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS
+#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags
+#endif
+
+//! Combination of validate flags
+/*! \see
+ */
+enum ValidateFlag {
+    kValidateNoFlags = 0,                                       //!< No flags are set.
+    kValidateContinueOnErrorFlag = 1,                           //!< Don't stop after first validation error.
+    kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS    //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Forward declarations
 
@@ -138,6 +163,8 @@ class ISchemaValidator {
 public:
     virtual ~ISchemaValidator() {}
     virtual bool IsValid() const = 0;
+    virtual void SetValidateFlags(unsigned flags) = 0;
+    virtual unsigned GetValidateFlags() const = 0;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -147,7 +174,7 @@ template <typename SchemaType>
 class ISchemaStateFactory {
 public:
     virtual ~ISchemaStateFactory() {}
-    virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
+    virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0;
     virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
     virtual void* CreateHasher() = 0;
     virtual uint64_t GetHashCode(void* hasher) = 0;
@@ -201,13 +228,13 @@ public:
     virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
     virtual bool EndDependencyErrors() = 0;
 
-    virtual void DisallowedValue() = 0;
+    virtual void DisallowedValue(const ValidateErrorCode code) = 0;
     virtual void StartDisallowedType() = 0;
     virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
     virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
     virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
     virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
-    virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
+    virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched) = 0;
     virtual void Disallowed() = 0;
 };
 
@@ -332,6 +359,7 @@ struct SchemaValidationContext {
         schema(s),
         valueSchema(),
         invalidKeyword(),
+        invalidCode(),
         hasher(),
         arrayElementHashCodes(),
         validators(),
@@ -372,6 +400,7 @@ struct SchemaValidationContext {
     const SchemaType* schema;
     const SchemaType* valueSchema;
     const Ch* invalidKeyword;
+    ValidateErrorCode invalidCode;
     void* hasher; // Only validator access
     void* arrayElementHashCodes; // Only validator access this
     ISchemaValidator** validators;
@@ -404,11 +433,13 @@ public:
     typedef Schema<SchemaDocumentType> SchemaType;
     typedef GenericValue<EncodingType, AllocatorType> SValue;
     typedef IValidationErrorHandler<Schema> ErrorHandler;
+    typedef GenericUri<ValueType, AllocatorType> UriType;
     friend class GenericSchemaDocument<ValueType, AllocatorType>;
 
-    Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
+    Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) :
         allocator_(allocator),
         uri_(schemaDocument->GetURI(), *allocator),
+        id_(id),
         pointer_(p, allocator),
         typeless_(schemaDocument->GetTypeless()),
         enum_(),
@@ -443,13 +474,31 @@ public:
         exclusiveMaximum_(false),
         defaultValueLength_(0)
     {
-        typedef typename SchemaDocumentType::ValueType ValueType;
         typedef typename ValueType::ConstValueIterator ConstValueIterator;
         typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
 
+        // PR #1393
+        // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite
+        // recursion (with recursive schemas), since schemaDocument->getSchema() is always
+        // checked before creating a new one. Don't cache typeless_, though.
+        if (this != typeless_) {
+          typedef typename SchemaDocumentType::SchemaEntry SchemaEntry;
+          SchemaEntry *entry = schemaDocument->schemaMap_.template Push<SchemaEntry>();
+          new (entry) SchemaEntry(pointer_, this, true, allocator_);
+          schemaDocument->AddSchemaRefs(this);
+        }
+
         if (!value.IsObject())
             return;
 
+        // If we have an id property, resolve it with the in-scope id
+        if (const ValueType* v = GetMember(value, GetIdString())) {
+            if (v->IsString()) {
+                UriType local(*v, allocator);
+                id_ = local.Resolve(id_, allocator);
+            }
+        }
+
         if (const ValueType* v = GetMember(value, GetTypeString())) {
             type_ = 0;
             if (v->IsString())
@@ -459,7 +508,7 @@ public:
                     AddType(*itr);
         }
 
-        if (const ValueType* v = GetMember(value, GetEnumString()))
+        if (const ValueType* v = GetMember(value, GetEnumString())) {
             if (v->IsArray() && v->Size() > 0) {
                 enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
                 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
@@ -471,17 +520,18 @@ public:
                     enum_[enumCount_++] = h.GetHashCode();
                 }
             }
+        }
 
         if (schemaDocument) {
             AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
             AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
             AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
-        }
 
-        if (const ValueType* v = GetMember(value, GetNotString())) {
-            schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document);
-            notValidatorIndex_ = validatorCount_;
-            validatorCount_++;
+            if (const ValueType* v = GetMember(value, GetNotString())) {
+                schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document, id_);
+                notValidatorIndex_ = validatorCount_;
+                validatorCount_++;
+            }
         }
 
         // Object
@@ -496,7 +546,7 @@ public:
             if (properties && properties->IsObject())
                 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
                     AddUniqueElement(allProperties, itr->name);
-            
+
             if (required && required->IsArray())
                 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
                     if (itr->IsString())
@@ -527,7 +577,7 @@ public:
             for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
                 SizeType index;
                 if (FindPropertyIndex(itr->name, &index))
-                    schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
+                    schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
             }
         }
 
@@ -539,7 +589,7 @@ public:
             for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
                 new (&patternProperties_[patternPropertyCount_]) PatternProperty();
                 patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
-                schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
+                schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
                 patternPropertyCount_++;
             }
         }
@@ -571,7 +621,7 @@ public:
                     }
                     else if (itr->value.IsObject()) {
                         hasSchemaDependencies_ = true;
-                        schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
+                        schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_);
                         properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
                         validatorCount_++;
                     }
@@ -583,7 +633,7 @@ public:
             if (v->IsBool())
                 additionalProperties_ = v->GetBool();
             else if (v->IsObject())
-                schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
+                schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_);
         }
 
         AssignIfExist(minProperties_, value, GetMinPropertiesString());
@@ -593,12 +643,12 @@ public:
         if (const ValueType* v = GetMember(value, GetItemsString())) {
             PointerType q = p.Append(GetItemsString(), allocator_);
             if (v->IsObject()) // List validation
-                schemaDocument->CreateSchema(&itemsList_, q, *v, document);
+                schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_);
             else if (v->IsArray()) { // Tuple validation
                 itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
                 SizeType index = 0;
                 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
-                    schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
+                    schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_);
             }
         }
 
@@ -609,7 +659,7 @@ public:
             if (v->IsBool())
                 additionalItems_ = v->GetBool();
             else if (v->IsObject())
-                schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
+                schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_);
         }
 
         AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
@@ -669,6 +719,10 @@ public:
         return uri_;
     }
 
+    const UriType& GetId() const {
+        return id_;
+    }
+
     const PointerType& GetPointer() const {
         return pointer_;
     }
@@ -689,7 +743,11 @@ public:
                     context.valueSchema = typeless_;
                 else {
                     context.error_handler.DisallowedItem(context.arrayElementIndex);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
+                    // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
+                    context.valueSchema = typeless_;
+                    // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set
+                    context.arrayElementIndex++;
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems);
                 }
             }
             else
@@ -701,6 +759,7 @@ public:
     }
 
     RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
+        // Only check pattern properties if we have validators
         if (context.patternPropertiesValidatorCount > 0) {
             bool otherValid = false;
             SizeType count = context.patternPropertiesValidatorCount;
@@ -717,66 +776,70 @@ public:
             if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
                 if (!patternValid) {
                     context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
                 }
             }
             else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
                 if (!patternValid || !otherValid) {
                     context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
                 }
             }
             else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
                 context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
-                RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+                RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
             }
         }
 
-        if (enum_) {
+        // For enums only check if we have a hasher
+        if (enum_ && context.hasher) {
             const uint64_t h = context.factory.GetHashCode(context.hasher);
             for (SizeType i = 0; i < enumCount_; i++)
                 if (enum_[i] == h)
                     goto foundEnum;
-            context.error_handler.DisallowedValue();
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
+            context.error_handler.DisallowedValue(kValidateErrorEnum);
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum);
             foundEnum:;
         }
 
-        if (allOf_.schemas)
-            for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
-                if (!context.validators[i]->IsValid()) {
-                    context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
-                }
-        
-        if (anyOf_.schemas) {
-            for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
-                if (context.validators[i]->IsValid())
-                    goto foundAny;
-            context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
-            foundAny:;
-        }
+        // Only check allOf etc if we have validators
+        if (context.validatorCount > 0) {
+            if (allOf_.schemas)
+                for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
+                    if (!context.validators[i]->IsValid()) {
+                        context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
+                        RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf);
+                    }
 
-        if (oneOf_.schemas) {
-            bool oneValid = false;
-            for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
-                if (context.validators[i]->IsValid()) {
-                    if (oneValid) {
-                        context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
-                        RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
-                    } else
-                        oneValid = true;
-                }
-            if (!oneValid) {
-                context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
-                RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+            if (anyOf_.schemas) {
+                for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
+                    if (context.validators[i]->IsValid())
+                        goto foundAny;
+                context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
+                RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf);
+                foundAny:;
             }
-        }
 
-        if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
-            context.error_handler.Disallowed();
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
+            if (oneOf_.schemas) {
+                bool oneValid = false;
+                for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
+                    if (context.validators[i]->IsValid()) {
+                        if (oneValid) {
+                            context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, true);
+                            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
+                        } else
+                            oneValid = true;
+                    }
+                if (!oneValid) {
+                    context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, false);
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
+                }
+            }
+
+            if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
+                context.error_handler.Disallowed();
+                RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot);
+            }
         }
 
         return true;
@@ -785,15 +848,15 @@ public:
     bool Null(Context& context) const {
         if (!(type_ & (1 << kNullSchemaType))) {
             DisallowedType(context, GetNullString());
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
         }
         return CreateParallelValidator(context);
     }
-    
+
     bool Bool(Context& context, bool) const {
         if (!(type_ & (1 << kBooleanSchemaType))) {
             DisallowedType(context, GetBooleanString());
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
         }
         return CreateParallelValidator(context);
     }
@@ -825,7 +888,7 @@ public:
     bool Double(Context& context, double d) const {
         if (!(type_ & (1 << kNumberSchemaType))) {
             DisallowedType(context, GetNumberString());
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
         }
 
         if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
@@ -833,17 +896,17 @@ public:
 
         if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
             return false;
-        
+
         if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
             return false;
-        
+
         return CreateParallelValidator(context);
     }
-    
+
     bool String(Context& context, const Ch* str, SizeType length, bool) const {
         if (!(type_ & (1 << kStringSchemaType))) {
             DisallowedType(context, GetStringString());
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
         }
 
         if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
@@ -851,18 +914,18 @@ public:
             if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
                 if (count < minLength_) {
                     context.error_handler.TooShort(str, length, minLength_);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength);
                 }
                 if (count > maxLength_) {
                     context.error_handler.TooLong(str, length, maxLength_);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength);
                 }
             }
         }
 
         if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
             context.error_handler.DoesNotMatch(str, length);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern);
         }
 
         return CreateParallelValidator(context);
@@ -871,7 +934,7 @@ public:
     bool StartObject(Context& context) const {
         if (!(type_ & (1 << kObjectSchemaType))) {
             DisallowedType(context, GetObjectString());
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
         }
 
         if (hasDependencies_ || hasRequired_) {
@@ -888,7 +951,7 @@ public:
 
         return CreateParallelValidator(context);
     }
-    
+
     bool Key(Context& context, const Ch* str, SizeType len, bool) const {
         if (patternProperties_) {
             context.patternPropertiesSchemaCount = 0;
@@ -899,7 +962,7 @@ public:
                 }
         }
 
-        SizeType index;
+        SizeType index  = 0;
         if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
             if (context.patternPropertiesSchemaCount > 0) {
                 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
@@ -916,7 +979,7 @@ public:
         }
 
         if (additionalPropertiesSchema_) {
-            if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
+            if (context.patternPropertiesSchemaCount > 0) {
                 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
                 context.valueSchema = typeless_;
                 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
@@ -931,8 +994,10 @@ public:
         }
 
         if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
+            // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
+            context.valueSchema = typeless_;
             context.error_handler.DisallowedProperty(str, len);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties);
         }
 
         return true;
@@ -946,17 +1011,17 @@ public:
                     if (properties_[index].schema->defaultValueLength_ == 0 )
                         context.error_handler.AddMissingProperty(properties_[index].name);
             if (context.error_handler.EndMissingProperties())
-                RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
+                RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired);
         }
 
         if (memberCount < minProperties_) {
             context.error_handler.TooFewProperties(memberCount, minProperties_);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties);
         }
 
         if (memberCount > maxProperties_) {
             context.error_handler.TooManyProperties(memberCount, maxProperties_);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties);
         }
 
         if (hasDependencies_) {
@@ -979,40 +1044,78 @@ public:
                 }
             }
             if (context.error_handler.EndDependencyErrors())
-                RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+                RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies);
         }
 
         return true;
     }
 
     bool StartArray(Context& context) const {
+        context.arrayElementIndex = 0;
+        context.inArray = true;  // Ensure we note that we are in an array
+
         if (!(type_ & (1 << kArraySchemaType))) {
             DisallowedType(context, GetArrayString());
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
         }
 
-        context.arrayElementIndex = 0;
-        context.inArray = true;
-
         return CreateParallelValidator(context);
     }
 
     bool EndArray(Context& context, SizeType elementCount) const {
         context.inArray = false;
-        
+
         if (elementCount < minItems_) {
             context.error_handler.TooFewItems(elementCount, minItems_);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems);
         }
-        
+
         if (elementCount > maxItems_) {
             context.error_handler.TooManyItems(elementCount, maxItems_);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems);
         }
 
         return true;
     }
 
+    static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) {
+        switch (validateErrorCode) {
+            case kValidateErrorMultipleOf:              return GetMultipleOfString();
+            case kValidateErrorMaximum:                 return GetMaximumString();
+            case kValidateErrorExclusiveMaximum:        return GetMaximumString(); // Same
+            case kValidateErrorMinimum:                 return GetMinimumString();
+            case kValidateErrorExclusiveMinimum:        return GetMinimumString(); // Same
+
+            case kValidateErrorMaxLength:               return GetMaxLengthString();
+            case kValidateErrorMinLength:               return GetMinLengthString();
+            case kValidateErrorPattern:                 return GetPatternString();
+
+            case kValidateErrorMaxItems:                return GetMaxItemsString();
+            case kValidateErrorMinItems:                return GetMinItemsString();
+            case kValidateErrorUniqueItems:             return GetUniqueItemsString();
+            case kValidateErrorAdditionalItems:         return GetAdditionalItemsString();
+
+            case kValidateErrorMaxProperties:           return GetMaxPropertiesString();
+            case kValidateErrorMinProperties:           return GetMinPropertiesString();
+            case kValidateErrorRequired:                return GetRequiredString();
+            case kValidateErrorAdditionalProperties:    return GetAdditionalPropertiesString();
+            case kValidateErrorPatternProperties:       return GetPatternPropertiesString();
+            case kValidateErrorDependencies:            return GetDependenciesString();
+
+            case kValidateErrorEnum:                    return GetEnumString();
+            case kValidateErrorType:                    return GetTypeString();
+
+            case kValidateErrorOneOf:                   return GetOneOfString();
+            case kValidateErrorOneOfMatch:              return GetOneOfString(); // Same
+            case kValidateErrorAllOf:                   return GetAllOfString();
+            case kValidateErrorAnyOf:                   return GetAnyOfString();
+            case kValidateErrorNot:                     return GetNotString();
+
+            default:                                    return GetNullString();
+        }
+    }
+
+
     // Generate functions for string literal according to Ch
 #define RAPIDJSON_STRING_(name, ...) \
     static const ValueType& Get##name##String() {\
@@ -1055,6 +1158,15 @@ public:
     RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
     RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
     RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
+    RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f')
+    RAPIDJSON_STRING_(Id, 'i', 'd')
+
+    RAPIDJSON_STRING_(SchemeEnd, ':')
+    RAPIDJSON_STRING_(AuthStart, '/', '/')
+    RAPIDJSON_STRING_(QueryStart, '?')
+    RAPIDJSON_STRING_(FragStart, '#')
+    RAPIDJSON_STRING_(Slash, '/')
+    RAPIDJSON_STRING_(Dot, '.')
 
 #undef RAPIDJSON_STRING_
 
@@ -1120,7 +1232,7 @@ private:
                 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
                 memset(out.schemas, 0, sizeof(Schema*)* out.count);
                 for (SizeType i = 0; i < out.count; i++)
-                    schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
+                    schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_);
                 out.begin = validatorCount_;
                 validatorCount_ += out.count;
             }
@@ -1191,31 +1303,32 @@ private:
             context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
             context.validatorCount = validatorCount_;
 
+            // Always return after first failure for these sub-validators
             if (allOf_.schemas)
-                CreateSchemaValidators(context, allOf_);
+                CreateSchemaValidators(context, allOf_, false);
 
             if (anyOf_.schemas)
-                CreateSchemaValidators(context, anyOf_);
-            
+                CreateSchemaValidators(context, anyOf_, false);
+
             if (oneOf_.schemas)
-                CreateSchemaValidators(context, oneOf_);
-            
+                CreateSchemaValidators(context, oneOf_, false);
+
             if (not_)
-                context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
-            
+                context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false);
+
             if (hasSchemaDependencies_) {
                 for (SizeType i = 0; i < propertyCount_; i++)
                     if (properties_[i].dependenciesSchema)
-                        context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
+                        context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false);
             }
         }
 
         return true;
     }
 
-    void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
+    void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const {
         for (SizeType i = 0; i < schemas.count; i++)
-            context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
+            context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors);
     }
 
     // O(n)
@@ -1223,7 +1336,7 @@ private:
         SizeType len = name.GetStringLength();
         const Ch* str = name.GetString();
         for (SizeType index = 0; index < propertyCount_; index++)
-            if (properties_[index].name.GetStringLength() == len && 
+            if (properties_[index].name.GetStringLength() == len &&
                 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
             {
                 *outIndex = index;
@@ -1235,19 +1348,19 @@ private:
     bool CheckInt(Context& context, int64_t i) const {
         if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
             DisallowedType(context, GetIntegerString());
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
         }
 
         if (!minimum_.IsNull()) {
             if (minimum_.IsInt64()) {
                 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
                     context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
                 }
             }
             else if (minimum_.IsUint64()) {
                 context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
-                RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
+                RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64()
             }
             else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
                 return false;
@@ -1257,7 +1370,7 @@ private:
             if (maximum_.IsInt64()) {
                 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
                     context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
                 }
             }
             else if (maximum_.IsUint64()) { }
@@ -1270,7 +1383,7 @@ private:
             if (multipleOf_.IsUint64()) {
                 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
                     context.error_handler.NotMultipleOf(i, multipleOf_);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
                 }
             }
             else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
@@ -1283,14 +1396,14 @@ private:
     bool CheckUint(Context& context, uint64_t i) const {
         if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
             DisallowedType(context, GetIntegerString());
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
         }
 
         if (!minimum_.IsNull()) {
             if (minimum_.IsUint64()) {
                 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
                     context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
                 }
             }
             else if (minimum_.IsInt64())
@@ -1303,12 +1416,12 @@ private:
             if (maximum_.IsUint64()) {
                 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
                     context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
                 }
             }
             else if (maximum_.IsInt64()) {
                 context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
-                RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
+                RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_
             }
             else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
                 return false;
@@ -1318,7 +1431,7 @@ private:
             if (multipleOf_.IsUint64()) {
                 if (i % multipleOf_.GetUint64() != 0) {
                     context.error_handler.NotMultipleOf(i, multipleOf_);
-                    RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+                    RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
                 }
             }
             else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
@@ -1331,7 +1444,7 @@ private:
     bool CheckDoubleMinimum(Context& context, double d) const {
         if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
             context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
         }
         return true;
     }
@@ -1339,7 +1452,7 @@ private:
     bool CheckDoubleMaximum(Context& context, double d) const {
         if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
             context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
         }
         return true;
     }
@@ -1350,7 +1463,7 @@ private:
         double r = a - q * b;
         if (r > 0.0) {
             context.error_handler.NotMultipleOf(d, multipleOf_);
-            RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+            RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
         }
         return true;
     }
@@ -1384,7 +1497,7 @@ private:
 
     struct PatternProperty {
         PatternProperty() : schema(), pattern() {}
-        ~PatternProperty() { 
+        ~PatternProperty() {
             if (pattern) {
                 pattern->~RegexType();
                 AllocatorType::Free(pattern);
@@ -1396,6 +1509,7 @@ private:
 
     AllocatorType* allocator_;
     SValue uri_;
+    UriType id_;
     PointerType pointer_;
     const SchemaType* typeless_;
     uint64_t* enum_;
@@ -1438,7 +1552,7 @@ private:
     SValue multipleOf_;
     bool exclusiveMinimum_;
     bool exclusiveMaximum_;
-    
+
     SizeType defaultValueLength_;
 };
 
@@ -1481,9 +1595,12 @@ template <typename SchemaDocumentType>
 class IGenericRemoteSchemaDocumentProvider {
 public:
     typedef typename SchemaDocumentType::Ch Ch;
+    typedef typename SchemaDocumentType::ValueType ValueType;
+    typedef typename SchemaDocumentType::AllocatorType AllocatorType;
 
     virtual ~IGenericRemoteSchemaDocumentProvider() {}
     virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
+    virtual const SchemaDocumentType* GetRemoteDocument(GenericUri<ValueType, AllocatorType> uri) { return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); }
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1508,7 +1625,8 @@ public:
     typedef typename EncodingType::Ch Ch;
     typedef internal::Schema<GenericSchemaDocument> SchemaType;
     typedef GenericPointer<ValueType, Allocator> PointerType;
-    typedef GenericValue<EncodingType, Allocator> URIType;
+    typedef GenericValue<EncodingType, AllocatorType> SValue;
+    typedef GenericUri<ValueType, Allocator> UriType;
     friend class internal::Schema<GenericSchemaDocument>;
     template <typename, typename, typename>
     friend class GenericSchemaValidator;
@@ -1522,9 +1640,11 @@ public:
         \param uriLength Length of \c name, in code points.
         \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
         \param allocator An optional allocator instance for allocating memory. Can be null.
+        \param pointer An optional JSON pointer to the start of the schema document
     */
     explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
-        IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
+        IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0,
+        const PointerType& pointer = PointerType()) :  // PR #1393
         remoteProvider_(remoteProvider),
         allocator_(allocator),
         ownAllocator_(),
@@ -1538,30 +1658,20 @@ public:
 
         Ch noUri[1] = {0};
         uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
+        docId_ = UriType(uri_, allocator_);
 
         typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
-        new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_);
+        new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
 
         // Generate root schema, it will call CreateSchema() to create sub-schemas,
-        // And call AddRefSchema() if there are $ref.
-        CreateSchemaRecursive(&root_, PointerType(), document, document);
-
-        // Resolve $ref
-        while (!schemaRef_.Empty()) {
-            SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
-            if (const SchemaType* s = GetSchema(refEntry->target)) {
-                if (refEntry->schema)
-                    *refEntry->schema = s;
-
-                // Create entry in map if not exist
-                if (!GetSchema(refEntry->source)) {
-                    new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
-                }
-            }
-            else if (refEntry->schema)
-                *refEntry->schema = typeless_;
-
-            refEntry->~SchemaRefEntry();
+        // And call HandleRefSchema() if there are $ref.
+        // PR #1393 use input pointer if supplied
+        root_ = typeless_;
+        if (pointer.GetTokenCount() == 0) {
+            CreateSchemaRecursive(&root_, pointer, document, document, docId_);
+        }
+        else if (const ValueType* v = pointer.Get(document)) {
+            CreateSchema(&root_, pointer, *v, document, docId_);
         }
 
         RAPIDJSON_ASSERT(root_ != 0);
@@ -1579,7 +1689,8 @@ public:
         typeless_(rhs.typeless_),
         schemaMap_(std::move(rhs.schemaMap_)),
         schemaRef_(std::move(rhs.schemaRef_)),
-        uri_(std::move(rhs.uri_))
+        uri_(std::move(rhs.uri_)),
+        docId_(rhs.docId_)
     {
         rhs.remoteProvider_ = 0;
         rhs.allocator_ = 0;
@@ -1601,7 +1712,7 @@ public:
         RAPIDJSON_DELETE(ownAllocator_);
     }
 
-    const URIType& GetURI() const { return uri_; }
+    const SValue& GetURI() const { return uri_; }
 
     //! Get the root schema.
     const SchemaType& GetRoot() const { return *root_; }
@@ -1612,12 +1723,7 @@ private:
     //! Prohibit assignment
     GenericSchemaDocument& operator=(const GenericSchemaDocument&);
 
-    struct SchemaRefEntry {
-        SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
-        PointerType source;
-        PointerType target;
-        const SchemaType** schema;
-    };
+    typedef const PointerType* SchemaRefPtr; // PR #1393
 
     struct SchemaEntry {
         SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
@@ -1632,79 +1738,197 @@ private:
         bool owned;
     };
 
-    void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
-        if (schema)
-            *schema = typeless_;
-
+    // Changed by PR #1393
+    void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
         if (v.GetType() == kObjectType) {
-            const SchemaType* s = GetSchema(pointer);
-            if (!s)
-                CreateSchema(schema, pointer, v, document);
+            UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_);
 
             for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
-                CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
+                CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid);
         }
         else if (v.GetType() == kArrayType)
             for (SizeType i = 0; i < v.Size(); i++)
-                CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
+                CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id);
     }
 
-    void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+    // Changed by PR #1393
+    const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
         RAPIDJSON_ASSERT(pointer.IsValid());
         if (v.IsObject()) {
-            if (!HandleRefSchema(pointer, schema, v, document)) {
-                SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
-                new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
+            if (const SchemaType* sc = GetSchema(pointer)) {
+                if (schema)
+                    *schema = sc;
+                AddSchemaRefs(const_cast<SchemaType*>(sc));
+            }
+            else if (!HandleRefSchema(pointer, schema, v, document, id)) {
+                // The new schema constructor adds itself and its $ref(s) to schemaMap_
+                SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id);
                 if (schema)
                     *schema = s;
+                return s->GetId();
             }
         }
+        else {
+            if (schema)
+                *schema = typeless_;
+            AddSchemaRefs(typeless_);
+        }
+        return id;
     }
 
-    bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
-        static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
-        static const ValueType kRefValue(kRefString, 4);
-
-        typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
+    // Changed by PR #1393
+    // TODO should this return a UriType& ?
+    bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) {
+        typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString());
         if (itr == v.MemberEnd())
             return false;
 
+        // Resolve the source pointer to the $ref'ed schema (finally)
+        new (schemaRef_.template Push<SchemaRefPtr>()) SchemaRefPtr(&source);
+
         if (itr->value.IsString()) {
             SizeType len = itr->value.GetStringLength();
             if (len > 0) {
-                const Ch* s = itr->value.GetString();
-                SizeType i = 0;
-                while (i < len && s[i] != '#') // Find the first #
-                    i++;
-
-                if (i > 0) { // Remote reference, resolve immediately
+                // First resolve $ref against the in-scope id
+                UriType scopeId = UriType(id, allocator_);
+                UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
+                // See if the resolved $ref minus the fragment matches a resolved id in this document
+                // Search from the root. Returns the subschema in the document and its absolute JSON pointer.
+                PointerType basePointer = PointerType();
+                const ValueType *base = FindId(document, ref, basePointer, docId_, false);
+                if (!base) {
+                    // Remote reference - call the remote document provider
                     if (remoteProvider_) {
-                        if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
-                            PointerType pointer(&s[i], len - i, allocator_);
-                            if (pointer.IsValid()) {
-                                if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
-                                    if (schema)
-                                        *schema = sc;
-                                    new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(source, const_cast<SchemaType*>(sc), false, allocator_);
+                        if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) {
+                            const Ch* s = ref.GetFragString();
+                            len = ref.GetFragStringLength();
+                            if (len <= 1 || s[1] == '/') {
+                                // JSON pointer fragment, absolute in the remote schema
+                                const PointerType pointer(s, len, allocator_);
+                                if (pointer.IsValid()) {
+                                    // Get the subschema
+                                    if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
+                                        if (schema)
+                                            *schema = sc;
+                                        AddSchemaRefs(const_cast<SchemaType *>(sc));
+                                        return true;
+                                    }
+                                }
+                          } else {
+                            // Plain name fragment, not allowed
+                          }
+                        }
+                    }
+                }
+                else { // Local reference
+                    const Ch* s = ref.GetFragString();
+                    len = ref.GetFragStringLength();
+                    if (len <= 1 || s[1] == '/') {
+                        // JSON pointer fragment, relative to the resolved URI
+                        const PointerType relPointer(s, len, allocator_);
+                        if (relPointer.IsValid()) {
+                            // Get the subschema
+                            if (const ValueType *pv = relPointer.Get(*base)) {
+                                // Now get the absolute JSON pointer by adding relative to base
+                                PointerType pointer(basePointer);
+                                for (SizeType i = 0; i < relPointer.GetTokenCount(); i++)
+                                    pointer = pointer.Append(relPointer.GetTokens()[i], allocator_);
+                                //GenericStringBuffer<EncodingType> sb;
+                                //pointer.StringifyUriFragment(sb);
+                                if (pointer.IsValid() && !IsCyclicRef(pointer)) {
+                                    // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
+                                    // TODO: cache pointer <-> id mapping
+                                    size_t unresolvedTokenIndex;
+                                    scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
+                                    CreateSchema(schema, pointer, *pv, document, scopeId);
                                     return true;
                                 }
                             }
                         }
-                    }
-                }
-                else if (s[i] == '#') { // Local reference, defer resolution
-                    PointerType pointer(&s[i], len - i, allocator_);
-                    if (pointer.IsValid()) {
-                        if (const ValueType* nv = pointer.Get(document))
-                            if (HandleRefSchema(source, schema, *nv, document))
+                    } else {
+                        // Plain name fragment, relative to the resolved URI
+                        // See if the fragment matches an id in this document.
+                        // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer.
+                        PointerType pointer = PointerType();
+                        if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
+                            if (!IsCyclicRef(pointer)) {
+                                //GenericStringBuffer<EncodingType> sb;
+                                //pointer.StringifyUriFragment(sb);
+                                // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
+                                // TODO: cache pointer <-> id mapping
+                                size_t unresolvedTokenIndex;
+                                scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
+                                CreateSchema(schema, pointer, *pv, document, scopeId);
                                 return true;
-
-                        new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
-                        return true;
+                            }
+                        }
                     }
                 }
             }
         }
+
+        // Invalid/Unknown $ref
+        if (schema)
+            *schema = typeless_;
+        AddSchemaRefs(typeless_);
+        return true;
+    }
+
+    //! Find the first subschema with a resolved 'id' that matches the specified URI.
+    // If full specified use all URI else ignore fragment.
+    // If found, return a pointer to the subschema and its JSON pointer.
+    // TODO cache pointer <-> id mapping
+    ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const {
+        SizeType i = 0;
+        ValueType* resval = 0;
+        UriType tempuri = UriType(finduri, allocator_);
+        UriType localuri = UriType(baseuri, allocator_);
+        if (doc.GetType() == kObjectType) {
+            // Establish the base URI of this object
+            typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString());
+            if (m != doc.MemberEnd() && m->value.GetType() == kStringType) {
+                localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_);
+            }
+            // See if it matches
+            if (localuri.Match(finduri, full)) {
+                resval = const_cast<ValueType *>(&doc);
+                resptr = here;
+                return resval;
+            }
+            // No match, continue looking
+            for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) {
+                if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) {
+                    resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_));
+                }
+                if (resval) break;
+            }
+        } else if (doc.GetType() == kArrayType) {
+            // Continue looking
+            for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) {
+                if (v->GetType() == kObjectType || v->GetType() == kArrayType) {
+                    resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_));
+                }
+                if (resval) break;
+                i++;
+            }
+        }
+        return resval;
+    }
+
+    // Added by PR #1393
+    void AddSchemaRefs(SchemaType* schema) {
+        while (!schemaRef_.Empty()) {
+            SchemaRefPtr *ref = schemaRef_.template Pop<SchemaRefPtr>(1);
+            SchemaEntry *entry = schemaMap_.template Push<SchemaEntry>();
+            new (entry) SchemaEntry(**ref, schema, false, allocator_);
+        }
+    }
+
+    // Added by PR #1393
+    bool IsCyclicRef(const PointerType& pointer) const {
+        for (const SchemaRefPtr* ref = schemaRef_.template Bottom<SchemaRefPtr>(); ref != schemaRef_.template End<SchemaRefPtr>(); ++ref)
+            if (pointer == **ref)
+                return true;
         return false;
     }
 
@@ -1733,8 +1957,9 @@ private:
     const SchemaType* root_;                //!< Root schema.
     SchemaType* typeless_;
     internal::Stack<Allocator> schemaMap_;  // Stores created Pointer -> Schemas
-    internal::Stack<Allocator> schemaRef_;  // Stores Pointer from $ref and schema which holds the $ref
-    URIType uri_;
+    internal::Stack<Allocator> schemaRef_;  // Stores Pointer(s) from $ref(s) until resolved
+    SValue uri_;                            // Schema document URI
+    UriType docId_;
 };
 
 //! GenericSchemaDocument using Value type.
@@ -1764,8 +1989,7 @@ template <
 class GenericSchemaValidator :
     public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>, 
     public internal::ISchemaValidator,
-    public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType>
-{
+    public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType> {
 public:
     typedef typename SchemaDocumentType::SchemaType SchemaType;
     typedef typename SchemaDocumentType::PointerType PointerType;
@@ -1798,7 +2022,8 @@ public:
         error_(kObjectType),
         currentError_(),
         missingDependents_(),
-        valid_(true)
+        valid_(true),
+        flags_(kValidateDefaultFlags)
 #if RAPIDJSON_SCHEMA_VERBOSE
         , depth_(0)
 #endif
@@ -1829,7 +2054,8 @@ public:
         error_(kObjectType),
         currentError_(),
         missingDependents_(),
-        valid_(true)
+        valid_(true),
+        flags_(kValidateDefaultFlags)
 #if RAPIDJSON_SCHEMA_VERBOSE
         , depth_(0)
 #endif
@@ -1847,31 +2073,61 @@ public:
         while (!schemaStack_.Empty())
             PopSchema();
         documentStack_.Clear();
+        ResetError();
+    }
+
+    //! Reset the error state.
+    void ResetError() {
         error_.SetObject();
         currentError_.SetNull();
         missingDependents_.SetNull();
         valid_ = true;
     }
 
+    //! Implementation of ISchemaValidator
+    void SetValidateFlags(unsigned flags) {
+        flags_ = flags;
+    }
+    virtual unsigned GetValidateFlags() const {
+        return flags_;
+    }
+
     //! Checks whether the current state is valid.
     // Implementation of ISchemaValidator
-    virtual bool IsValid() const { return valid_; }
+    virtual bool IsValid() const {
+        if (!valid_) return false;
+        if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
+        return true;
+    }
 
     //! Gets the error object.
     ValueType& GetError() { return error_; }
     const ValueType& GetError() const { return error_; }
 
     //! Gets the JSON pointer pointed to the invalid schema.
+    //  If reporting all errors, the stack will be empty.
     PointerType GetInvalidSchemaPointer() const {
         return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
     }
 
     //! Gets the keyword of invalid schema.
+    //  If reporting all errors, the stack will be empty, so return "errors".
     const Ch* GetInvalidSchemaKeyword() const {
-        return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
+        if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword;
+        if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString();
+        return 0;
+    }
+
+    //! Gets the error code of invalid schema.
+    //  If reporting all errors, the stack will be empty, so return kValidateErrors.
+    ValidateErrorCode GetInvalidSchemaCode() const {
+        if (!schemaStack_.Empty()) return CurrentContext().invalidCode;
+        if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors;
+        return kValidateErrorNone;
     }
 
     //! Gets the JSON pointer pointed to the invalid value.
+    //  If reporting all errors, the stack will be empty.
     PointerType GetInvalidDocumentPointer() const {
         if (documentStack_.Empty()) {
             return PointerType();
@@ -1882,64 +2138,64 @@ public:
     }
 
     void NotMultipleOf(int64_t actual, const SValue& expected) {
-        AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
+        AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
     }
     void NotMultipleOf(uint64_t actual, const SValue& expected) {
-        AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
+        AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
     }
     void NotMultipleOf(double actual, const SValue& expected) {
-        AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
+        AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
     }
     void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
-        AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
+        AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
             exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
     }
     void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
-        AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
+        AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
             exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
     }
     void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
-        AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
+        AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
             exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
     }
     void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
-        AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
+        AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
             exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
     }
     void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
-        AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
+        AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
             exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
     }
     void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
-        AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
+        AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
             exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
     }
 
     void TooLong(const Ch* str, SizeType length, SizeType expected) {
-        AddNumberError(SchemaType::GetMaxLengthString(),
+        AddNumberError(kValidateErrorMaxLength,
             ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
     }
     void TooShort(const Ch* str, SizeType length, SizeType expected) {
-        AddNumberError(SchemaType::GetMinLengthString(),
+        AddNumberError(kValidateErrorMinLength,
             ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
     }
     void DoesNotMatch(const Ch* str, SizeType length) {
         currentError_.SetObject();
         currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
-        AddCurrentError(SchemaType::GetPatternString());
+        AddCurrentError(kValidateErrorPattern);
     }
 
     void DisallowedItem(SizeType index) {
         currentError_.SetObject();
         currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
-        AddCurrentError(SchemaType::GetAdditionalItemsString(), true);
+        AddCurrentError(kValidateErrorAdditionalItems, true);
     }
     void TooFewItems(SizeType actualCount, SizeType expectedCount) {
-        AddNumberError(SchemaType::GetMinItemsString(),
+        AddNumberError(kValidateErrorMinItems,
             ValueType(actualCount).Move(), SValue(expectedCount).Move());
     }
     void TooManyItems(SizeType actualCount, SizeType expectedCount) {
-        AddNumberError(SchemaType::GetMaxItemsString(),
+        AddNumberError(kValidateErrorMaxItems,
             ValueType(actualCount).Move(), SValue(expectedCount).Move());
     }
     void DuplicateItems(SizeType index1, SizeType index2) {
@@ -1948,15 +2204,15 @@ public:
         duplicates.PushBack(index2, GetStateAllocator());
         currentError_.SetObject();
         currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
-        AddCurrentError(SchemaType::GetUniqueItemsString(), true);
+        AddCurrentError(kValidateErrorUniqueItems, true);
     }
 
     void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
-        AddNumberError(SchemaType::GetMaxPropertiesString(),
+        AddNumberError(kValidateErrorMaxProperties,
             ValueType(actualCount).Move(), SValue(expectedCount).Move());
     }
     void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
-        AddNumberError(SchemaType::GetMinPropertiesString(),
+        AddNumberError(kValidateErrorMinProperties,
             ValueType(actualCount).Move(), SValue(expectedCount).Move());
     }
     void StartMissingProperties() {
@@ -1971,7 +2227,7 @@ public:
         ValueType error(kObjectType);
         error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
         currentError_ = error;
-        AddCurrentError(SchemaType::GetRequiredString());
+        AddCurrentError(kValidateErrorRequired);
         return true;
     }
     void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
@@ -1981,7 +2237,7 @@ public:
     void DisallowedProperty(const Ch* name, SizeType length) {
         currentError_.SetObject();
         currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
-        AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true);
+        AddCurrentError(kValidateErrorAdditionalProperties, true);
     }
 
     void StartDependencyErrors() {
@@ -1994,9 +2250,20 @@ public:
         missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
     }
     void EndMissingDependentProperties(const SValue& sourceName) {
-        if (!missingDependents_.Empty())
-            currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
-                missingDependents_, GetStateAllocator());
+        if (!missingDependents_.Empty()) {
+            // Create equivalent 'required' error
+            ValueType error(kObjectType);
+            ValidateErrorCode code = kValidateErrorRequired;
+            error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator());
+            AddErrorCode(error, code);
+            AddErrorInstanceLocation(error, false);
+            // When appending to a pointer ensure its allocator is used
+            PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator());
+            AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator()));
+            ValueType wrapper(kObjectType);
+            wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator());
+            currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator());
+        }
     }
     void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
         currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
@@ -2008,13 +2275,13 @@ public:
         ValueType error(kObjectType);
         error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
         currentError_ = error;
-        AddCurrentError(SchemaType::GetDependenciesString());
+        AddCurrentError(kValidateErrorDependencies);
         return true;
     }
 
-    void DisallowedValue() {
+    void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) {
         currentError_.SetObject();
-        AddCurrentError(SchemaType::GetEnumString());
+        AddCurrentError(code);
     }
     void StartDisallowedType() {
         currentError_.SetArray();
@@ -2027,22 +2294,24 @@ public:
         error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
         error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
         currentError_ = error;
-        AddCurrentError(SchemaType::GetTypeString());
+        AddCurrentError(kValidateErrorType);
     }
     void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
-        for (SizeType i = 0; i < count; ++i) {
-            MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
-        }
+        // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf
+        AddErrorArray(kValidateErrorAllOf, subvalidators, count);
+        //for (SizeType i = 0; i < count; ++i) {
+        //    MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
+        //}
     }
     void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
-        AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count);
+        AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
     }
-    void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
-        AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count);
+    void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched = false) {
+        AddErrorArray(matched ? kValidateErrorOneOfMatch : kValidateErrorOneOf, subvalidators, count);
     }
     void Disallowed() {
         currentError_.SetObject();
-        AddCurrentError(SchemaType::GetNotString());
+        AddCurrentError(kValidateErrorNot);
     }
 
 #define RAPIDJSON_STRING_(name, ...) \
@@ -2059,6 +2328,8 @@ public:
     RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
     RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
     RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
+    RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
+    RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
     RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
 
 #undef RAPIDJSON_STRING_
@@ -2076,7 +2347,7 @@ RAPIDJSON_MULTILINEMACRO_END
 
 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
     if (!valid_) return false; \
-    if (!BeginValue() || !CurrentSchema().method arg1) {\
+    if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
         RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
         return valid_ = false;\
     }
@@ -2094,7 +2365,8 @@ RAPIDJSON_MULTILINEMACRO_END
     }
 
 #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
-    return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2)
+    valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\
+    return valid_;
 
 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
     RAPIDJSON_SCHEMA_HANDLE_BEGIN_   (method, arg1);\
@@ -2122,15 +2394,15 @@ RAPIDJSON_MULTILINEMACRO_END
     bool Key(const Ch* str, SizeType len, bool copy) {
         if (!valid_) return false;
         AppendToken(str, len);
-        if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
+        if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) return valid_ = false;
         RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
         return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
     }
     
-    bool EndObject(SizeType memberCount) { 
+    bool EndObject(SizeType memberCount) {
         if (!valid_) return false;
         RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
-        if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
+        if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) return valid_ = false;
         RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
     }
 
@@ -2143,7 +2415,7 @@ RAPIDJSON_MULTILINEMACRO_END
     bool EndArray(SizeType elementCount) {
         if (!valid_) return false;
         RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
-        if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
+        if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) return valid_ = false;
         RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
     }
 
@@ -2153,12 +2425,14 @@ RAPIDJSON_MULTILINEMACRO_END
 #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
 
     // Implementation of ISchemaStateFactory<SchemaType>
-    virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
-        return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
+    virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
+        ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
 #if RAPIDJSON_SCHEMA_VERBOSE
         depth_ + 1,
 #endif
         &GetStateAllocator());
+        sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag);
+        return sv;
     }
 
     virtual void DestroySchemaValidator(ISchemaValidator* validator) {
@@ -2215,7 +2489,8 @@ private:
         error_(kObjectType),
         currentError_(),
         missingDependents_(),
-        valid_(true)
+        valid_(true),
+        flags_(kValidateDefaultFlags)
 #if RAPIDJSON_SCHEMA_VERBOSE
         , depth_(depth)
 #endif
@@ -2230,6 +2505,10 @@ private:
         return *stateAllocator_;
     }
 
+    bool GetContinueOnErrors() const {
+        return flags_ & kValidateContinueOnErrorFlag;
+    }
+
     bool BeginValue() {
         if (schemaStack_.Empty())
             PushSchema(root_);
@@ -2237,7 +2516,7 @@ private:
             if (CurrentContext().inArray)
                 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
 
-            if (!CurrentSchema().BeginValue(CurrentContext()))
+            if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors())
                 return false;
 
             SizeType count = CurrentContext().patternPropertiesSchemaCount;
@@ -2253,7 +2532,7 @@ private:
                 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
                 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
                 for (SizeType i = 0; i < count; i++)
-                    va[validatorCount++] = CreateSchemaValidator(*sa[i]);
+                    va[validatorCount++] = CreateSchemaValidator(*sa[i], true);  // Inherit continueOnError
             }
 
             CurrentContext().arrayUniqueness = valueUniqueness;
@@ -2262,7 +2541,7 @@ private:
     }
 
     bool EndValue() {
-        if (!CurrentSchema().EndValue(CurrentContext()))
+        if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
             return false;
 
 #if RAPIDJSON_SCHEMA_VERBOSE
@@ -2273,21 +2552,27 @@ private:
         documentStack_.template Pop<Ch>(1);
         internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
 #endif
-
-        uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
+        void* hasher = CurrentContext().hasher;
+        uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
         
         PopSchema();
 
         if (!schemaStack_.Empty()) {
             Context& context = CurrentContext();
-            if (context.valueUniqueness) {
+            // Only check uniqueness if there is a hasher
+            if (hasher && context.valueUniqueness) {
                 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
                 if (!a)
                     CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
                 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
                     if (itr->GetUint64() == h) {
                         DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
-                        RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
+                        // Cleanup before returning if continuing
+                        if (GetContinueOnErrors()) {
+                            a->PushBack(h, GetStateAllocator());
+                            while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/');
+                        }
+                        RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems);
                     }
                 a->PushBack(h, GetStateAllocator());
             }
@@ -2328,25 +2613,32 @@ private:
         c->~Context();
     }
 
-    void AddErrorLocation(ValueType& result, bool parent) {
+    void AddErrorInstanceLocation(ValueType& result, bool parent) {
         GenericStringBuffer<EncodingType> sb;
         PointerType instancePointer = GetInvalidDocumentPointer();
         ((parent && instancePointer.GetTokenCount() > 0)
-            ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
-            : instancePointer).StringifyUriFragment(sb);
+         ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
+         : instancePointer).StringifyUriFragment(sb);
         ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
-            GetStateAllocator());
+                              GetStateAllocator());
         result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
-        sb.Clear();
-        memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()),
-            CurrentSchema().GetURI().GetString(),
-            CurrentSchema().GetURI().GetStringLength() * sizeof(Ch));
-        GetInvalidSchemaPointer().StringifyUriFragment(sb);
+    }
+
+    void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) {
+        GenericStringBuffer<EncodingType> sb;
+        SizeType len = CurrentSchema().GetURI().GetStringLength();
+        if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch));
+        if (schema.GetTokenCount()) schema.StringifyUriFragment(sb);
+        else GetInvalidSchemaPointer().StringifyUriFragment(sb);
         ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
             GetStateAllocator());
         result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
     }
 
+    void AddErrorCode(ValueType& result, const ValidateErrorCode code) {
+        result.AddMember(GetErrorCodeString(), code, GetStateAllocator());
+    }
+
     void AddError(ValueType& keyword, ValueType& error) {
         typename ValueType::MemberIterator member = error_.FindMember(keyword);
         if (member == error_.MemberEnd())
@@ -2361,9 +2653,11 @@ private:
         }
     }
 
-    void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) {
-        AddErrorLocation(currentError_, parent);
-        AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_);
+    void AddCurrentError(const ValidateErrorCode code, bool parent = false) {
+        AddErrorCode(currentError_, code);
+        AddErrorInstanceLocation(currentError_, parent);
+        AddErrorSchemaLocation(currentError_);
+        AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_);
     }
 
     void MergeError(ValueType& other) {
@@ -2372,24 +2666,24 @@ private:
         }
     }
 
-    void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected,
+    void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected,
         const typename SchemaType::ValueType& (*exclusive)() = 0) {
         currentError_.SetObject();
         currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
         currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
         if (exclusive)
             currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
-        AddCurrentError(keyword);
+        AddCurrentError(code);
     }
 
-    void AddErrorArray(const typename SchemaType::ValueType& keyword,
+    void AddErrorArray(const ValidateErrorCode code,
         ISchemaValidator** subvalidators, SizeType count) {
         ValueType errors(kArrayType);
         for (SizeType i = 0; i < count; ++i)
             errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
         currentError_.SetObject();
         currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
-        AddCurrentError(keyword);
+        AddCurrentError(code);
     }
 
     const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
@@ -2409,6 +2703,7 @@ private:
     ValueType currentError_;
     ValueType missingDependents_;
     bool valid_;
+    unsigned flags_;
 #if RAPIDJSON_SCHEMA_VERBOSE
     unsigned depth_;
 #endif
@@ -2446,7 +2741,7 @@ public:
         \param is Input stream.
         \param sd Schema document.
     */
-    SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {}
+    SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {}
 
     template <typename Handler>
     bool operator()(Handler& handler) {
@@ -2464,6 +2759,7 @@ public:
         else {
             invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
             invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
+            invalidSchemaCode_ = validator.GetInvalidSchemaCode();
             invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
             error_.CopyFrom(validator.GetError(), allocator_);
         }
@@ -2477,6 +2773,7 @@ public:
     const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
     const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
     const ValueType& GetError() const { return error_; }
+    ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; }
 
 private:
     InputStream& is_;
@@ -2486,6 +2783,7 @@ private:
     PointerType invalidSchemaPointer_;
     const Ch* invalidSchemaKeyword_;
     PointerType invalidDocumentPointer_;
+    ValidateErrorCode invalidSchemaCode_;
     StackAllocator allocator_;
     ValueType error_;
     bool isValid_;
diff --git a/src/3rdparty/rapidjson/stream.h b/src/3rdparty/rapidjson/stream.h
index 7f2643e48..1fd70915c 100644
--- a/src/3rdparty/rapidjson/stream.h
+++ b/src/3rdparty/rapidjson/stream.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 //
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/stringbuffer.h b/src/3rdparty/rapidjson/stringbuffer.h
index 4e38b82c3..82ad3ca6b 100644
--- a/src/3rdparty/rapidjson/stringbuffer.h
+++ b/src/3rdparty/rapidjson/stringbuffer.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
diff --git a/src/3rdparty/rapidjson/uri.h b/src/3rdparty/rapidjson/uri.h
new file mode 100644
index 000000000..7de7b805c
--- /dev/null
+++ b/src/3rdparty/rapidjson/uri.h
@@ -0,0 +1,466 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// (C) Copyright IBM Corporation 2021
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_URI_H_
+#define RAPIDJSON_URI_H_
+
+#include "internal/strfunc.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#elif defined(_MSC_VER)
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericUri
+
+template <typename ValueType, typename Allocator=CrtAllocator>
+class GenericUri {
+public:
+    typedef typename ValueType::Ch Ch;
+#if RAPIDJSON_HAS_STDSTRING
+    typedef std::basic_string<Ch> String;
+#endif
+
+    //! Constructors
+    GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
+    }
+
+    GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
+        Parse(uri, len);
+    }
+
+    GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
+        Parse(uri, internal::StrLen<Ch>(uri));
+    }
+
+    // Use with specializations of GenericValue
+    template<typename T> GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
+        const Ch* u = uri.template Get<const Ch*>(); // TypeHelper from document.h
+        Parse(u, internal::StrLen<Ch>(u));
+    }
+
+#if RAPIDJSON_HAS_STDSTRING
+    GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
+        Parse(uri.c_str(), internal::StrLen<Ch>(uri.c_str()));
+    }
+#endif
+
+    //! Copy constructor
+    GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() {
+        *this = rhs;
+    }
+
+    //! Copy constructor
+    GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
+        *this = rhs;
+    }
+
+    //! Destructor.
+    ~GenericUri() {
+        Free();
+        RAPIDJSON_DELETE(ownAllocator_);
+    }
+
+    //! Assignment operator
+    GenericUri& operator=(const GenericUri& rhs) {
+        if (this != &rhs) {
+            // Do not delete ownAllocator
+            Free();
+            Allocate(rhs.GetStringLength());
+            auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength());
+            path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength());
+            query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength());
+            frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength());
+            base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength());
+            uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength());
+            CopyPart(uri_, rhs.uri_, rhs.GetStringLength());
+        }
+        return *this;
+    }
+
+    //! Getters
+    // Use with specializations of GenericValue
+    template<typename T> void Get(T& uri, Allocator& allocator) {
+        uri.template Set<const Ch*>(this->GetString(), allocator); // TypeHelper from document.h
+    }
+
+    const Ch* GetString() const { return uri_; }
+    SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen<Ch>(uri_); }
+    const Ch* GetBaseString() const { return base_; }
+    SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen<Ch>(base_); }
+    const Ch* GetSchemeString() const { return scheme_; }
+    SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen<Ch>(scheme_); }
+    const Ch* GetAuthString() const { return auth_; }
+    SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen<Ch>(auth_); }
+    const Ch* GetPathString() const { return path_; }
+    SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen<Ch>(path_); }
+    const Ch* GetQueryString() const { return query_; }
+    SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen<Ch>(query_); }
+    const Ch* GetFragString() const { return frag_; }
+    SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen<Ch>(frag_); }
+
+#if RAPIDJSON_HAS_STDSTRING
+    static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); }
+    static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); }
+    static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); }
+    static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); }
+    static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); }
+    static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); }
+    static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); }
+#endif
+
+    //! Equality operators
+    bool operator==(const GenericUri& rhs) const {
+        return Match(rhs, true);
+    }
+
+    bool operator!=(const GenericUri& rhs) const {
+        return !Match(rhs, true);
+    }
+
+    bool Match(const GenericUri& uri, bool full = true) const {
+        Ch* s1;
+        Ch* s2;
+        if (full) {
+            s1 = uri_;
+            s2 = uri.uri_;
+        } else {
+            s1 = base_;
+            s2 = uri.base_;
+        }
+        if (s1 == s2) return true;
+        if (s1 == 0 || s2 == 0) return false;
+        return internal::StrCmp<Ch>(s1, s2) == 0;
+    }
+
+    //! Resolve this URI against another (base) URI in accordance with URI resolution rules.
+    // See https://tools.ietf.org/html/rfc3986
+    // Use for resolving an id or $ref with an in-scope id.
+    // Returns a new GenericUri for the resolved URI.
+    GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) {
+        GenericUri resuri;
+        resuri.allocator_ = allocator;
+        // Ensure enough space for combining paths
+        resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash
+
+        if (!(GetSchemeStringLength() == 0)) {
+            // Use all of this URI
+            resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength());
+            resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
+            resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
+            resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
+            resuri.RemoveDotSegments();
+        } else {
+            // Use the base scheme
+            resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength());
+            if (!(GetAuthStringLength() == 0)) {
+                // Use this auth, path, query
+                resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
+                resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
+                resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
+                resuri.RemoveDotSegments();
+            } else {
+                // Use the base auth
+                resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength());
+                if (GetPathStringLength() == 0) {
+                    // Use the base path
+                    resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength());
+                    if (GetQueryStringLength() == 0) {
+                        // Use the base query
+                        resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength());
+                    } else {
+                        // Use this query
+                        resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
+                    }
+                } else {
+                    if (path_[0] == '/') {
+                        // Absolute path - use all of this path
+                        resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
+                        resuri.RemoveDotSegments();
+                    } else {
+                        // Relative path - append this path to base path after base path's last slash
+                        size_t pos = 0;
+                        if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) {
+                            resuri.path_[pos] = '/';
+                            pos++;
+                        }
+                        size_t lastslashpos = baseuri.GetPathStringLength();
+                        while (lastslashpos > 0) {
+                            if (baseuri.path_[lastslashpos - 1] == '/') break;
+                            lastslashpos--;
+                        }
+                        std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch));
+                        pos += lastslashpos;
+                        resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength());
+                        resuri.RemoveDotSegments();
+                    }
+                    // Use this query
+                    resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
+                }
+            }
+        }
+        // Always use this frag
+        resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength());
+
+        // Re-constitute base_ and uri_
+        resuri.SetBase();
+        resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1;
+        resuri.SetUri();
+        return resuri;
+    }
+
+    //! Get the allocator of this GenericUri.
+    Allocator& GetAllocator() { return *allocator_; }
+
+private:
+    // Allocate memory for a URI
+    // Returns total amount allocated
+    std::size_t Allocate(std::size_t len) {
+        // Create own allocator if user did not supply.
+        if (!allocator_)
+            ownAllocator_ =  allocator_ = RAPIDJSON_NEW(Allocator)();
+
+        // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated.
+        // Order: scheme, auth, path, query, frag, base, uri
+        size_t total = (3 * len + 7) * sizeof(Ch);
+        scheme_ = static_cast<Ch*>(allocator_->Malloc(total));
+        *scheme_ = '\0';
+        auth_ = scheme_ + 1;
+        *auth_ = '\0';
+        path_ = auth_ + 1;
+        *path_ = '\0';
+        query_ = path_ + 1;
+        *query_ = '\0';
+        frag_ = query_ + 1;
+        *frag_ = '\0';
+        base_ = frag_ + 1;
+        *base_ = '\0';
+        uri_ = base_ + 1;
+        *uri_ = '\0';
+        return total;
+    }
+
+    // Free memory for a URI
+    void Free() {
+        if (scheme_) {
+            Allocator::Free(scheme_);
+            scheme_ = 0;
+        }
+    }
+
+    // Parse a URI into constituent scheme, authority, path, query, & fragment parts
+    // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per
+    // https://tools.ietf.org/html/rfc3986
+    void Parse(const Ch* uri, std::size_t len) {
+        std::size_t start = 0, pos1 = 0, pos2 = 0;
+        Allocate(len);
+
+        // Look for scheme ([^:/?#]+):)?
+        if (start < len) {
+            while (pos1 < len) {
+                if (uri[pos1] == ':') break;
+                pos1++;
+            }
+            if (pos1 != len) {
+                while (pos2 < len) {
+                    if (uri[pos2] == '/') break;
+                    if (uri[pos2] == '?') break;
+                    if (uri[pos2] == '#') break;
+                    pos2++;
+                }
+                if (pos1 < pos2) {
+                    pos1++;
+                    std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch));
+                    scheme_[pos1] = '\0';
+                    start = pos1;
+                }
+            }
+        }
+        // Look for auth (//([^/?#]*))?
+        auth_ = scheme_ + GetSchemeStringLength() + 1;
+        *auth_ = '\0';
+        if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') {
+            pos2 = start + 2;
+            while (pos2 < len) {
+                if (uri[pos2] == '/') break;
+                if (uri[pos2] == '?') break;
+                if (uri[pos2] == '#') break;
+                pos2++;
+            }
+            std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch));
+            auth_[pos2 - start] = '\0';
+            start = pos2;
+        }
+        // Look for path ([^?#]*)
+        path_ = auth_ + GetAuthStringLength() + 1;
+        *path_ = '\0';
+        if (start < len) {
+            pos2 = start;
+            while (pos2 < len) {
+                if (uri[pos2] == '?') break;
+                if (uri[pos2] == '#') break;
+                pos2++;
+            }
+            if (start != pos2) {
+                std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch));
+                path_[pos2 - start] = '\0';
+                if (path_[0] == '/')
+                    RemoveDotSegments();   // absolute path - normalize
+                start = pos2;
+            }
+        }
+        // Look for query (\?([^#]*))?
+        query_ = path_ + GetPathStringLength() + 1;
+        *query_ = '\0';
+        if (start < len && uri[start] == '?') {
+            pos2 = start + 1;
+            while (pos2 < len) {
+                if (uri[pos2] == '#') break;
+                pos2++;
+            }
+            if (start != pos2) {
+                std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch));
+                query_[pos2 - start] = '\0';
+                start = pos2;
+            }
+        }
+        // Look for fragment (#(.*))?
+        frag_ = query_ + GetQueryStringLength() + 1;
+        *frag_ = '\0';
+        if (start < len && uri[start] == '#') {
+            std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch));
+            frag_[len - start] = '\0';
+        }
+
+        // Re-constitute base_ and uri_
+        base_ = frag_ + GetFragStringLength() + 1;
+        SetBase();
+        uri_ = base_ + GetBaseStringLength() + 1;
+        SetUri();
+    }
+
+    // Reconstitute base
+    void SetBase() {
+        Ch* next = base_;
+        std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch));
+        next+= GetSchemeStringLength();
+        std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch));
+        next+= GetAuthStringLength();
+        std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch));
+        next+= GetPathStringLength();
+        std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch));
+        next+= GetQueryStringLength();
+        *next = '\0';
+    }
+
+    // Reconstitute uri
+    void SetUri() {
+        Ch* next = uri_;
+        std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch));
+        next+= GetBaseStringLength();
+        std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch));
+        next+= GetFragStringLength();
+        *next = '\0';
+    }
+
+    // Copy a part from one GenericUri to another
+    // Return the pointer to the next part to be copied to
+    Ch* CopyPart(Ch* to, Ch* from, std::size_t len) {
+        RAPIDJSON_ASSERT(to != 0);
+        RAPIDJSON_ASSERT(from != 0);
+        std::memcpy(to, from, len * sizeof(Ch));
+        to[len] = '\0';
+        Ch* next = to + len + 1;
+        return next;
+    }
+
+    // Remove . and .. segments from the path_ member.
+    // https://tools.ietf.org/html/rfc3986
+    // This is done in place as we are only removing segments.
+    void RemoveDotSegments() {
+        std::size_t pathlen = GetPathStringLength();
+        std::size_t pathpos = 0;  // Position in path_
+        std::size_t newpos = 0;   // Position in new path_
+
+        // Loop through each segment in original path_
+        while (pathpos < pathlen) {
+            // Get next segment, bounded by '/' or end
+            size_t slashpos = 0;
+            while ((pathpos + slashpos) < pathlen) {
+                if (path_[pathpos + slashpos] == '/') break;
+                slashpos++;
+            }
+            // Check for .. and . segments
+            if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') {
+                // Backup a .. segment in the new path_
+                // We expect to find a previously added slash at the end or nothing
+                RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/');
+                size_t lastslashpos = newpos;
+                // Make sure we don't go beyond the start segment
+                if (lastslashpos > 1) {
+                    // Find the next to last slash and back up to it
+                    lastslashpos--;
+                    while (lastslashpos > 0) {
+                        if (path_[lastslashpos - 1] == '/') break;
+                        lastslashpos--;
+                    }
+                    // Set the new path_ position
+                    newpos = lastslashpos;
+                }
+            } else if (slashpos == 1 && path_[pathpos] == '.') {
+                // Discard . segment, leaves new path_ unchanged
+            } else {
+                // Move any other kind of segment to the new path_
+                RAPIDJSON_ASSERT(newpos <= pathpos);
+                std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch));
+                newpos += slashpos;
+                // Add slash if not at end
+                if ((pathpos + slashpos) < pathlen) {
+                    path_[newpos] = '/';
+                    newpos++;
+                }
+            }
+            // Move to next segment
+            pathpos += slashpos + 1;
+        }
+        path_[newpos] = '\0';
+    }
+
+    Ch* uri_;    // Everything
+    Ch* base_;   // Everything except fragment
+    Ch* scheme_; // Includes the :
+    Ch* auth_;   // Includes the //
+    Ch* path_;   // Absolute if starts with /
+    Ch* query_;  // Includes the ?
+    Ch* frag_;   // Includes the #
+
+    Allocator* allocator_;      //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
+    Allocator* ownAllocator_;   //!< Allocator owned by this Uri.
+};
+
+//! GenericUri for Value (UTF-8, default allocator).
+typedef GenericUri<Value> Uri;
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_URI_H_
diff --git a/src/3rdparty/rapidjson/writer.h b/src/3rdparty/rapidjson/writer.h
index 1d33b2f92..35d49088c 100644
--- a/src/3rdparty/rapidjson/writer.h
+++ b/src/3rdparty/rapidjson/writer.h
@@ -1,6 +1,6 @@
 // Tencent is pleased to support the open source community by making RapidJSON available.
 // 
-// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
 //
 // Licensed under the MIT License (the "License"); you may not use this file except
 // in compliance with the License. You may obtain a copy of the License at
@@ -16,6 +16,7 @@
 #define RAPIDJSON_WRITER_H_
 
 #include "stream.h"
+#include "internal/clzll.h"
 #include "internal/meta.h"
 #include "internal/stack.h"
 #include "internal/strfunc.h"
@@ -226,7 +227,7 @@ public:
       return Key(str.data(), SizeType(str.size()));
     }
 #endif
-	
+
     bool EndObject(SizeType memberCount = 0) {
         (void)memberCount;
         RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object
@@ -282,6 +283,8 @@ public:
         os_->Flush();
     }
 
+    static const size_t kDefaultLevelDepth = 32;
+
 protected:
     //! Information for each nested level
     struct Level {
@@ -291,8 +294,6 @@ protected:
         bool inLine = false;
     };
 
-    static const size_t kDefaultLevelDepth = 32;
-
     bool WriteNull()  {
         PutReserve(*os_, 4);
         PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
@@ -669,19 +670,19 @@ inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, siz
         x = vorrq_u8(x, vcltq_u8(s, s3));
 
         x = vrev64q_u8(x);                     // Rev in 64
-        uint64_t low = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 0);   // extract
-        uint64_t high = vgetq_lane_u64(reinterpret_cast<uint64x2_t>(x), 1);  // extract
+        uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0);   // extract
+        uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1);  // extract
 
         SizeType len = 0;
         bool escaped = false;
         if (low == 0) {
             if (high != 0) {
-                unsigned lz = (unsigned)__builtin_clzll(high);
+                uint32_t lz = internal::clzll(high);
                 len = 8 + (lz >> 3);
                 escaped = true;
             }
         } else {
-            unsigned lz = (unsigned)__builtin_clzll(low);
+            uint32_t lz = internal::clzll(low);
             len = lz >> 3;
             escaped = true;
         }
diff --git a/src/base/io/json/JsonChain.cpp b/src/base/io/json/JsonChain.cpp
index 9f9204648..156899efd 100644
--- a/src/base/io/json/JsonChain.cpp
+++ b/src/base/io/json/JsonChain.cpp
@@ -174,7 +174,6 @@ const rapidjson::Value &xmrig::JsonChain::getValue(const char *key) const
 }
 
 
-
 const rapidjson::Value &xmrig::JsonChain::object() const
 {
     assert(false);
diff --git a/src/base/io/json/JsonChain.h b/src/base/io/json/JsonChain.h
index d7fc2f05a..f56d9184a 100644
--- a/src/base/io/json/JsonChain.h
+++ b/src/base/io/json/JsonChain.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/io/json/JsonRequest.cpp b/src/base/io/json/JsonRequest.cpp
index 2bfdda001..c158f27d4 100644
--- a/src/base/io/json/JsonRequest.cpp
+++ b/src/base/io/json/JsonRequest.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/io/json/JsonRequest.h"
 #include "3rdparty/rapidjson/document.h"
 
diff --git a/src/base/io/json/JsonRequest.h b/src/base/io/json/JsonRequest.h
index 21451a43e..7599cf9aa 100644
--- a/src/base/io/json/JsonRequest.h
+++ b/src/base/io/json/JsonRequest.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/io/json/Json_unix.cpp b/src/base/io/json/Json_unix.cpp
index d2a2f351b..25ce2262f 100644
--- a/src/base/io/json/Json_unix.cpp
+++ b/src/base/io/json/Json_unix.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <fstream>
 
 
diff --git a/src/base/io/json/Json_win.cpp b/src/base/io/json/Json_win.cpp
index cd7cf5844..499ae1298 100644
--- a/src/base/io/json/Json_win.cpp
+++ b/src/base/io/json/Json_win.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <windows.h>
 
 
diff --git a/src/base/kernel/interfaces/IJsonReader.h b/src/base/kernel/interfaces/IJsonReader.h
index 044a291c0..cc835702f 100644
--- a/src/base/kernel/interfaces/IJsonReader.h
+++ b/src/base/kernel/interfaces/IJsonReader.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/kernel/interfaces/ILogBackend.h b/src/base/kernel/interfaces/ILogBackend.h
index 88137fd1f..ed6e68bc9 100644
--- a/src/base/kernel/interfaces/ILogBackend.h
+++ b/src/base/kernel/interfaces/ILogBackend.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/tools/Chrono.h b/src/base/tools/Chrono.h
index 99ad19cfd..78da18c15 100644
--- a/src/base/tools/Chrono.h
+++ b/src/base/tools/Chrono.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/tools/Object.h b/src/base/tools/Object.h
index 412ae6ff5..00bd9315a 100644
--- a/src/base/tools/Object.h
+++ b/src/base/tools/Object.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
diff --git a/src/base/tools/String.cpp b/src/base/tools/String.cpp
index f001ff8fa..2d9ef4da1 100644
--- a/src/base/tools/String.cpp
+++ b/src/base/tools/String.cpp
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
  *   along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "base/tools/String.h"
 #include "3rdparty/rapidjson/document.h"
 
diff --git a/src/base/tools/String.h b/src/base/tools/String.h
index 4ba603cec..59d6f1a26 100644
--- a/src/base/tools/String.h
+++ b/src/base/tools/String.h
@@ -1,6 +1,6 @@
 /* XMRig
- * Copyright (c) 2018-2020 SChernykh   <https://github.com/SChernykh>
- * Copyright (c) 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by

From 5fdf5516ff36c0543442a062a328ef4e18e4ba7b Mon Sep 17 00:00:00 2001
From: Chris <chriskobasiuk@gmail.com>
Date: Fri, 27 Aug 2021 08:19:54 -0600
Subject: [PATCH 10/20] Added Graft RandonX

---
 scripts/generate_cl.js                        |  1 +
 src/backend/opencl/cl/cn/algorithm.cl         |  1 +
 src/backend/opencl/cl/rx/randomx.cl           |  2 +
 .../opencl/cl/rx/randomx_constants_graft.h    | 96 +++++++++++++++++++
 src/base/crypto/Algorithm.cpp                 |  6 +-
 src/base/crypto/Algorithm.h                   |  2 +
 src/base/crypto/Coin.cpp                      |  4 +
 src/base/crypto/Coin.h                        |  1 +
 src/crypto/randomx/randomx.cpp                | 10 ++
 src/crypto/randomx/randomx.h                  |  2 +
 src/crypto/rx/RxAlgo.cpp                      |  3 +
 11 files changed, 127 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/opencl/cl/rx/randomx_constants_graft.h

diff --git a/scripts/generate_cl.js b/scripts/generate_cl.js
index 7e19290b5..c3fffbf98 100644
--- a/scripts/generate_cl.js
+++ b/scripts/generate_cl.js
@@ -51,6 +51,7 @@ function rx()
         'randomx_constants_wow.h',
         'randomx_constants_arqma.h',
         'randomx_constants_keva.h',
+        'randomx_constants_graft.h',
         'aes.cl',
         'blake2b.cl',
         'randomx_vm.cl',
diff --git a/src/backend/opencl/cl/cn/algorithm.cl b/src/backend/opencl/cl/cn/algorithm.cl
index 6253f40cf..dcf43d445 100644
--- a/src/backend/opencl/cl/cn/algorithm.cl
+++ b/src/backend/opencl/cl/cn/algorithm.cl
@@ -23,6 +23,7 @@
 #define ALGO_RX_ARQMA       0x72121061
 #define ALGO_RX_SFX         0x72151273
 #define ALGO_RX_KEVA        0x7214116b
+#define ALGO_RX_GRAFT       0x7257ef81
 #define ALGO_AR2_CHUKWA     0x61130000
 #define ALGO_AR2_CHUKWA_V2  0x61140000
 #define ALGO_AR2_WRKZ       0x61120000
diff --git a/src/backend/opencl/cl/rx/randomx.cl b/src/backend/opencl/cl/rx/randomx.cl
index 18c3b1812..a08b8ba27 100644
--- a/src/backend/opencl/cl/rx/randomx.cl
+++ b/src/backend/opencl/cl/rx/randomx.cl
@@ -8,6 +8,8 @@
 #include "randomx_constants_arqma.h"
 #elif (ALGO == ALGO_RX_KEVA)
 #include "randomx_constants_keva.h"
+#elif (ALGO == ALGO_RX_GRAFT)
+#include "randomx_constants_graft.h"
 #endif
 
 #include "aes.cl"
diff --git a/src/backend/opencl/cl/rx/randomx_constants_graft.h b/src/backend/opencl/cl/rx/randomx_constants_graft.h
new file mode 100644
index 000000000..ab0647b17
--- /dev/null
+++ b/src/backend/opencl/cl/rx/randomx_constants_graft.h
@@ -0,0 +1,96 @@
+/*
+Copyright (c) 2019 SChernykh
+
+This file is part of RandomX OpenCL.
+
+RandomX OpenCL is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+RandomX OpenCL is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with RandomX OpenCL. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+//Dataset base size in bytes. Must be a power of 2.
+#define RANDOMX_DATASET_BASE_SIZE  2147483648
+
+//Dataset extra size. Must be divisible by 64.
+#define RANDOMX_DATASET_EXTRA_SIZE 33554368
+
+//Scratchpad L3 size in bytes. Must be a power of 2.
+#define RANDOMX_SCRATCHPAD_L3      2097152
+
+//Scratchpad L2 size in bytes. Must be a power of two and less than or equal to RANDOMX_SCRATCHPAD_L3.
+#define RANDOMX_SCRATCHPAD_L2      262144
+
+//Scratchpad L1 size in bytes. Must be a power of two (minimum 64) and less than or equal to RANDOMX_SCRATCHPAD_L2.
+#define RANDOMX_SCRATCHPAD_L1      16384
+
+//Jump condition mask size in bits.
+#define RANDOMX_JUMP_BITS          8
+
+//Jump condition mask offset in bits. The sum of RANDOMX_JUMP_BITS and RANDOMX_JUMP_OFFSET must not exceed 16.
+#define RANDOMX_JUMP_OFFSET        8
+
+//Integer instructions
+#define RANDOMX_FREQ_IADD_RS       16
+#define RANDOMX_FREQ_IADD_M         7
+#define RANDOMX_FREQ_ISUB_R        16
+#define RANDOMX_FREQ_ISUB_M         7
+#define RANDOMX_FREQ_IMUL_R        16
+#define RANDOMX_FREQ_IMUL_M         4
+#define RANDOMX_FREQ_IMULH_R        4
+#define RANDOMX_FREQ_IMULH_M        1
+#define RANDOMX_FREQ_ISMULH_R       4
+#define RANDOMX_FREQ_ISMULH_M       1
+#define RANDOMX_FREQ_IMUL_RCP       8
+#define RANDOMX_FREQ_INEG_R         2
+#define RANDOMX_FREQ_IXOR_R        15
+#define RANDOMX_FREQ_IXOR_M         5
+#define RANDOMX_FREQ_IROR_R         7
+#define RANDOMX_FREQ_IROL_R         3
+#define RANDOMX_FREQ_ISWAP_R        4
+
+//Floating point instructions
+#define RANDOMX_FREQ_FSWAP_R        4
+#define RANDOMX_FREQ_FADD_R        16
+#define RANDOMX_FREQ_FADD_M         5
+#define RANDOMX_FREQ_FSUB_R        16
+#define RANDOMX_FREQ_FSUB_M         5
+#define RANDOMX_FREQ_FSCAL_R        6
+#define RANDOMX_FREQ_FMUL_R        32
+#define RANDOMX_FREQ_FDIV_M         4
+#define RANDOMX_FREQ_FSQRT_R        6
+
+//Control instructions
+#define RANDOMX_FREQ_CBRANCH       25
+#define RANDOMX_FREQ_CFROUND        1
+
+//Store instruction
+#define RANDOMX_FREQ_ISTORE        16
+
+//No-op instruction
+#define RANDOMX_FREQ_NOP            0
+
+#define RANDOMX_DATASET_ITEM_SIZE 64
+
+#define RANDOMX_PROGRAM_SIZE 280
+
+#define HASH_SIZE 64
+#define ENTROPY_SIZE (128 + RANDOMX_PROGRAM_SIZE * 8)
+#define REGISTERS_SIZE 256
+#define IMM_BUF_SIZE (RANDOMX_PROGRAM_SIZE * 4 - REGISTERS_SIZE)
+#define IMM_INDEX_COUNT ((IMM_BUF_SIZE / 4) - 2)
+#define VM_STATE_SIZE (REGISTERS_SIZE + IMM_BUF_SIZE + RANDOMX_PROGRAM_SIZE * 4)
+#define ROUNDING_MODE (RANDOMX_FREQ_CFROUND ? -1 : 0)
+
+// Scratchpad L1/L2/L3 bits
+#define LOC_L1 (32 - 14)
+#define LOC_L2 (32 - 18)
+#define LOC_L3 (32 - 21)
diff --git a/src/base/crypto/Algorithm.cpp b/src/base/crypto/Algorithm.cpp
index 18adcf4e0..dd2df673d 100644
--- a/src/base/crypto/Algorithm.cpp
+++ b/src/base/crypto/Algorithm.cpp
@@ -79,6 +79,7 @@ const char *Algorithm::kRX              = "rx";
 const char *Algorithm::kRX_0            = "rx/0";
 const char *Algorithm::kRX_WOW          = "rx/wow";
 const char *Algorithm::kRX_ARQ          = "rx/arq";
+const char *Algorithm::kRX_GRAFT        = "rx/graft";
 const char *Algorithm::kRX_SFX          = "rx/sfx";
 const char *Algorithm::kRX_KEVA         = "rx/keva";
 #endif
@@ -149,6 +150,7 @@ static const std::map<uint32_t, const char *> kAlgorithmNames = {
     ALGO_NAME(RX_0),
     ALGO_NAME(RX_WOW),
     ALGO_NAME(RX_ARQ),
+    ALGO_NAME(RX_GRAFT),
     ALGO_NAME(RX_SFX),
     ALGO_NAME(RX_KEVA),
 #   endif
@@ -260,6 +262,8 @@ static const std::map<const char *, Algorithm::Id, aliasCompare> kAlgorithmAlias
                                     ALGO_ALIAS(RX_WOW,          "randomwow"),
     ALGO_ALIAS_AUTO(RX_ARQ),        ALGO_ALIAS(RX_ARQ,          "randomx/arq"),
                                     ALGO_ALIAS(RX_ARQ,          "randomarq"),
+    ALGO_ALIAS_AUTO(RX_GRAFT),      ALGO_ALIAS(RX_GRAFT,        "randomx/graft"),
+                                    ALGO_ALIAS(RX_GRAFT,        "randomgraft"),
     ALGO_ALIAS_AUTO(RX_SFX),        ALGO_ALIAS(RX_SFX,          "randomx/sfx"),
                                     ALGO_ALIAS(RX_SFX,          "randomsfx"),
     ALGO_ALIAS_AUTO(RX_KEVA),       ALGO_ALIAS(RX_KEVA,         "randomx/keva"),
@@ -350,7 +354,7 @@ std::vector<xmrig::Algorithm> xmrig::Algorithm::all(const std::function<bool(con
         CN_HEAVY_0, CN_HEAVY_TUBE, CN_HEAVY_XHV,
         CN_PICO_0, CN_PICO_TLO,
         CN_UPX2,
-        RX_0, RX_WOW, RX_ARQ, RX_SFX, RX_KEVA,
+        RX_0, RX_WOW, RX_ARQ, RX_GRAFT, RX_SFX, RX_KEVA,
         AR2_CHUKWA, AR2_CHUKWA_V2, AR2_WRKZ,
         ASTROBWT_DERO,
         KAWPOW_RVN
diff --git a/src/base/crypto/Algorithm.h b/src/base/crypto/Algorithm.h
index 4fc6de7a4..062e3b469 100644
--- a/src/base/crypto/Algorithm.h
+++ b/src/base/crypto/Algorithm.h
@@ -68,6 +68,7 @@ public:
         RX_0            = 0x72151200,   // "rx/0"             RandomX (reference configuration).
         RX_WOW          = 0x72141177,   // "rx/wow"           RandomWOW (Wownero).
         RX_ARQ          = 0x72121061,   // "rx/arq"           RandomARQ (Arqma).
+        RX_GRAFT        = 0x7257ef81,   // "rx/graft"         RandomGRAFT (Graft).
         RX_SFX          = 0x72151273,   // "rx/sfx"           RandomSFX (Safex Cash).
         RX_KEVA         = 0x7214116b,   // "rx/keva"          RandomKEVA (Keva).
         AR2_CHUKWA      = 0x61130000,   // "argon2/chukwa"    Argon2id (Chukwa).
@@ -134,6 +135,7 @@ public:
     static const char *kRX_0;
     static const char *kRX_WOW;
     static const char *kRX_ARQ;
+    static const char *kRX_GRAFT;
     static const char *kRX_SFX;
     static const char *kRX_KEVA;
 #   endif
diff --git a/src/base/crypto/Coin.cpp b/src/base/crypto/Coin.cpp
index a70ca0751..82d773f15 100644
--- a/src/base/crypto/Coin.cpp
+++ b/src/base/crypto/Coin.cpp
@@ -52,6 +52,7 @@ static CoinName const coin_names[] = {
     { "arqma",      Coin::ARQMA   },
     { "arq",        Coin::ARQMA   },
     { "dero",       Coin::DERO    },
+    { "graft",      Coin::GRAFT   },
     { "keva",       Coin::KEVA    },
     { "ravencoin",  Coin::RAVEN   },
     { "raven",      Coin::RAVEN   },
@@ -80,6 +81,9 @@ xmrig::Algorithm::Id xmrig::Coin::algorithm(uint8_t blobVersion) const
     case KEVA:
         return (blobVersion >= 11) ? Algorithm::RX_KEVA : Algorithm::CN_R;
 
+    case GRAFT:
+        return Algorithm::RX_GRAFT;
+
     case RAVEN:
         return Algorithm::KAWPOW_RVN;
 
diff --git a/src/base/crypto/Coin.h b/src/base/crypto/Coin.h
index 985335cb4..10e4de326 100644
--- a/src/base/crypto/Coin.h
+++ b/src/base/crypto/Coin.h
@@ -42,6 +42,7 @@ public:
         MONERO,
         ARQMA,
         DERO,
+        GRAFT,
         KEVA,
         RAVEN,
         CONCEAL,
diff --git a/src/crypto/randomx/randomx.cpp b/src/crypto/randomx/randomx.cpp
index fe438b61e..1e0f1e63e 100644
--- a/src/crypto/randomx/randomx.cpp
+++ b/src/crypto/randomx/randomx.cpp
@@ -86,6 +86,15 @@ RandomX_ConfigurationArqma::RandomX_ConfigurationArqma()
 	ScratchpadL3_Size = 262144;
 }
 
+RandomX_ConfigurationGraft::RandomX_ConfigurationGraft()
+{
+    ArgonLanes = 2;
+    ArgonSalt = "RandomX-Graft\x01";
+    ProgramSize = 280;
+	RANDOMX_FREQ_IROR_R = 7;
+	RANDOMX_FREQ_IROL_R = 3;
+}
+
 RandomX_ConfigurationSafex::RandomX_ConfigurationSafex()
 {
 	ArgonSalt = "RandomSFX\x01";
@@ -346,6 +355,7 @@ typedef void(randomx::JitCompilerX86::* InstructionGeneratorX86_2)(const randomx
 RandomX_ConfigurationMonero RandomX_MoneroConfig;
 RandomX_ConfigurationWownero RandomX_WowneroConfig;
 RandomX_ConfigurationArqma RandomX_ArqmaConfig;
+RandomX_ConfigurationGraft RandomX_GraftConfig;
 RandomX_ConfigurationSafex RandomX_SafexConfig;
 RandomX_ConfigurationKeva RandomX_KevaConfig;
 
diff --git a/src/crypto/randomx/randomx.h b/src/crypto/randomx/randomx.h
index 937a0844c..92e252646 100644
--- a/src/crypto/randomx/randomx.h
+++ b/src/crypto/randomx/randomx.h
@@ -144,12 +144,14 @@ struct RandomX_ConfigurationBase
 struct RandomX_ConfigurationMonero : public RandomX_ConfigurationBase {};
 struct RandomX_ConfigurationWownero : public RandomX_ConfigurationBase { RandomX_ConfigurationWownero(); };
 struct RandomX_ConfigurationArqma : public RandomX_ConfigurationBase { RandomX_ConfigurationArqma(); };
+struct RandomX_ConfigurationGraft : public RandomX_ConfigurationBase { RandomX_ConfigurationGraft(); };
 struct RandomX_ConfigurationSafex : public RandomX_ConfigurationBase { RandomX_ConfigurationSafex(); };
 struct RandomX_ConfigurationKeva : public RandomX_ConfigurationBase { RandomX_ConfigurationKeva(); };
 
 extern RandomX_ConfigurationMonero RandomX_MoneroConfig;
 extern RandomX_ConfigurationWownero RandomX_WowneroConfig;
 extern RandomX_ConfigurationArqma RandomX_ArqmaConfig;
+extern RandomX_ConfigurationGraft RandomX_GraftConfig;
 extern RandomX_ConfigurationSafex RandomX_SafexConfig;
 extern RandomX_ConfigurationKeva RandomX_KevaConfig;
 
diff --git a/src/crypto/rx/RxAlgo.cpp b/src/crypto/rx/RxAlgo.cpp
index b7d2b0834..63ea2256e 100644
--- a/src/crypto/rx/RxAlgo.cpp
+++ b/src/crypto/rx/RxAlgo.cpp
@@ -39,6 +39,9 @@ const RandomX_ConfigurationBase *xmrig::RxAlgo::base(Algorithm::Id algorithm)
     case Algorithm::RX_ARQ:
         return &RandomX_ArqmaConfig;
 
+    case Algorithm::RX_GRAFT:
+        return &RandomX_GraftConfig;
+
     case Algorithm::RX_SFX:
         return &RandomX_SafexConfig;
 

From 7f2771b4660a09678ae7a2d350ee4f7916d7e2ca Mon Sep 17 00:00:00 2001
From: Chris <chriskobasiuk@gmail.com>
Date: Fri, 27 Aug 2021 10:31:36 -0600
Subject: [PATCH 11/20] Fixed Algorithm id

Algorithm id should be 0x72151267, second and third byte encode L3 and L2 size.
0x72 = 'r'
0x15 = 1 << 0x15 (L3 size)
0x12 = 1 << 0x12 (L2 size)
0x67 = 'g'
---
 src/backend/opencl/cl/cn/algorithm.cl | 2 +-
 src/base/crypto/Algorithm.h           | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/backend/opencl/cl/cn/algorithm.cl b/src/backend/opencl/cl/cn/algorithm.cl
index dcf43d445..7258479d1 100644
--- a/src/backend/opencl/cl/cn/algorithm.cl
+++ b/src/backend/opencl/cl/cn/algorithm.cl
@@ -23,7 +23,7 @@
 #define ALGO_RX_ARQMA       0x72121061
 #define ALGO_RX_SFX         0x72151273
 #define ALGO_RX_KEVA        0x7214116b
-#define ALGO_RX_GRAFT       0x7257ef81
+#define ALGO_RX_GRAFT       0x72151267
 #define ALGO_AR2_CHUKWA     0x61130000
 #define ALGO_AR2_CHUKWA_V2  0x61140000
 #define ALGO_AR2_WRKZ       0x61120000
diff --git a/src/base/crypto/Algorithm.h b/src/base/crypto/Algorithm.h
index 062e3b469..15df2a2c0 100644
--- a/src/base/crypto/Algorithm.h
+++ b/src/base/crypto/Algorithm.h
@@ -68,7 +68,7 @@ public:
         RX_0            = 0x72151200,   // "rx/0"             RandomX (reference configuration).
         RX_WOW          = 0x72141177,   // "rx/wow"           RandomWOW (Wownero).
         RX_ARQ          = 0x72121061,   // "rx/arq"           RandomARQ (Arqma).
-        RX_GRAFT        = 0x7257ef81,   // "rx/graft"         RandomGRAFT (Graft).
+        RX_GRAFT        = 0x72151267,   // "rx/graft"         RandomGRAFT (Graft).
         RX_SFX          = 0x72151273,   // "rx/sfx"           RandomSFX (Safex Cash).
         RX_KEVA         = 0x7214116b,   // "rx/keva"          RandomKEVA (Keva).
         AR2_CHUKWA      = 0x61130000,   // "argon2/chukwa"    Argon2id (Chukwa).

From 4dbb5b89daba6686f75f91d8e57155d764f9b44a Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sat, 28 Aug 2021 12:16:41 +0700
Subject: [PATCH 12/20] Update hwloc for MSVC.

---
 src/3rdparty/hwloc/NEWS                       |  72 +-
 src/3rdparty/hwloc/VERSION                    |   6 +-
 src/3rdparty/hwloc/include/hwloc.h            |  66 +-
 .../hwloc/include/hwloc/autogen/config.h      |   6 +-
 src/3rdparty/hwloc/include/hwloc/cuda.h       |  16 +-
 src/3rdparty/hwloc/include/hwloc/cudart.h     |  12 +-
 src/3rdparty/hwloc/include/hwloc/deprecated.h |  11 +-
 src/3rdparty/hwloc/include/hwloc/distances.h  | 199 ++++-
 src/3rdparty/hwloc/include/hwloc/gl.h         |  15 +-
 src/3rdparty/hwloc/include/hwloc/helper.h     |  45 +-
 src/3rdparty/hwloc/include/hwloc/levelzero.h  | 157 ++++
 src/3rdparty/hwloc/include/hwloc/nvml.h       |  12 +-
 src/3rdparty/hwloc/include/hwloc/opencl.h     |  11 +-
 .../hwloc/include/hwloc/openfabrics-verbs.h   |  14 +-
 src/3rdparty/hwloc/include/hwloc/plugins.h    | 102 ++-
 src/3rdparty/hwloc/include/hwloc/rename.h     |  40 +-
 src/3rdparty/hwloc/include/hwloc/rsmi.h       |  14 +-
 src/3rdparty/hwloc/include/hwloc/windows.h    |  76 ++
 .../include/private/internal-components.h     |   3 +-
 src/3rdparty/hwloc/include/private/private.h  |  16 +-
 src/3rdparty/hwloc/src/components.c           |  93 +--
 src/3rdparty/hwloc/src/cpukinds.c             |  43 +-
 src/3rdparty/hwloc/src/distances.c            | 738 ++++++++++++++----
 src/3rdparty/hwloc/src/pci-common.c           |  29 +-
 src/3rdparty/hwloc/src/topology-windows.c     | 176 ++++-
 src/3rdparty/hwloc/src/topology-x86.c         |  56 +-
 src/3rdparty/hwloc/src/topology-xml.c         |  24 +-
 src/3rdparty/hwloc/src/topology.c             | 102 ++-
 src/3rdparty/hwloc/src/traversal.c            | 107 ++-
 29 files changed, 1900 insertions(+), 361 deletions(-)
 create mode 100644 src/3rdparty/hwloc/include/hwloc/levelzero.h
 create mode 100644 src/3rdparty/hwloc/include/hwloc/windows.h

diff --git a/src/3rdparty/hwloc/NEWS b/src/3rdparty/hwloc/NEWS
index 0ec17bb6d..0bf74d449 100644
--- a/src/3rdparty/hwloc/NEWS
+++ b/src/3rdparty/hwloc/NEWS
@@ -1,5 +1,5 @@
 Copyright © 2009 CNRS
-Copyright © 2009-2020 Inria.  All rights reserved.
+Copyright © 2009-2021 Inria.  All rights reserved.
 Copyright © 2009-2013 Université Bordeaux
 Copyright © 2009-2011 Cisco Systems, Inc.  All rights reserved.
 Copyright © 2020 Hewlett Packard Enterprise.  All rights reserved.
@@ -17,6 +17,76 @@ bug fixes (and other actions) for each version of hwloc since version
 0.9.
 
 
+Version 2.5.0
+-------------
+* API
+  + Add hwloc/windows.h to query Windows processor groups.
+  + Add hwloc_get_obj_with_same_locality() to convert between objects
+    with same locality, for instance NUMA nodes and Packages,
+    or OS devices within a PCI device.
+  + Add hwloc_distances_transform() to modify distances structures.
+    - hwloc-annotate and lstopo have new distances-transform options.
+  + hwloc_distances_add() is replaced with _add_create() followed by
+    _add_values() and _add_commit(). See hwloc/distances.h for details.
+  + Add topology flags to mitigate binding modifications during
+    hwloc discovery, especially on Windows:
+    - HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING and _MEMBINDING
+      restrict discovery to PUs and NUMA nodes inside the binding.
+    - HWLOC_TOPOLOGY_FLAG_DONT_CHANGE_BINDING prevents from ever
+      changing the binding during discovery.
+* Backends
+  + Add a levelzero backend for oneAPI L0 devices, exposed as OS devices
+    of subtype "LevelZero" and name such as "ze0".
+    - Add hwloc/levelzero.h for interoperability between converting
+      between L0 API devices and hwloc cpusets or OS devices.
+  + Expose NEC Vector Engine cards on Linux as OS devices of subtype
+    "VectorEngine" and name "ve0", etc.
+    Thanks to Anara Kozhokanova, Tim Cramer and Erich Focht for the help.
+  + Add a NVLinkBandwidth distances structure between NVIDIA GPUs
+    (and POWER processor or NVSwitches) in the NVML backend,
+    and a XGMIBandwidth distances structure between AMD GPUs
+    in the RSMI backends.
+    - See "Topology Attributes: Distances, Memory Attributes and CPU Kinds"
+      in the documentation for details about these new distances.
+  + Add support for NUMA node 0 being offline in Linux, thanks to Jirka Hladky.
+* Build
+  + Add --with-cuda-version=<version> or look at the CUDA_VERSION
+    environment variable to find the appropriate CUDA pkg-config files.
+    Thanks to Stephen Herbein for the suggestion.
+    - Also add --with-cuda=<dir> to specify the CUDA installation path
+      manually (and its NVML and OpenCL components).
+      Thanks to Andrea Bocci for the suggestion.
+    - See "How do I enable CUDA and select which CUDA version to use?"
+      in the FAQ for details.
+* Tools
+  + lstopo now has a --windows-processor-groups option on Windows.
+  + hwloc-ps now has a --short-name option to avoid long/truncated
+    command path.
+  + hwloc-ps now has a --single-ancestor option to return a single
+    (possibly too large) object where a process is bound.
+  + hwloc-ps --pid-cmd may now query environment variables,
+    including MPI-specific variables to find out process ranks.
+
+
+Version 2.4.1
+-------------
+* Fix AMD OpenCL device locality when PCI bus or device number >= 128.
+  Thanks to Edgar Leon for reporting the issue.
+  + Applications using any of the following inline functions must
+    be recompiled to get the fix: hwloc_opencl_get_device_pci_busid()
+    hwloc_opencl_get_device_cpuset(), hwloc_opencl_get_device_osdev().
+* Fix the ranking of cpukinds on non-Windows systems,
+  thanks to Ivan Kochin for the report.
+* Fix the insertion of custom Groups after loading the topology,
+  thanks to Scott Hicks.
+* Add support for CPU0 being offline in Linux, thanks to Garrett Clay.
+* Fix missing x86 Package and Core objects FreeBSD/NetBSD.
+  Thanks to Thibault Payet and Yuri Victorovich for the report.
+* Fix the import of very large distances with heterogeneous object types.
+* Fix a memory leak in the Linux backend,
+  thanks to Perceval Anichini.
+
+
 Version 2.4.0
 -------------
 * API
diff --git a/src/3rdparty/hwloc/VERSION b/src/3rdparty/hwloc/VERSION
index 979c2cc8e..a74f0a53e 100644
--- a/src/3rdparty/hwloc/VERSION
+++ b/src/3rdparty/hwloc/VERSION
@@ -8,7 +8,7 @@
 # Please update HWLOC_VERSION* in contrib/windows/hwloc_config.h too.
 
 major=2
-minor=4
+minor=5
 release=0
 
 # greek is used for alpha or beta release tags.  If it is non-empty,
@@ -22,7 +22,7 @@ greek=
 
 # The date when this release was created
 
-date="Nov 26, 2020"
+date="Jun 14, 2021"
 
 # If snapshot=1, then use the value from snapshot_version as the
 # entire hwloc version (i.e., ignore major, minor, release, and
@@ -41,7 +41,7 @@ snapshot_version=${major}.${minor}.${release}${greek}-git
 # 2. Version numbers are described in the Libtool current:revision:age
 # format.
 
-libhwloc_so_version=19:0:4
+libhwloc_so_version=20:0:5
 libnetloc_so_version=0:0:0
 
 # Please also update the <TargetName> lines in contrib/windows/libhwloc.vcxproj
diff --git a/src/3rdparty/hwloc/include/hwloc.h b/src/3rdparty/hwloc/include/hwloc.h
index 261626f42..88fac968e 100644
--- a/src/3rdparty/hwloc/include/hwloc.h
+++ b/src/3rdparty/hwloc/include/hwloc.h
@@ -93,7 +93,7 @@ extern "C" {
  * Two stable releases of the same series usually have the same ::HWLOC_API_VERSION
  * even if their HWLOC_VERSION are different.
  */
-#define HWLOC_API_VERSION 0x00020400
+#define HWLOC_API_VERSION 0x00020500
 
 /** \brief Indicate at runtime which hwloc API version was used at build time.
  *
@@ -1966,7 +1966,69 @@ enum hwloc_topology_flags_e {
    * hwloc and machine support.
    *
    */
-  HWLOC_TOPOLOGY_FLAG_IMPORT_SUPPORT = (1UL<<3)
+  HWLOC_TOPOLOGY_FLAG_IMPORT_SUPPORT = (1UL<<3),
+
+  /** \brief Do not consider resources outside of the process CPU binding.
+   *
+   * If the binding of the process is limited to a subset of cores,
+   * ignore the other cores during discovery.
+   *
+   * The resulting topology is identical to what a call to hwloc_topology_restrict()
+   * would generate, but this flag also prevents hwloc from ever touching other
+   * resources during the discovery.
+   *
+   * This flag especially tells the x86 backend to never temporarily
+   * rebind a thread on any excluded core. This is useful on Windows
+   * because such temporary rebinding can change the process binding.
+   * Another use-case is to avoid cores that would not be able to
+   * perform the hwloc discovery anytime soon because they are busy
+   * executing some high-priority real-time tasks.
+   *
+   * If process CPU binding is not supported,
+   * the thread CPU binding is considered instead if supported,
+   * or the flag is ignored.
+   *
+   * This flag requires ::HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM as well
+   * since binding support is required.
+   */
+  HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING = (1UL<<4),
+
+  /** \brief Do not consider resources outside of the process memory binding.
+   *
+   * If the binding of the process is limited to a subset of NUMA nodes,
+   * ignore the other NUMA nodes during discovery.
+   *
+   * The resulting topology is identical to what a call to hwloc_topology_restrict()
+   * would generate, but this flag also prevents hwloc from ever touching other
+   * resources during the discovery.
+   *
+   * This flag is meant to be used together with
+   * ::HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING when both cores
+   * and NUMA nodes should be ignored outside of the process binding.
+   *
+   * If process memory binding is not supported,
+   * the thread memory binding is considered instead if supported,
+   * or the flag is ignored.
+   *
+   * This flag requires ::HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM as well
+   * since binding support is required.
+   */
+  HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_MEMBINDING = (1UL<<5),
+
+  /** \brief Do not ever modify the process or thread binding during discovery.
+   *
+   * This flag disables all hwloc discovery steps that require a change of
+   * the process or thread binding. This currently only affects the x86
+   * backend which gets entirely disabled.
+   *
+   * This is useful when hwloc_topology_load() is called while the
+   * application also creates additional threads or modifies the binding.
+   *
+   * This flag is also a strict way to make sure the process binding will
+   * not change to due thread binding changes on Windows
+   * (see ::HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING).
+   */
+  HWLOC_TOPOLOGY_FLAG_DONT_CHANGE_BINDING = (1UL<<6)
 };
 
 /** \brief Set OR'ed flags to non-yet-loaded topology.
diff --git a/src/3rdparty/hwloc/include/hwloc/autogen/config.h b/src/3rdparty/hwloc/include/hwloc/autogen/config.h
index e490466be..eb70ba490 100644
--- a/src/3rdparty/hwloc/include/hwloc/autogen/config.h
+++ b/src/3rdparty/hwloc/include/hwloc/autogen/config.h
@@ -11,10 +11,10 @@
 #ifndef HWLOC_CONFIG_H
 #define HWLOC_CONFIG_H
 
-#define HWLOC_VERSION "2.4.1"
+#define HWLOC_VERSION "2.5.0"
 #define HWLOC_VERSION_MAJOR 2
-#define HWLOC_VERSION_MINOR 4
-#define HWLOC_VERSION_RELEASE 1
+#define HWLOC_VERSION_MINOR 5
+#define HWLOC_VERSION_RELEASE 0
 #define HWLOC_VERSION_GREEK ""
 
 #define __hwloc_restrict
diff --git a/src/3rdparty/hwloc/include/hwloc/cuda.h b/src/3rdparty/hwloc/include/hwloc/cuda.h
index 582270d16..72fb8ccbd 100644
--- a/src/3rdparty/hwloc/include/hwloc/cuda.h
+++ b/src/3rdparty/hwloc/include/hwloc/cuda.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2010-2020 Inria.  All rights reserved.
+ * Copyright © 2010-2021 Inria.  All rights reserved.
  * Copyright © 2010-2011 Université Bordeaux
  * Copyright © 2011 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -75,7 +75,7 @@ hwloc_cuda_get_device_pci_ids(hwloc_topology_t topology __hwloc_attribute_unused
 /** \brief Get the CPU set of processors that are physically
  * close to device \p cudevice.
  *
- * Return the CPU set describing the locality of the CUDA device \p cudevice.
+ * Store in \p set the CPU-set describing the locality of the CUDA device \p cudevice.
  *
  * Topology \p topology and device \p cudevice must match the local machine.
  * I/O devices detection and the CUDA component are not needed in the topology.
@@ -120,8 +120,8 @@ hwloc_cuda_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused,
 /** \brief Get the hwloc PCI device object corresponding to the
  * CUDA device \p cudevice.
  *
- * Return the PCI device object describing the CUDA device \p cudevice.
- * Return NULL if there is none.
+ * \return The hwloc PCI device object describing the CUDA device \p cudevice.
+ * \return \c NULL if none could be found.
  *
  * Topology \p topology and device \p cudevice must match the local machine.
  * I/O devices detection must be enabled in topology \p topology.
@@ -140,8 +140,8 @@ hwloc_cuda_get_device_pcidev(hwloc_topology_t topology, CUdevice cudevice)
 
 /** \brief Get the hwloc OS device object corresponding to CUDA device \p cudevice.
  *
- * Return the hwloc OS device object that describes the given
- * CUDA device \p cudevice. Return NULL if there is none.
+ * \return The hwloc OS device object that describes the given CUDA device \p cudevice.
+ * \return \c NULL if none could be found.
  *
  * Topology \p topology and device \p cudevice must match the local machine.
  * I/O devices detection and the CUDA component must be enabled in the topology.
@@ -183,8 +183,8 @@ hwloc_cuda_get_device_osdev(hwloc_topology_t topology, CUdevice cudevice)
 /** \brief Get the hwloc OS device object corresponding to the
  * CUDA device whose index is \p idx.
  *
- * Return the OS device object describing the CUDA device whose
- * index is \p idx. Return NULL if there is none.
+ * \return The hwloc OS device object describing the CUDA device whose index is \p idx.
+ * \return \c NULL if none could be found.
  *
  * The topology \p topology does not necessarily have to match the current
  * machine. For instance the topology may be an XML import of a remote host.
diff --git a/src/3rdparty/hwloc/include/hwloc/cudart.h b/src/3rdparty/hwloc/include/hwloc/cudart.h
index 059727ae4..676cffec9 100644
--- a/src/3rdparty/hwloc/include/hwloc/cudart.h
+++ b/src/3rdparty/hwloc/include/hwloc/cudart.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2010-2020 Inria.  All rights reserved.
+ * Copyright © 2010-2021 Inria.  All rights reserved.
  * Copyright © 2010-2011 Université Bordeaux
  * Copyright © 2011 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -72,7 +72,7 @@ hwloc_cudart_get_device_pci_ids(hwloc_topology_t topology __hwloc_attribute_unus
 /** \brief Get the CPU set of processors that are physically
  * close to device \p idx.
  *
- * Return the CPU set describing the locality of the CUDA device
+ * Store in \p set the CPU-set describing the locality of the CUDA device
  * whose index is \p idx.
  *
  * Topology \p topology and device \p idx must match the local machine.
@@ -117,8 +117,8 @@ hwloc_cudart_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unuse
 /** \brief Get the hwloc PCI device object corresponding to the
  * CUDA device whose index is \p idx.
  *
- * Return the PCI device object describing the CUDA device whose
- * index is \p idx. Return NULL if there is none.
+ * \return The hwloc PCI device object describing the CUDA device whose index is \p idx.
+ * \return \c NULL if none could be found.
  *
  * Topology \p topology and device \p idx must match the local machine.
  * I/O devices detection must be enabled in topology \p topology.
@@ -138,8 +138,8 @@ hwloc_cudart_get_device_pcidev(hwloc_topology_t topology, int idx)
 /** \brief Get the hwloc OS device object corresponding to the
  * CUDA device whose index is \p idx.
  *
- * Return the OS device object describing the CUDA device whose
- * index is \p idx. Return NULL if there is none.
+ * \return The hwloc OS device object describing the CUDA device whose index is \p idx.
+ * \return \c NULL if none could be found.
  *
  * The topology \p topology does not necessarily have to match the current
  * machine. For instance the topology may be an XML import of a remote host.
diff --git a/src/3rdparty/hwloc/include/hwloc/deprecated.h b/src/3rdparty/hwloc/include/hwloc/deprecated.h
index 4a231f507..f2419dd48 100644
--- a/src/3rdparty/hwloc/include/hwloc/deprecated.h
+++ b/src/3rdparty/hwloc/include/hwloc/deprecated.h
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009 CNRS
- * Copyright © 2009-2018 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * Copyright © 2009-2012 Université Bordeaux
  * Copyright © 2009-2010 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -30,6 +30,15 @@ extern "C" {
 /* backward compat with v1.10 before Node->NUMANode clarification */
 #define HWLOC_OBJ_NODE HWLOC_OBJ_NUMANODE
 
+/** \brief Add a distances structure.
+ *
+ * Superseded by hwloc_distances_add_create()+hwloc_distances_add_values()+hwloc_distances_add_commit()
+ * in v2.5.
+ */
+HWLOC_DECLSPEC int hwloc_distances_add(hwloc_topology_t topology,
+				       unsigned nbobjs, hwloc_obj_t *objs, hwloc_uint64_t *values,
+				       unsigned long kind, unsigned long flags) __hwloc_attribute_deprecated;
+
 /** \brief Insert a misc object by parent.
  *
  * Identical to hwloc_topology_insert_misc_object().
diff --git a/src/3rdparty/hwloc/include/hwloc/distances.h b/src/3rdparty/hwloc/include/hwloc/distances.h
index 57e53cd51..6eac94e97 100644
--- a/src/3rdparty/hwloc/include/hwloc/distances.h
+++ b/src/3rdparty/hwloc/include/hwloc/distances.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2010-2020 Inria.  All rights reserved.
+ * Copyright © 2010-2021 Inria.  All rights reserved.
  * See COPYING in top-level directory.
  */
 
@@ -35,9 +35,19 @@ extern "C" {
  * from a core in another node.
  * The corresponding kind is ::HWLOC_DISTANCES_KIND_FROM_OS | ::HWLOC_DISTANCES_KIND_FROM_USER.
  * The name of this distances structure is "NUMALatency".
+ * Others distance structures include and "XGMIBandwidth" and "NVLinkBandwidth".
  *
  * The matrix may also contain bandwidths between random sets of objects,
  * possibly provided by the user, as specified in the \p kind attribute.
+ *
+ * Pointers \p objs and \p values should not be replaced, reallocated, freed, etc.
+ * However callers are allowed to modify \p kind as well as the contents
+ * of \p objs and \p values arrays.
+ * For instance, if there is a single NUMA node per Package,
+ * hwloc_get_obj_with_same_locality() may be used to convert between them
+ * and replace NUMA nodes in the \p objs array with the corresponding Packages.
+ * See also hwloc_distances_transform() for applying some transformations
+ * to the structure.
  */
 struct hwloc_distances_s {
   unsigned nbobjs;		/**< \brief Number of objects described by the distance matrix. */
@@ -91,6 +101,8 @@ enum hwloc_distances_kind_e {
   HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH = (1UL<<3),
 
   /** \brief This distances structure covers objects of different types.
+   * This may apply to the "NVLinkBandwidth" structure in presence
+   * of a NVSwitch or POWER processor NVLink port.
    * \hideinitializer
    */
   HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES = (1UL<<4)
@@ -147,6 +159,7 @@ hwloc_distances_get_by_type(hwloc_topology_t topology, hwloc_obj_type_t type,
  * Usually only one distances structure may match a given name.
  *
  * The name of the most common structure is "NUMALatency".
+ * Others include "XGMIBandwidth" and "NVLinkBandwidth".
  */
 HWLOC_DECLSPEC int
 hwloc_distances_get_by_name(hwloc_topology_t topology, const char *name,
@@ -168,6 +181,85 @@ hwloc_distances_get_name(hwloc_topology_t topology, struct hwloc_distances_s *di
 HWLOC_DECLSPEC void
 hwloc_distances_release(hwloc_topology_t topology, struct hwloc_distances_s *distances);
 
+/** \brief Transformations of distances structures. */
+enum hwloc_distances_transform_e {
+  /** \brief Remove \c NULL objects from the distances structure.
+   *
+   * Every object that was replaced with \c NULL in the \p objs array
+   * is removed and the \p values array is updated accordingly.
+   *
+   * At least \c 2 objects must remain, otherwise hwloc_distances_transform()
+   * will return \c -1 with \p errno set to \c EINVAL.
+   *
+   * \p kind will be updated with or without ::HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES
+   * according to the remaining objects.
+   *
+   * \hideinitializer
+   */
+  HWLOC_DISTANCES_TRANSFORM_REMOVE_NULL = 0,
+
+  /** \brief Replace bandwidth values with a number of links.
+   *
+   * Usually all values will be either \c 0 (no link) or \c 1 (one link).
+   * However some matrices could get larger values if some pairs of
+   * peers are connected by different numbers of links.
+   *
+   * Values on the diagonal are set to \c 0.
+   *
+   * This transformation only applies to bandwidth matrices.
+   *
+   * \hideinitializer
+   */
+  HWLOC_DISTANCES_TRANSFORM_LINKS = 1,
+
+  /** \brief Merge switches with multiple ports into a single object.
+   * This currently only applies to NVSwitches where GPUs seem connected to different
+   * separate switch ports in the NVLinkBandwidth matrix. This transformation will
+   * replace all of them with the same port connected to all GPUs.
+   * Other ports are removed by applying ::HWLOC_DISTANCES_TRANSFORM_REMOVE_NULL internally.
+   * \hideinitializer
+   */
+  HWLOC_DISTANCES_TRANSFORM_MERGE_SWITCH_PORTS = 2,
+
+  /** \brief Apply a transitive closure to the matrix to connect objects across switches.
+   * This currently only applies to GPUs and NVSwitches in the NVLinkBandwidth matrix.
+   * All pairs of GPUs will be reported as directly connected.
+   * \hideinitializer
+   */
+  HWLOC_DISTANCES_TRANSFORM_TRANSITIVE_CLOSURE = 3
+};
+
+/** \brief Apply a transformation to a distances structure.
+ *
+ * Modify a distances structure that was previously obtained with
+ * hwloc_distances_get() or one of its variants.
+ *
+ * This modifies the local copy of the distances structures but does
+ * not modify the distances information stored inside the topology
+ * (retrieved by another call to hwloc_distances_get() or exported to XML).
+ * To do so, one should add a new distances structure with same
+ * name, kind, objects and values (see \ref hwlocality_distances_add)
+ * and then remove this old one with hwloc_distances_release_remove().
+ *
+ * \p transform must be one of the transformations listed
+ * in ::hwloc_distances_transform_e.
+ *
+ * These transformations may modify the contents of the \p objs or \p values arrays.
+ *
+ * \p transform_attr must be \c NULL for now.
+ *
+ * \p flags must be \c 0 for now.
+ *
+ * \note Objects in distances array \p objs may be directly modified
+ * in place without using hwloc_distances_transform().
+ * One may use hwloc_get_obj_with_same_locality() to easily convert
+ * between similar objects of different types.
+ */
+HWLOC_DECLSPEC int hwloc_distances_transform(hwloc_topology_t topology, struct hwloc_distances_s *distances,
+                                             enum hwloc_distances_transform_e transform,
+                                             void *transform_attr,
+                                             unsigned long flags);
+
 /** @} */
 
 
@@ -215,13 +307,84 @@ hwloc_distances_obj_pair_values(struct hwloc_distances_s *distances,
 
 
 
-/** \defgroup hwlocality_distances_add Add or remove distances between objects
+/** \defgroup hwlocality_distances_add Add distances between objects
+ *
+ * The usual way to add distances is:
+ * \code
+ * hwloc_distances_add_handle_t handle;
+ * int err = -1;
+ * handle = hwloc_distances_add_create(topology, "name", kind, 0);
+ * if (handle) {
+ *   err = hwloc_distances_add_values(topology, handle, nbobjs, objs, values, 0);
+ *   if (!err)
+ *     err = hwloc_distances_add_commit(topology, handle, flags);
+ * }
+ * \endcode
+ * If \p err is \c 0 at the end, then addition was successful.
+ *
  * @{
  */
 
+/** \brief Handle to a new distances structure during its addition to the topology. */
+typedef void * hwloc_distances_add_handle_t;
+
+/** \brief Create a new empty distances structure.
+ *
+ * Create an empty distances structure
+ * to be filled with hwloc_distances_add_values()
+ * and then committed with hwloc_distances_add_commit().
+ *
+ * Parameter \p name is optional, it may be \c NULL.
+ * Otherwise, it will be copied internally and may later be freed by the caller.
+ *
+ * \p kind specifies the kind of distance as a OR'ed set of ::hwloc_distances_kind_e.
+ * Kind ::HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES will be automatically set
+ * according to objects having different types in hwloc_distances_add_values().
+ *
+ * \p flags must be \c 0 for now.
+ *
+ * \return A hwloc_distances_add_handle_t that should then be passed
+ * to hwloc_distances_add_values() and hwloc_distances_add_commit().
+ *
+ * \return \c NULL on error.
+ */
+HWLOC_DECLSPEC hwloc_distances_add_handle_t
+hwloc_distances_add_create(hwloc_topology_t topology,
+                           const char *name, unsigned long kind,
+                           unsigned long flags);
+
+/** \brief Specify the objects and values in a new empty distances structure.
+ *
+ * Specify the objects and values for a new distances structure
+ * that was returned as a handle by hwloc_distances_add_create().
+ * The structure must then be committed with hwloc_distances_add_commit().
+ *
+ * The number of objects is \p nbobjs and the array of objects is \p objs.
+ * Distance values are stored as a one-dimension array in \p values.
+ * The distance from object i to object j is in slot i*nbobjs+j.
+ *
+ * \p nbobjs must be at least 2.
+ *
+ * Arrays \p objs and \p values will be copied internally,
+ * they may later be freed by the caller.
+ *
+ * On error, the temporary distances structure and its content are destroyed.
+ *
+ * \p flags must be \c 0 for now.
+ *
+ * \return \c 0 on success.
+ * \return \c -1 on error.
+ */
+HWLOC_DECLSPEC int hwloc_distances_add_values(hwloc_topology_t topology,
+                                              hwloc_distances_add_handle_t handle,
+                                              unsigned nbobjs, hwloc_obj_t *objs,
+                                              hwloc_uint64_t *values,
+                                              unsigned long flags);
+
 /** \brief Flags for adding a new distances to a topology. */
 enum hwloc_distances_add_flag_e {
   /** \brief Try to group objects based on the newly provided distance information.
+   * This is ignored for distances between objects of different types.
    * \hideinitializer
    */
   HWLOC_DISTANCES_ADD_FLAG_GROUP = (1UL<<0),
@@ -233,23 +396,33 @@ enum hwloc_distances_add_flag_e {
   HWLOC_DISTANCES_ADD_FLAG_GROUP_INACCURATE = (1UL<<1)
 };
 
-/** \brief Provide a new distance matrix.
+/** \brief Commit a new distances structure.
  *
- * Provide the matrix of distances between a set of objects given by \p nbobjs
- * and the \p objs array. \p nbobjs must be at least 2.
- * The distances are stored as a one-dimension array in \p values.
- * The distance from object i to object j is in slot i*nbobjs+j.
+ * This function finalizes the distances structure and inserts in it the topology.
  *
- * \p kind specifies the kind of distance as a OR'ed set of ::hwloc_distances_kind_e.
- * Kind ::HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES will be automatically added
- * if objects of different types are given.
+ * Parameter \p handle was previously returned by hwloc_distances_add_create().
+ * Then objects and values were specified with hwloc_distances_add_values().
  *
  * \p flags configures the behavior of the function using an optional OR'ed set of
  * ::hwloc_distances_add_flag_e.
+ * It may be used to request the grouping of existing objects based on distances.
+ *
+ * On error, the temporary distances structure and its content are destroyed.
+ *
+ * \return \c 0 on success.
+ * \return \c -1 on error.
+ */
+HWLOC_DECLSPEC int hwloc_distances_add_commit(hwloc_topology_t topology,
+                                              hwloc_distances_add_handle_t handle,
+                                              unsigned long flags);
+
+/** @} */
+
+
+
+/** \defgroup hwlocality_distances_remove Remove distances between objects
+ * @{
  */
-HWLOC_DECLSPEC int hwloc_distances_add(hwloc_topology_t topology,
-				       unsigned nbobjs, hwloc_obj_t *objs, hwloc_uint64_t *values,
-				       unsigned long kind, unsigned long flags);
 
 /** \brief Remove all distance matrices from a topology.
  *
diff --git a/src/3rdparty/hwloc/include/hwloc/gl.h b/src/3rdparty/hwloc/include/hwloc/gl.h
index 897ef784b..56a402a86 100644
--- a/src/3rdparty/hwloc/include/hwloc/gl.h
+++ b/src/3rdparty/hwloc/include/hwloc/gl.h
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2012 Blue Brain Project, EPFL. All rights reserved.
- * Copyright © 2012-2013 Inria.  All rights reserved.
+ * Copyright © 2012-2021 Inria.  All rights reserved.
  * See COPYING in top-level directory.
  */
 
@@ -39,9 +39,9 @@ extern "C" {
 /** \brief Get the hwloc OS device object corresponding to the
  * OpenGL display given by port and device index.
  *
- * Return the OS device object describing the OpenGL display
+ * \return The hwloc OS device object describing the OpenGL display
  * whose port (server) is \p port and device (screen) is \p device.
- * Return NULL if there is none.
+ * \return \c NULL if none could be found.
  *
  * The topology \p topology does not necessarily have to match the current
  * machine. For instance the topology may be an XML import of a remote host.
@@ -70,9 +70,9 @@ hwloc_gl_get_display_osdev_by_port_device(hwloc_topology_t topology,
 /** \brief Get the hwloc OS device object corresponding to the
  * OpenGL display given by name.
  *
- * Return the OS device object describing the OpenGL display
+ * \return The hwloc OS device object describing the OpenGL display
  * whose name is \p name, built as ":port.device" such as ":0.0" .
- * Return NULL if there is none.
+ * \return \c NULL if none could be found.
  *
  * The topology \p topology does not necessarily have to match the current
  * machine. For instance the topology may be an XML import of a remote host.
@@ -99,9 +99,10 @@ hwloc_gl_get_display_osdev_by_name(hwloc_topology_t topology,
 /** \brief Get the OpenGL display port and device corresponding
  * to the given hwloc OS object.
  *
- * Return the OpenGL display port (server) in \p port and device (screen)
+ * Retrieves the OpenGL display port (server) in \p port and device (screen)
  * in \p screen that correspond to the given hwloc OS device object.
- * Return \c -1 if there is none.
+ *
+ * \return \c -1 if none could be found.
  *
  * The topology \p topology does not necessarily have to match the current
  * machine. For instance the topology may be an XML import of a remote host.
diff --git a/src/3rdparty/hwloc/include/hwloc/helper.h b/src/3rdparty/hwloc/include/hwloc/helper.h
index 8e4d45323..f918d8163 100644
--- a/src/3rdparty/hwloc/include/hwloc/helper.h
+++ b/src/3rdparty/hwloc/include/hwloc/helper.h
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009 CNRS
- * Copyright © 2009-2020 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * Copyright © 2009-2012 Université Bordeaux
  * Copyright © 2009-2010 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -807,6 +807,49 @@ hwloc_get_obj_below_array_by_type (hwloc_topology_t topology, int nr, hwloc_obj_
   return obj;
 }
 
+/** \brief Return an object of a different type with same locality.
+ *
+ * If the source object \p src is a normal or memory type,
+ * this function returns an object of type \p type with same
+ * CPU and node sets, either below or above in the hierarchy.
+ *
+ * If the source object \p src is a PCI or an OS device within a PCI
+ * device, the function may either return that PCI device, or another
+ * OS device in the same PCI parent.
+ * This may for instance be useful for converting between OS devices
+ * such as "nvml0" or "rsmi1" used in distance structures into the
+ * the PCI device, or the CUDA or OpenCL OS device that correspond
+ * to the same physical card.
+ *
+ * If not \c NULL, parameter \p subtype only select objects whose
+ * subtype attribute exists and is \p subtype (case-insensitively),
+ * for instance "OpenCL" or "CUDA".
+ *
+ * If not \c NULL, parameter \p nameprefix only selects objects whose
+ * name attribute exists and starts with \p nameprefix (case-insensitively),
+ * for instance "rsmi" for matching "rsmi0".
+ *
+ * If multiple objects match, the first one is returned.
+ *
+ * This function will not walk the hierarchy across bridges since
+ * the PCI locality may become different.
+ * This function cannot also convert between normal/memory objects
+ * and I/O or Misc objects.
+ *
+ * \p flags must be \c 0 for now.
+ *
+ * \return An object with identical locality,
+ * matching \p subtype and \p nameprefix if any.
+ *
+ * \return \c NULL if no matching object could be found,
+ * or if the source object and target type are incompatible,
+ * for instance if converting between CPU and I/O objects.
+ */
+HWLOC_DECLSPEC hwloc_obj_t
+hwloc_get_obj_with_same_locality(hwloc_topology_t topology, hwloc_obj_t src,
+                                 hwloc_obj_type_t type, const char *subtype, const char *nameprefix,
+                                 unsigned long flags);
+
 /** @} */
 
 
diff --git a/src/3rdparty/hwloc/include/hwloc/levelzero.h b/src/3rdparty/hwloc/include/hwloc/levelzero.h
new file mode 100644
index 000000000..4c356fc85
--- /dev/null
+++ b/src/3rdparty/hwloc/include/hwloc/levelzero.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright © 2021 Inria.  All rights reserved.
+ * See COPYING in top-level directory.
+ */
+
+/** \file
+ * \brief Macros to help interaction between hwloc and the oneAPI Level Zero interface.
+ *
+ * Applications that use both hwloc and Level Zero may want to
+ * include this file so as to get topology information for L0 devices.
+ */
+
+#ifndef HWLOC_LEVELZERO_H
+#define HWLOC_LEVELZERO_H
+
+#include "hwloc.h"
+#include "hwloc/autogen/config.h"
+#include "hwloc/helper.h"
+#ifdef HWLOC_LINUX_SYS
+#include "hwloc/linux.h"
+#endif
+
+#include <level_zero/ze_api.h>
+#include <level_zero/zes_api.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** \defgroup hwlocality_levelzero Interoperability with the oneAPI Level Zero interface.
+ *
+ * This interface offers ways to retrieve topology information about
+ * devices managed by the Level Zero API.
+ *
+ * @{
+ */
+
+/** \brief Get the CPU set of logical processors that are physically
+ * close to the Level Zero device \p device
+ *
+ * Store in \p set the CPU-set describing the locality of
+ * the Level Zero device \p device.
+ *
+ * Topology \p topology and device \p device must match the local machine.
+ * The Level Zero must have been initialized with Sysman enabled
+ * (ZES_ENABLE_SYSMAN=1 in the environment).
+ * I/O devices detection and the Level Zero component are not needed in the
+ * topology.
+ *
+ * The function only returns the locality of the device.
+ * If more information about the device is needed, OS objects should
+ * be used instead, see hwloc_levelzero_get_device_osdev().
+ *
+ * This function is currently only implemented in a meaningful way for
+ * Linux; other systems will simply get a full cpuset.
+ */
+static __hwloc_inline int
+hwloc_levelzero_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused,
+                                  ze_device_handle_t device, hwloc_cpuset_t set)
+{
+#ifdef HWLOC_LINUX_SYS
+  /* If we're on Linux, use the sysfs mechanism to get the local cpus */
+#define HWLOC_LEVELZERO_DEVICE_SYSFS_PATH_MAX 128
+  char path[HWLOC_LEVELZERO_DEVICE_SYSFS_PATH_MAX];
+  zes_pci_properties_t pci;
+  zes_device_handle_t sdevice = device;
+  ze_result_t res;
+
+  if (!hwloc_topology_is_thissystem(topology)) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  res = zesDevicePciGetProperties(sdevice, &pci);
+  if (res != ZE_RESULT_SUCCESS) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  sprintf(path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/local_cpus",
+          pci.address.domain, pci.address.bus, pci.address.device, pci.address.function);
+  if (hwloc_linux_read_path_as_cpumask(path, set) < 0
+      || hwloc_bitmap_iszero(set))
+    hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology));
+#else
+  /* Non-Linux systems simply get a full cpuset */
+  hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology));
+#endif
+  return 0;
+}
+
+/** \brief Get the hwloc OS device object corresponding to Level Zero device
+ * \p device.
+ *
+ * \return The hwloc OS device object that describes the given Level Zero device \p device.
+ * \return \c NULL if none could be found.
+ *
+ * Topology \p topology and device \p dv_ind must match the local machine.
+ * I/O devices detection and the Level Zero component must be enabled in the
+ * topology. If not, the locality of the object may still be found using
+ * hwloc_levelzero_get_device_cpuset().
+ *
+ * \note The corresponding hwloc PCI device may be found by looking
+ * at the result parent pointer (unless PCI devices are filtered out).
+ */
+static __hwloc_inline hwloc_obj_t
+hwloc_levelzero_get_device_osdev(hwloc_topology_t topology, ze_device_handle_t device)
+{
+  zes_device_handle_t sdevice = device;
+  zes_pci_properties_t pci;
+  ze_result_t res;
+  hwloc_obj_t osdev;
+
+  if (!hwloc_topology_is_thissystem(topology)) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  res = zesDevicePciGetProperties(sdevice, &pci);
+  if (res != ZE_RESULT_SUCCESS) {
+    /* L0 was likely initialized without sysman, don't bother */
+    errno = EINVAL;
+    return NULL;
+  }
+
+  osdev = NULL;
+  while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) {
+    hwloc_obj_t pcidev = osdev->parent;
+
+    if (strncmp(osdev->name, "ze", 2))
+      continue;
+
+    if (pcidev
+      && pcidev->type == HWLOC_OBJ_PCI_DEVICE
+      && pcidev->attr->pcidev.domain == pci.address.domain
+      && pcidev->attr->pcidev.bus == pci.address.bus
+      && pcidev->attr->pcidev.dev == pci.address.device
+      && pcidev->attr->pcidev.func == pci.address.function)
+      return osdev;
+
+    /* FIXME: when we'll have serialnumber, try it in case PCI is filtered-out */
+  }
+
+  return NULL;
+}
+
+/** @} */
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* HWLOC_LEVELZERO_H */
diff --git a/src/3rdparty/hwloc/include/hwloc/nvml.h b/src/3rdparty/hwloc/include/hwloc/nvml.h
index 9d578903c..57f36a85d 100644
--- a/src/3rdparty/hwloc/include/hwloc/nvml.h
+++ b/src/3rdparty/hwloc/include/hwloc/nvml.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2012-2020 Inria.  All rights reserved.
+ * Copyright © 2012-2021 Inria.  All rights reserved.
  * See COPYING in top-level directory.
  */
 
@@ -39,7 +39,7 @@ extern "C" {
 /** \brief Get the CPU set of processors that are physically
  * close to NVML device \p device.
  *
- * Return the CPU set describing the locality of the NVML device \p device.
+ * Store in \p set the CPU-set describing the locality of the NVML device \p device.
  *
  * Topology \p topology and device \p device must match the local machine.
  * I/O devices detection and the NVML component are not needed in the topology.
@@ -88,8 +88,8 @@ hwloc_nvml_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused,
 /** \brief Get the hwloc OS device object corresponding to the
  * NVML device whose index is \p idx.
  *
- * Return the OS device object describing the NVML device whose
- * index is \p idx. Returns NULL if there is none.
+ * \return The hwloc OS device object describing the NVML device whose index is \p idx.
+ * \return \c NULL if none could be found.
  *
  * The topology \p topology does not necessarily have to match the current
  * machine. For instance the topology may be an XML import of a remote host.
@@ -114,8 +114,8 @@ hwloc_nvml_get_device_osdev_by_index(hwloc_topology_t topology, unsigned idx)
 
 /** \brief Get the hwloc OS device object corresponding to NVML device \p device.
  *
- * Return the hwloc OS device object that describes the given
- * NVML device \p device. Return NULL if there is none.
+ * \return The hwloc OS device object that describes the given NVML device \p device.
+ * \return \c NULL if none could be found.
  *
  * Topology \p topology and device \p device must match the local machine.
  * I/O devices detection and the NVML component must be enabled in the topology.
diff --git a/src/3rdparty/hwloc/include/hwloc/opencl.h b/src/3rdparty/hwloc/include/hwloc/opencl.h
index 9a2fdacb4..395b32e3a 100644
--- a/src/3rdparty/hwloc/include/hwloc/opencl.h
+++ b/src/3rdparty/hwloc/include/hwloc/opencl.h
@@ -113,7 +113,7 @@ hwloc_opencl_get_device_pci_busid(cl_device_id device,
 /** \brief Get the CPU set of processors that are physically
  * close to OpenCL device \p device.
  *
- * Return the CPU set describing the locality of the OpenCL device \p device.
+ * Store in \p set the CPU-set describing the locality of the OpenCL device \p device.
  *
  * Topology \p topology and device \p device must match the local machine.
  * I/O devices detection and the OpenCL component are not needed in the topology.
@@ -162,10 +162,10 @@ hwloc_opencl_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unuse
 /** \brief Get the hwloc OS device object corresponding to the
  * OpenCL device for the given indexes.
  *
- * Return the OS device object describing the OpenCL device
+ * \return The hwloc OS device object describing the OpenCL device
  * whose platform index is \p platform_index,
  * and whose device index within this platform if \p device_index.
- * Return NULL if there is none.
+ * \return \c NULL if there is none.
  *
  * The topology \p topology does not necessarily have to match the current
  * machine. For instance the topology may be an XML import of a remote host.
@@ -192,8 +192,9 @@ hwloc_opencl_get_device_osdev_by_index(hwloc_topology_t topology,
 
 /** \brief Get the hwloc OS device object corresponding to OpenCL device \p deviceX.
  *
- * Use OpenCL device attributes to find the corresponding hwloc OS device object.
- * Return NULL if there is none or if useful attributes are not available.
+ * \return The hwloc OS device object corresponding to the given OpenCL device \p device.
+ * \return \c NULL if none could be found, for instance
+ * if required OpenCL attributes are not available.
  *
  * This function currently only works on AMD and NVIDIA OpenCL devices that support
  * relevant OpenCL extensions. hwloc_opencl_get_device_osdev_by_index()
diff --git a/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h b/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h
index bbf25d0fe..7cee137e9 100644
--- a/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h
+++ b/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009 CNRS
- * Copyright © 2009-2020 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * Copyright © 2009-2010 Université Bordeaux
  * Copyright © 2009-2011 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -44,7 +44,7 @@ extern "C" {
 /** \brief Get the CPU set of processors that are physically
  * close to device \p ibdev.
  *
- * Return the CPU set describing the locality of the OpenFabrics
+ * Store in \p set the CPU-set describing the locality of the OpenFabrics
  * device \p ibdev (InfiniBand, etc).
  *
  * Topology \p topology and device \p ibdev must match the local machine.
@@ -88,10 +88,11 @@ hwloc_ibv_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused,
 /** \brief Get the hwloc OS device object corresponding to the OpenFabrics
  * device named \p ibname.
  *
- * Return the OS device object describing the OpenFabrics device
+ * \return The hwloc OS device object describing the OpenFabrics device
  * (InfiniBand, Omni-Path, usNIC, etc) whose name is \p ibname
  * (mlx5_0, hfi1_0, usnic_0, qib0, etc).
- * Returns NULL if there is none.
+ * \return \c NULL if none could be found.
+ *
  * The name \p ibname is usually obtained from ibv_get_device_name().
  *
  * The topology \p topology does not necessarily have to match the current
@@ -117,8 +118,9 @@ hwloc_ibv_get_device_osdev_by_name(hwloc_topology_t topology,
 /** \brief Get the hwloc OS device object corresponding to the OpenFabrics
  * device \p ibdev.
  *
- * Return the OS device object describing the OpenFabrics device \p ibdev
- * (InfiniBand, etc). Returns NULL if there is none.
+ * \return The hwloc OS device object describing the OpenFabrics
+ * device \p ibdev (InfiniBand, etc).
+ * \return \c NULL if none could be found.
  *
  * Topology \p topology and device \p ibdev must match the local machine.
  * I/O devices detection must be enabled in the topology.
diff --git a/src/3rdparty/hwloc/include/hwloc/plugins.h b/src/3rdparty/hwloc/include/hwloc/plugins.h
index 06e1c3e92..6e4f12915 100644
--- a/src/3rdparty/hwloc/include/hwloc/plugins.h
+++ b/src/3rdparty/hwloc/include/hwloc/plugins.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2013-2020 Inria.  All rights reserved.
+ * Copyright © 2013-2021 Inria.  All rights reserved.
  * Copyright © 2016 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
  */
@@ -27,6 +27,9 @@ struct hwloc_backend;
 
 
 /** \defgroup hwlocality_disc_components Components and Plugins: Discovery components
+ *
+ * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified.
+ *
  * @{
  */
 
@@ -93,6 +96,9 @@ struct hwloc_disc_component {
 
 
 /** \defgroup hwlocality_disc_backends Components and Plugins: Discovery backends
+ *
+ * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified.
+ *
  * @{
  */
 
@@ -241,6 +247,9 @@ HWLOC_DECLSPEC int hwloc_backend_enable(struct hwloc_backend *backend);
 
 
 /** \defgroup hwlocality_generic_components Components and Plugins: Generic components
+ *
+ * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified.
+ *
  * @{
  */
 
@@ -310,10 +319,26 @@ struct hwloc_component {
 
 
 /** \defgroup hwlocality_components_core_funcs Components and Plugins: Core functions to be used by components
+ *
+ * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified.
+ *
  * @{
  */
 
-/** \brief Check whether insertion errors are hidden */
+/** \brief Check whether error messages are hidden.
+ *
+ * Callers should print critical error messages
+ * (e.g. invalid hw topo info, invalid config)
+ * only if this function returns strictly less than 2.
+ *
+ * Callers should print non-critical error messages
+ * (e.g. failure to initialize CUDA)
+ * if this function returns 0.
+ *
+ * This function return 1 by default (show critical only),
+ * 0 in lstopo (show all),
+ * or anything set in HWLOC_HIDE_ERRORS in the environment.
+ */
 HWLOC_DECLSPEC int hwloc_hide_errors(void);
 
 /** \brief Add an object to the topology.
@@ -455,6 +480,9 @@ hwloc_plugin_check_namespace(const char *pluginname __hwloc_attribute_unused, co
 
 
 /** \defgroup hwlocality_components_filtering Components and Plugins: Filtering objects
+ *
+ * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified.
+ *
  * @{
  */
 
@@ -472,6 +500,7 @@ hwloc_filter_check_pcidev_subtype_important(unsigned classid)
 	  || baseclass == 0x0b /* PCI_BASE_CLASS_PROCESSOR */
 	  || classid == 0x0c04 /* PCI_CLASS_SERIAL_FIBER */
 	  || classid == 0x0c06 /* PCI_CLASS_SERIAL_INFINIBAND */
+          || baseclass == 0x06 /* PCI_BASE_CLASS_BRIDGE with non-PCI downstream. the core will drop the useless ones later */
 	  || baseclass == 0x12 /* Processing Accelerators */);
 }
 
@@ -527,6 +556,9 @@ hwloc_filter_check_keep_object(hwloc_topology_t topology, hwloc_obj_t obj)
 
 
 /** \defgroup hwlocality_components_pcidisc Components and Plugins: helpers for PCI discovery
+ *
+ * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified.
+ *
  * @{
  */
 
@@ -578,18 +610,76 @@ HWLOC_DECLSPEC int hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, st
 
 
 /** \defgroup hwlocality_components_pcifind Components and Plugins: finding PCI objects during other discoveries
+ *
+ * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified.
+ *
  * @{
  */
 
-/** \brief Find the normal parent of a PCI bus ID.
+/** \brief Find the object or a parent of a PCI bus ID.
  *
- * Look at PCI affinity to find out where the given PCI bus ID should be attached.
+ * When attaching a new object (typically an OS device) whose locality
+ * is specified by PCI bus ID, this function returns the PCI object
+ * to use as a parent for attaching.
  *
- * This function should be used to attach an I/O device under the corresponding
- * PCI object (if any), or under a normal (non-I/O) object with same locality.
+ * If the exact PCI device with this bus ID exists, it is returned.
+ * Otherwise (for instance if it was filtered out), the function returns
+ * another object with similar locality (for instance a parent bridge,
+ * or the local CPU Package).
  */
 HWLOC_DECLSPEC struct hwloc_obj * hwloc_pci_find_parent_by_busid(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func);
 
+/** \brief Find the PCI device or bridge matching a PCI bus ID exactly.
+ *
+ * This is useful for adding specific information about some objects
+ * based on their PCI id. When it comes to attaching objects based on
+ * PCI locality, hwloc_pci_find_parent_by_busid() should be preferred.
+ */
+HWLOC_DECLSPEC struct hwloc_obj * hwloc_pci_find_by_busid(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func);
+
+/** \brief Handle to a new distances structure during its addition to the topology. */
+typedef void * hwloc_backend_distances_add_handle_t;
+
+/** \brief Create a new empty distances structure.
+ *
+ * This is identical to hwloc_distances_add_create()
+ * but this variant is designed for backend inserting
+ * distances during topology discovery.
+ */
+HWLOC_DECLSPEC hwloc_backend_distances_add_handle_t
+hwloc_backend_distances_add_create(hwloc_topology_t topology,
+                                   const char *name, unsigned long kind,
+                                   unsigned long flags);
+
+/** \brief Specify the objects and values in a new empty distances structure.
+ *
+ * This is similar to hwloc_distances_add_values()
+ * but this variant is designed for backend inserting
+ * distances during topology discovery.
+ *
+ * The only semantical difference is that \p objs and \p values
+ * are not duplicated, but directly attached to the topology.
+ * On success, these arrays are given to the core and should not
+ * ever be freed by the caller anymore.
+ */
+HWLOC_DECLSPEC int
+hwloc_backend_distances_add_values(hwloc_topology_t topology,
+                                   hwloc_backend_distances_add_handle_t handle,
+                                   unsigned nbobjs, hwloc_obj_t *objs,
+                                   hwloc_uint64_t *values,
+                                   unsigned long flags);
+
+/** \brief Commit a new distances structure.
+ *
+ * This is similar to hwloc_distances_add_commit()
+ * but this variant is designed for backend inserting
+ * distances during topology discovery.
+ */
+HWLOC_DECLSPEC int
+hwloc_backend_distances_add_commit(hwloc_topology_t topology,
+                                   hwloc_backend_distances_add_handle_t handle,
+                                   unsigned long flags);
+
 /** @} */
 
 
diff --git a/src/3rdparty/hwloc/include/hwloc/rename.h b/src/3rdparty/hwloc/include/hwloc/rename.h
index c2a304851..ae439b51c 100644
--- a/src/3rdparty/hwloc/include/hwloc/rename.h
+++ b/src/3rdparty/hwloc/include/hwloc/rename.h
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009-2011 Cisco Systems, Inc.  All rights reserved.
- * Copyright © 2010-2020 Inria.  All rights reserved.
+ * Copyright © 2010-2021 Inria.  All rights reserved.
  * See COPYING in top-level directory.
  */
 
@@ -120,6 +120,9 @@ extern "C" {
 #define HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM HWLOC_NAME_CAPS(TOPOLOGY_FLAG_IS_THISSYSTEM)
 #define HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES HWLOC_NAME_CAPS(TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES)
 #define HWLOC_TOPOLOGY_FLAG_IMPORT_SUPPORT HWLOC_NAME_CAPS(TOPOLOGY_FLAG_IMPORT_SUPPORT)
+#define HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING HWLOC_NAME_CAPS(TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING)
+#define HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_MEMBINDING HWLOC_NAME_CAPS(TOPOLOGY_FLAG_RESTRICT_TO_MEMBINDING)
+#define HWLOC_TOPOLOGY_FLAG_DONT_CHANGE_BINDING HWLOC_NAME_CAPS(TOPOLOGY_FLAG_DONT_CHANGE_BINDING)
 
 #define hwloc_topology_set_pid HWLOC_NAME(topology_set_pid)
 #define hwloc_topology_set_synthetic HWLOC_NAME(topology_set_synthetic)
@@ -356,6 +359,7 @@ extern "C" {
 #define hwloc_get_closest_objs HWLOC_NAME(get_closest_objs)
 #define hwloc_get_obj_below_by_type HWLOC_NAME(get_obj_below_by_type)
 #define hwloc_get_obj_below_array_by_type HWLOC_NAME(get_obj_below_array_by_type)
+#define hwloc_get_obj_with_same_locality HWLOC_NAME(get_obj_with_same_locality)
 #define hwloc_distrib_flags_e HWLOC_NAME(distrib_flags_e)
 #define HWLOC_DISTRIB_FLAG_REVERSE HWLOC_NAME_CAPS(DISTRIB_FLAG_REVERSE)
 #define hwloc_distrib HWLOC_NAME(distrib)
@@ -454,11 +458,22 @@ extern "C" {
 #define hwloc_distances_obj_index HWLOC_NAME(distances_obj_index)
 #define hwloc_distances_obj_pair_values HWLOC_NAME(distances_pair_values)
 
+#define hwloc_distances_transform_e HWLOC_NAME(distances_transform_e)
+#define HWLOC_DISTANCES_TRANSFORM_REMOVE_NULL HWLOC_NAME_CAPS(DISTANCES_TRANSFORM_REMOVE_NULL)
+#define HWLOC_DISTANCES_TRANSFORM_LINKS HWLOC_NAME_CAPS(DISTANCES_TRANSFORM_LINKS)
+#define HWLOC_DISTANCES_TRANSFORM_MERGE_SWITCH_PORTS HWLOC_NAME_CAPS(DISTANCES_TRANSFORM_MERGE_SWITCH_PORTS)
+#define HWLOC_DISTANCES_TRANSFORM_TRANSITIVE_CLOSURE HWLOC_NAME_CAPS(DISTANCES_TRANSFORM_TRANSITIVE_CLOSURE)
+#define hwloc_distances_transform HWLOC_NAME(distances_transform)
+
 #define hwloc_distances_add_flag_e HWLOC_NAME(distances_add_flag_e)
 #define HWLOC_DISTANCES_ADD_FLAG_GROUP HWLOC_NAME_CAPS(DISTANCES_ADD_FLAG_GROUP)
 #define HWLOC_DISTANCES_ADD_FLAG_GROUP_INACCURATE HWLOC_NAME_CAPS(DISTANCES_ADD_FLAG_GROUP_INACCURATE)
 
-#define hwloc_distances_add HWLOC_NAME(distances_add)
+#define hwloc_distances_add_handle_t HWLOC_NAME(distances_add_handle_t)
+#define hwloc_distances_add_create HWLOC_NAME(distances_add_create)
+#define hwloc_distances_add_values HWLOC_NAME(distances_add_values)
+#define hwloc_distances_add_commit HWLOC_NAME(distances_add_commit)
+
 #define hwloc_distances_remove HWLOC_NAME(distances_remove)
 #define hwloc_distances_remove_by_depth HWLOC_NAME(distances_remove_by_depth)
 #define hwloc_distances_remove_by_type HWLOC_NAME(distances_remove_by_type)
@@ -523,6 +538,11 @@ extern "C" {
 #define hwloc_linux_get_tid_last_cpu_location HWLOC_NAME(linux_get_tid_last_cpu_location)
 #define hwloc_linux_read_path_as_cpumask HWLOC_NAME(linux_read_file_cpumask)
 
+/* windows.h */
+
+#define hwloc_windows_get_nr_processor_groups HWLOC_NAME(windows_get_nr_processor_groups)
+#define hwloc_windows_get_processor_group_cpuset HWLOC_NAME(windows_get_processor_group_cpuset)
+
 /* openfabrics-verbs.h */
 
 #define hwloc_ibv_get_device_cpuset HWLOC_NAME(ibv_get_device_cpuset)
@@ -564,6 +584,11 @@ extern "C" {
 #define hwloc_rsmi_get_device_osdev HWLOC_NAME(rsmi_get_device_osdev)
 #define hwloc_rsmi_get_device_osdev_by_index HWLOC_NAME(rsmi_get_device_osdev_by_index)
 
+/* levelzero.h */
+
+#define hwloc_levelzero_get_device_cpuset HWLOC_NAME(levelzero_get_device_cpuset)
+#define hwloc_levelzero_get_device_osdev HWLOC_NAME(levelzero_get_device_osdev)
+
 /* gl.h */
 
 #define hwloc_gl_get_display_osdev_by_port_device HWLOC_NAME(gl_get_display_osdev_by_port_device)
@@ -620,10 +645,18 @@ extern "C" {
 #define hwloc_pcidisc_tree_insert_by_busid HWLOC_NAME(pcidisc_tree_insert_by_busid)
 #define hwloc_pcidisc_tree_attach HWLOC_NAME(pcidisc_tree_attach)
 
+#define hwloc_pci_find_by_busid HWLOC_NAME(pcidisc_find_by_busid)
 #define hwloc_pci_find_parent_by_busid HWLOC_NAME(pcidisc_find_busid_parent)
 
+#define hwloc_backend_distances_add_handle_t HWLOC_NAME(backend_distances_add_handle_t)
+#define hwloc_backend_distances_add_create HWLOC_NAME(backend_distances_add_create)
+#define hwloc_backend_distances_add_values HWLOC_NAME(backend_distances_add_values)
+#define hwloc_backend_distances_add_commit HWLOC_NAME(backend_distances_add_commit)
+
 /* hwloc/deprecated.h */
 
+#define hwloc_distances_add HWLOC_NAME(distances_add)
+
 #define hwloc_topology_insert_misc_object_by_parent HWLOC_NAME(topology_insert_misc_object_by_parent)
 #define hwloc_obj_cpuset_snprintf HWLOC_NAME(obj_cpuset_snprintf)
 #define hwloc_obj_type_sscanf HWLOC_NAME(obj_type_sscanf)
@@ -733,6 +766,7 @@ extern "C" {
 
 #define hwloc_cuda_component HWLOC_NAME(cuda_component)
 #define hwloc_gl_component HWLOC_NAME(gl_component)
+#define hwloc_levelzero_component HWLOC_NAME(levelzero_component)
 #define hwloc_nvml_component HWLOC_NAME(nvml_component)
 #define hwloc_rsmi_component HWLOC_NAME(rsmi_component)
 #define hwloc_opencl_component HWLOC_NAME(opencl_component)
@@ -772,7 +806,6 @@ extern "C" {
 #define hwloc_pci_discovery_init HWLOC_NAME(pci_discovery_init)
 #define hwloc_pci_discovery_prepare HWLOC_NAME(pci_discovery_prepare)
 #define hwloc_pci_discovery_exit HWLOC_NAME(pci_discovery_exit)
-#define hwloc_pci_find_by_busid HWLOC_NAME(pcidisc_find_by_busid)
 #define hwloc_find_insert_io_parent_by_complete_cpuset HWLOC_NAME(hwloc_find_insert_io_parent_by_complete_cpuset)
 
 #define hwloc__add_info HWLOC_NAME(_add_info)
@@ -816,7 +849,6 @@ extern "C" {
 #define hwloc_internal_distances_dup HWLOC_NAME(internal_distances_dup)
 #define hwloc_internal_distances_refresh HWLOC_NAME(internal_distances_refresh)
 #define hwloc_internal_distances_destroy HWLOC_NAME(internal_distances_destroy)
-
 #define hwloc_internal_distances_add HWLOC_NAME(internal_distances_add)
 #define hwloc_internal_distances_add_by_index HWLOC_NAME(internal_distances_add_by_index)
 #define hwloc_internal_distances_invalidate_cached_objs HWLOC_NAME(hwloc_internal_distances_invalidate_cached_objs)
diff --git a/src/3rdparty/hwloc/include/hwloc/rsmi.h b/src/3rdparty/hwloc/include/hwloc/rsmi.h
index a6d55b3c9..55aa1272b 100644
--- a/src/3rdparty/hwloc/include/hwloc/rsmi.h
+++ b/src/3rdparty/hwloc/include/hwloc/rsmi.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2012-2020 Inria.  All rights reserved.
+ * Copyright © 2012-2021 Inria.  All rights reserved.
  * Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
  * Written by Advanced Micro Devices,
  * See COPYING in top-level directory.
@@ -41,7 +41,7 @@ extern "C" {
 /** \brief Get the CPU set of logical processors that are physically
  * close to AMD GPU device whose index is \p dv_ind.
  *
- * Return the CPU set describing the locality of the AMD GPU device
+ * Store in \p set the CPU-set describing the locality of the AMD GPU device
  * whose index is \p dv_ind.
  *
  * Topology \p topology and device \p dv_ind must match the local machine.
@@ -96,8 +96,9 @@ hwloc_rsmi_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused,
 /** \brief Get the hwloc OS device object corresponding to the
  * AMD GPU device whose index is \p dv_ind.
  *
- * Return the OS device object describing the AMD GPU device whose
- * index is \p dv_ind. Returns NULL if there is none.
+ * \return The hwloc OS device object describing the AMD GPU device whose
+ * index is \p dv_ind.
+ * \return \c NULL if none could be found.
  *
  * The topology \p topology does not necessarily have to match the current
  * machine. For instance the topology may be an XML import of a remote host.
@@ -124,8 +125,9 @@ hwloc_rsmi_get_device_osdev_by_index(hwloc_topology_t topology, uint32_t dv_ind)
 /** \brief Get the hwloc OS device object corresponding to AMD GPU device,
  * whose index is \p dv_ind.
  *
- * Return the hwloc OS device object that describes the given
- * AMD GPU, whose index is \p dv_ind Return NULL if there is none.
+ * \return The hwloc OS device object that describes the given
+ * AMD GPU, whose index is \p dv_ind.
+ * \return \c NULL if none could be found.
  *
  * Topology \p topology and device \p dv_ind must match the local machine.
  * I/O devices detection and the ROCm SMI component must be enabled in the
diff --git a/src/3rdparty/hwloc/include/hwloc/windows.h b/src/3rdparty/hwloc/include/hwloc/windows.h
new file mode 100644
index 000000000..dd6c7c99c
--- /dev/null
+++ b/src/3rdparty/hwloc/include/hwloc/windows.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2021 Inria.  All rights reserved.
+ * See COPYING in top-level directory.
+ */
+
+/** \file
+ * \brief Macros to help interaction between hwloc and Windows.
+ *
+ * Applications that use hwloc on Windows may want to include this file
+ * for Windows specific hwloc features.
+ */
+
+#ifndef HWLOC_WINDOWS_H
+#define HWLOC_WINDOWS_H
+
+#include "hwloc.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** \defgroup hwlocality_windows Windows-specific helpers
+ *
+ * These functions query Windows processor groups.
+ * These groups partition the operating system into virtual sets
+ * of up to 64 neighbor PUs.
+ * Threads and processes may only be bound inside a single group.
+ * Although Windows processor groups may be exposed in the hwloc
+ * hierarchy as hwloc Groups, they are also often merged into
+ * existing hwloc objects such as NUMA nodes or Packages.
+ * This API provides explicit information about Windows processor
+ * groups so that applications know whether binding to a large
+ * set of PUs may fail because it spans over multiple Windows
+ * processor groups.
+ *
+ * @{
+ */
+
+
+/** \brief Get the number of Windows processor groups
+ *
+ * \p flags must be 0 for now.
+ *
+ * \return at least \c 1 on success.
+ * \return -1 on error, for instance if the topology does not match
+ * the current system (e.g. loaded from another machine through XML).
+ */
+HWLOC_DECLSPEC int hwloc_windows_get_nr_processor_groups(hwloc_topology_t topology, unsigned long flags);
+
+/** \brief Get the CPU-set of a Windows processor group.
+ *
+ * Get the set of PU included in the processor group specified
+ * by \p pg_index.
+ * \p pg_index must be between \c 0 and the value returned
+ * by hwloc_windows_get_nr_processor_groups() minus 1.
+ *
+ * \p flags must be 0 for now.
+ *
+ * \return \c 0 on success.
+ * \return \c -1 on error, for instance if \p pg_index is invalid,
+ * or if the topology does not match the current system (e.g. loaded
+ * from another machine through XML).
+ */
+HWLOC_DECLSPEC int hwloc_windows_get_processor_group_cpuset(hwloc_topology_t topology, unsigned pg_index, hwloc_cpuset_t cpuset, unsigned long flags);
+
+/** @} */
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* HWLOC_WINDOWS_H */
diff --git a/src/3rdparty/hwloc/include/private/internal-components.h b/src/3rdparty/hwloc/include/private/internal-components.h
index 0b82a45c9..65cfdd7d4 100644
--- a/src/3rdparty/hwloc/include/private/internal-components.h
+++ b/src/3rdparty/hwloc/include/private/internal-components.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2018-2019 Inria.  All rights reserved.
+ * Copyright © 2018-2020 Inria.  All rights reserved.
  *
  * See COPYING in top-level directory.
  */
@@ -31,6 +31,7 @@ HWLOC_DECLSPEC extern const struct hwloc_component hwloc_cuda_component;
 HWLOC_DECLSPEC extern const struct hwloc_component hwloc_gl_component;
 HWLOC_DECLSPEC extern const struct hwloc_component hwloc_nvml_component;
 HWLOC_DECLSPEC extern const struct hwloc_component hwloc_rsmi_component;
+HWLOC_DECLSPEC extern const struct hwloc_component hwloc_levelzero_component;
 HWLOC_DECLSPEC extern const struct hwloc_component hwloc_opencl_component;
 HWLOC_DECLSPEC extern const struct hwloc_component hwloc_pci_component;
 
diff --git a/src/3rdparty/hwloc/include/private/private.h b/src/3rdparty/hwloc/include/private/private.h
index e07826598..5e216632f 100644
--- a/src/3rdparty/hwloc/include/private/private.h
+++ b/src/3rdparty/hwloc/include/private/private.h
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009      CNRS
- * Copyright © 2009-2020 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * Copyright © 2009-2012, 2020 Université Bordeaux
  * Copyright © 2009-2011 Cisco Systems, Inc.  All rights reserved.
  *
@@ -166,6 +166,7 @@ struct hwloc_topology {
     unsigned long kind;
 
 #define HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID (1U<<0) /* if the objs array is valid below */
+#define HWLOC_INTERNAL_DIST_FLAG_NOT_COMMITTED (1U<<1) /* if the distances isn't in the list yet */
     unsigned iflags;
 
     /* objects are currently stored in physical_index order */
@@ -304,11 +305,6 @@ extern void hwloc_pci_discovery_init(struct hwloc_topology *topology);
 extern void hwloc_pci_discovery_prepare(struct hwloc_topology *topology);
 extern void hwloc_pci_discovery_exit(struct hwloc_topology *topology);
 
-/* Look for an object matching the given domain/bus/func,
- * either exactly or return the smallest container bridge
- */
-extern struct hwloc_obj * hwloc_pci_find_by_busid(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func);
-
 /* Look for an object matching complete cpuset exactly, or insert one.
  * Return NULL on failure.
  * Return a good fallback (object above) on failure to insert.
@@ -408,10 +404,14 @@ extern void hwloc_internal_distances_prepare(hwloc_topology_t topology);
 extern void hwloc_internal_distances_destroy(hwloc_topology_t topology);
 extern int hwloc_internal_distances_dup(hwloc_topology_t new, hwloc_topology_t old);
 extern void hwloc_internal_distances_refresh(hwloc_topology_t topology);
-extern int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name, unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values, unsigned long kind, unsigned long flags);
-extern int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, const char *name, hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types, unsigned nbobjs, uint64_t *indexes, uint64_t *values, unsigned long kind, unsigned long flags);
 extern void hwloc_internal_distances_invalidate_cached_objs(hwloc_topology_t topology);
 
+/* these distances_add() functions are higher-level than those in hwloc/plugins.h
+ * but they may change in the future, hence they are not exported to plugins.
+ */
+extern int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, const char *name, hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types, unsigned nbobjs, uint64_t *indexes, uint64_t *values, unsigned long kind, unsigned long flags);
+extern int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name, unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values, unsigned long kind, unsigned long flags);
+
 extern void hwloc_internal_memattrs_init(hwloc_topology_t topology);
 extern void hwloc_internal_memattrs_prepare(hwloc_topology_t topology);
 extern void hwloc_internal_memattrs_destroy(hwloc_topology_t topology);
diff --git a/src/3rdparty/hwloc/src/components.c b/src/3rdparty/hwloc/src/components.c
index 496ed2322..81e3116b3 100644
--- a/src/3rdparty/hwloc/src/components.c
+++ b/src/3rdparty/hwloc/src/components.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2009-2020 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * Copyright © 2012 Université Bordeaux
  * See COPYING in top-level directory.
  */
@@ -124,7 +124,7 @@ hwloc_dlforeachfile(const char *_paths,
       *colon = '\0';
 
     if (hwloc_plugins_verbose)
-      fprintf(stderr, " Looking under %s\n", path);
+      fprintf(stderr, "hwloc:  Looking under %s\n", path);
 
     dir = opendir(path);
     if (!dir)
@@ -198,7 +198,7 @@ hwloc__dlforeach_cb(const char *filename, void *_data __hwloc_attribute_unused)
   char *componentsymbolname;
 
   if (hwloc_plugins_verbose)
-    fprintf(stderr, "Plugin dlforeach found `%s'\n", filename);
+    fprintf(stderr, "hwloc: Plugin dlforeach found `%s'\n", filename);
 
   basename = strrchr(filename, '/');
   if (!basename)
@@ -208,7 +208,7 @@ hwloc__dlforeach_cb(const char *filename, void *_data __hwloc_attribute_unused)
 
   if (hwloc_plugins_blacklist && strstr(hwloc_plugins_blacklist, basename)) {
     if (hwloc_plugins_verbose)
-      fprintf(stderr, "Plugin `%s' is blacklisted in the environment\n", basename);
+      fprintf(stderr, "hwloc: Plugin `%s' is blacklisted in the environment\n", basename);
     goto out;
   }
 
@@ -216,14 +216,14 @@ hwloc__dlforeach_cb(const char *filename, void *_data __hwloc_attribute_unused)
   handle = hwloc_dlopenext(filename);
   if (!handle) {
     if (hwloc_plugins_verbose)
-      fprintf(stderr, "Failed to load plugin: %s\n", hwloc_dlerror());
+      fprintf(stderr, "hwloc: Failed to load plugin: %s\n", hwloc_dlerror());
     goto out;
   }
 
   componentsymbolname = malloc(strlen(basename)+10+1);
   if (!componentsymbolname) {
     if (hwloc_plugins_verbose)
-      fprintf(stderr, "Failed to allocation component `%s' symbol\n",
+      fprintf(stderr, "hwloc: Failed to allocation component `%s' symbol\n",
 	      basename);
     goto out_with_handle;
   }
@@ -231,38 +231,38 @@ hwloc__dlforeach_cb(const char *filename, void *_data __hwloc_attribute_unused)
   component = hwloc_dlsym(handle, componentsymbolname);
   if (!component) {
     if (hwloc_plugins_verbose)
-      fprintf(stderr, "Failed to find component symbol `%s'\n",
+      fprintf(stderr, "hwloc: Failed to find component symbol `%s'\n",
 	      componentsymbolname);
     free(componentsymbolname);
     goto out_with_handle;
   }
   if (component->abi != HWLOC_COMPONENT_ABI) {
     if (hwloc_plugins_verbose)
-      fprintf(stderr, "Plugin symbol ABI %u instead of %d\n",
+      fprintf(stderr, "hwloc: Plugin symbol ABI %u instead of %d\n",
 	      component->abi, HWLOC_COMPONENT_ABI);
     free(componentsymbolname);
     goto out_with_handle;
   }
   if (hwloc_plugins_verbose)
-    fprintf(stderr, "Plugin contains expected symbol `%s'\n",
+    fprintf(stderr, "hwloc: Plugin contains expected symbol `%s'\n",
 	    componentsymbolname);
   free(componentsymbolname);
 
   if (HWLOC_COMPONENT_TYPE_DISC == component->type) {
     if (strncmp(basename, "hwloc_", 6)) {
       if (hwloc_plugins_verbose)
-	fprintf(stderr, "Plugin name `%s' doesn't match its type DISCOVERY\n", basename);
+	fprintf(stderr, "hwloc: Plugin name `%s' doesn't match its type DISCOVERY\n", basename);
       goto out_with_handle;
     }
   } else if (HWLOC_COMPONENT_TYPE_XML == component->type) {
     if (strncmp(basename, "hwloc_xml_", 10)) {
       if (hwloc_plugins_verbose)
-	fprintf(stderr, "Plugin name `%s' doesn't match its type XML\n", basename);
+	fprintf(stderr, "hwloc: Plugin name `%s' doesn't match its type XML\n", basename);
       goto out_with_handle;
     }
   } else {
     if (hwloc_plugins_verbose)
-      fprintf(stderr, "Plugin name `%s' has invalid type %u\n",
+      fprintf(stderr, "hwloc: Plugin name `%s' has invalid type %u\n",
 	      basename, (unsigned) component->type);
     goto out_with_handle;
   }
@@ -277,7 +277,7 @@ hwloc__dlforeach_cb(const char *filename, void *_data __hwloc_attribute_unused)
   desc->handle = handle;
   desc->next = NULL;
   if (hwloc_plugins_verbose)
-    fprintf(stderr, "Plugin descriptor `%s' ready\n", basename);
+    fprintf(stderr, "hwloc: Plugin descriptor `%s' ready\n", basename);
 
   /* append to the list */
   prevdesc = &hwloc_plugins;
@@ -285,7 +285,7 @@ hwloc__dlforeach_cb(const char *filename, void *_data __hwloc_attribute_unused)
     prevdesc = &((*prevdesc)->next);
   *prevdesc = desc;
   if (hwloc_plugins_verbose)
-    fprintf(stderr, "Plugin descriptor `%s' queued\n", basename);
+    fprintf(stderr, "hwloc: Plugin descriptor `%s' queued\n", basename);
   return 0;
 
  out_with_handle:
@@ -300,7 +300,7 @@ hwloc_plugins_exit(void)
   struct hwloc__plugin_desc *desc, *next;
 
   if (hwloc_plugins_verbose)
-    fprintf(stderr, "Closing all plugins\n");
+    fprintf(stderr, "hwloc: Closing all plugins\n");
 
   desc = hwloc_plugins;
   while (desc) {
@@ -340,7 +340,7 @@ hwloc_plugins_init(void)
   hwloc_plugins = NULL;
 
   if (hwloc_plugins_verbose)
-    fprintf(stderr, "Starting plugin dlforeach in %s\n", path);
+    fprintf(stderr, "hwloc: Starting plugin dlforeach in %s\n", path);
   err = hwloc_dlforeachfile(path, hwloc__dlforeach_cb, NULL);
   if (err)
     goto out_with_init;
@@ -364,14 +364,14 @@ hwloc_disc_component_register(struct hwloc_disc_component *component,
   /* check that the component name is valid */
   if (!strcmp(component->name, HWLOC_COMPONENT_STOP_NAME)) {
     if (hwloc_components_verbose)
-      fprintf(stderr, "Cannot register discovery component with reserved name `" HWLOC_COMPONENT_STOP_NAME "'\n");
+      fprintf(stderr, "hwloc: Cannot register discovery component with reserved name `" HWLOC_COMPONENT_STOP_NAME "'\n");
     return -1;
   }
   if (strchr(component->name, HWLOC_COMPONENT_EXCLUDE_CHAR)
       || strchr(component->name, HWLOC_COMPONENT_PHASESEP_CHAR)
       || strcspn(component->name, HWLOC_COMPONENT_SEPS) != strlen(component->name)) {
     if (hwloc_components_verbose)
-      fprintf(stderr, "Cannot register discovery component with name `%s' containing reserved characters `%c" HWLOC_COMPONENT_SEPS "'\n",
+      fprintf(stderr, "hwloc: Cannot register discovery component with name `%s' containing reserved characters `%c" HWLOC_COMPONENT_SEPS "'\n",
 	      component->name, HWLOC_COMPONENT_EXCLUDE_CHAR);
     return -1;
   }
@@ -386,8 +386,9 @@ hwloc_disc_component_register(struct hwloc_disc_component *component,
 				   |HWLOC_DISC_PHASE_MISC
 				   |HWLOC_DISC_PHASE_ANNOTATE
 				   |HWLOC_DISC_PHASE_TWEAK))) {
-    fprintf(stderr, "Cannot register discovery component `%s' with invalid phases 0x%x\n",
-	    component->name, component->phases);
+    if (hwloc_hide_errors() < 2)
+      fprintf(stderr, "hwloc: Cannot register discovery component `%s' with invalid phases 0x%x\n",
+              component->name, component->phases);
     return -1;
   }
 
@@ -398,13 +399,13 @@ hwloc_disc_component_register(struct hwloc_disc_component *component,
       if ((*prev)->priority < component->priority) {
 	/* drop the existing component */
 	if (hwloc_components_verbose)
-	  fprintf(stderr, "Dropping previously registered discovery component `%s', priority %u lower than new one %u\n",
+	  fprintf(stderr, "hwloc: Dropping previously registered discovery component `%s', priority %u lower than new one %u\n",
 		  (*prev)->name, (*prev)->priority, component->priority);
 	*prev = (*prev)->next;
       } else {
 	/* drop the new one */
 	if (hwloc_components_verbose)
-	  fprintf(stderr, "Ignoring new discovery component `%s', priority %u lower than previously registered one %u\n",
+	  fprintf(stderr, "hwloc: Ignoring new discovery component `%s', priority %u lower than previously registered one %u\n",
 		  component->name, component->priority, (*prev)->priority);
 	return -1;
       }
@@ -412,7 +413,7 @@ hwloc_disc_component_register(struct hwloc_disc_component *component,
     prev = &((*prev)->next);
   }
   if (hwloc_components_verbose)
-    fprintf(stderr, "Registered discovery component `%s' phases 0x%x with priority %u (%s%s)\n",
+    fprintf(stderr, "hwloc: Registered discovery component `%s' phases 0x%x with priority %u (%s%s)\n",
 	    component->name, component->phases, component->priority,
 	    filename ? "from plugin " : "statically build", filename ? filename : "");
 
@@ -475,15 +476,16 @@ hwloc_components_init(void)
   /* hwloc_static_components is created by configure in static-components.h */
   for(i=0; NULL != hwloc_static_components[i]; i++) {
     if (hwloc_static_components[i]->flags) {
-      fprintf(stderr, "Ignoring static component with invalid flags %lx\n",
-	      hwloc_static_components[i]->flags);
+      if (hwloc_hide_errors() < 2)
+        fprintf(stderr, "hwloc: Ignoring static component with invalid flags %lx\n",
+                hwloc_static_components[i]->flags);
       continue;
     }
 
     /* initialize the component */
     if (hwloc_static_components[i]->init && hwloc_static_components[i]->init(0) < 0) {
       if (hwloc_components_verbose)
-	fprintf(stderr, "Ignoring static component, failed to initialize\n");
+	fprintf(stderr, "hwloc: Ignoring static component, failed to initialize\n");
       continue;
     }
     /* queue ->finalize() callback if any */
@@ -503,15 +505,16 @@ hwloc_components_init(void)
 #ifdef HWLOC_HAVE_PLUGINS
   for(desc = hwloc_plugins; NULL != desc; desc = desc->next) {
     if (desc->component->flags) {
-      fprintf(stderr, "Ignoring plugin `%s' component with invalid flags %lx\n",
-	      desc->name, desc->component->flags);
+      if (hwloc_hide_errors() < 2)
+        fprintf(stderr, "hwloc: Ignoring plugin `%s' component with invalid flags %lx\n",
+                desc->name, desc->component->flags);
       continue;
     }
 
     /* initialize the component */
     if (desc->component->init && desc->component->init(0) < 0) {
       if (hwloc_components_verbose)
-	fprintf(stderr, "Ignoring plugin `%s', failed to initialize\n", desc->name);
+	fprintf(stderr, "hwloc: Ignoring plugin `%s', failed to initialize\n", desc->name);
       continue;
     }
     /* queue ->finalize() callback if any */
@@ -608,7 +611,7 @@ hwloc_disc_component_blacklist_one(struct hwloc_topology *topology,
     /* replace linuxpci and linuxio with linux (with IO phases)
      * for backward compatibility with pre-v2.0 and v2.0 respectively */
     if (hwloc_components_verbose)
-      fprintf(stderr, "Replacing deprecated component `%s' with `linux' IO phases in blacklisting\n", name);
+      fprintf(stderr, "hwloc: Replacing deprecated component `%s' with `linux' IO phases in blacklisting\n", name);
     comp = hwloc_disc_component_find("linux", NULL);
     phases = HWLOC_DISC_PHASE_PCI | HWLOC_DISC_PHASE_IO | HWLOC_DISC_PHASE_MISC | HWLOC_DISC_PHASE_ANNOTATE;
 
@@ -624,7 +627,7 @@ hwloc_disc_component_blacklist_one(struct hwloc_topology *topology,
   }
 
   if (hwloc_components_verbose)
-    fprintf(stderr, "Blacklisting component `%s` phases 0x%x\n", comp->name, phases);
+    fprintf(stderr, "hwloc: Blacklisting component `%s` phases 0x%x\n", comp->name, phases);
 
   for(i=0; i<topology->nr_blacklisted_components; i++) {
     if (topology->blacklisted_components[i].component == comp) {
@@ -727,7 +730,7 @@ hwloc_disc_component_try_enable(struct hwloc_topology *topology,
     if (hwloc_components_verbose)
       /* do not warn if envvar_forced since system-wide HWLOC_COMPONENTS must be silently ignored after set_xml() etc.
        */
-      fprintf(stderr, "Excluding discovery component `%s' phases 0x%x, conflicts with excludes 0x%x\n",
+      fprintf(stderr, "hwloc: Excluding discovery component `%s' phases 0x%x, conflicts with excludes 0x%x\n",
 	      comp->name, comp->phases, topology->backend_excluded_phases);
     return -1;
   }
@@ -735,8 +738,8 @@ hwloc_disc_component_try_enable(struct hwloc_topology *topology,
   backend = comp->instantiate(topology, comp, topology->backend_excluded_phases | blacklisted_phases,
 			      NULL, NULL, NULL);
   if (!backend) {
-    if (hwloc_components_verbose || envvar_forced)
-      fprintf(stderr, "Failed to instantiate discovery component `%s'\n", comp->name);
+    if (hwloc_components_verbose || (envvar_forced && hwloc_hide_errors() < 2))
+      fprintf(stderr, "hwloc: Failed to instantiate discovery component `%s'\n", comp->name);
     return -1;
   }
 
@@ -817,7 +820,7 @@ hwloc_disc_components_enable_others(struct hwloc_topology *topology)
 	name = curenv;
 	if (!strcmp(name, "linuxpci") || !strcmp(name, "linuxio")) {
 	  if (hwloc_components_verbose)
-	    fprintf(stderr, "Replacing deprecated component `%s' with `linux' in envvar forcing\n", name);
+	    fprintf(stderr, "hwloc: Replacing deprecated component `%s' with `linux' in envvar forcing\n", name);
 	  name = "linux";
 	}
 
@@ -832,7 +835,8 @@ hwloc_disc_components_enable_others(struct hwloc_topology *topology)
 	  if (comp->phases & ~blacklisted_phases)
 	    hwloc_disc_component_try_enable(topology, comp, 1 /* envvar forced */, blacklisted_phases);
 	} else {
-	  fprintf(stderr, "Cannot find discovery component `%s'\n", name);
+          if (hwloc_hide_errors() < 2)
+            fprintf(stderr, "hwloc: Cannot find discovery component `%s'\n", name);
 	}
 
 	/* restore chars (the second loop below needs env to be unmodified) */
@@ -864,7 +868,7 @@ hwloc_disc_components_enable_others(struct hwloc_topology *topology)
 
       if (!(comp->phases & ~blacklisted_phases)) {
 	if (hwloc_components_verbose)
-	  fprintf(stderr, "Excluding blacklisted discovery component `%s' phases 0x%x\n",
+	  fprintf(stderr, "hwloc: Excluding blacklisted discovery component `%s' phases 0x%x\n",
 		  comp->name, comp->phases);
 	goto nextcomp;
       }
@@ -879,7 +883,7 @@ nextcomp:
     /* print a summary */
     int first = 1;
     backend = topology->backends;
-    fprintf(stderr, "Final list of enabled discovery components: ");
+    fprintf(stderr, "hwloc: Final list of enabled discovery components: ");
     while (backend != NULL) {
       fprintf(stderr, "%s%s(0x%x)", first ? "" : ",", backend->component->name, backend->phases);
       backend = backend->next;
@@ -935,7 +939,7 @@ hwloc_backend_alloc(struct hwloc_topology *topology,
   /* filter-out component phases that are excluded */
   backend->phases = component->phases & ~topology->backend_excluded_phases;
   if (backend->phases != component->phases && hwloc_components_verbose)
-    fprintf(stderr, "Trying discovery component `%s' with phases 0x%x instead of 0x%x\n",
+    fprintf(stderr, "hwloc: Trying discovery component `%s' with phases 0x%x instead of 0x%x\n",
 	    component->name, backend->phases, component->phases);
   backend->flags = 0;
   backend->discover = NULL;
@@ -963,8 +967,9 @@ hwloc_backend_enable(struct hwloc_backend *backend)
 
   /* check backend flags */
   if (backend->flags) {
-    fprintf(stderr, "Cannot enable discovery component `%s' phases 0x%x with unknown flags %lx\n",
-	    backend->component->name, backend->component->phases, backend->flags);
+    if (hwloc_hide_errors() < 2)
+      fprintf(stderr, "hwloc: Cannot enable discovery component `%s' phases 0x%x with unknown flags %lx\n",
+              backend->component->name, backend->component->phases, backend->flags);
     return -1;
   }
 
@@ -973,7 +978,7 @@ hwloc_backend_enable(struct hwloc_backend *backend)
   while (NULL != *pprev) {
     if ((*pprev)->component == backend->component) {
       if (hwloc_components_verbose)
-	fprintf(stderr, "Cannot enable  discovery component `%s' phases 0x%x twice\n",
+	fprintf(stderr, "hwloc: Cannot enable  discovery component `%s' phases 0x%x twice\n",
 		backend->component->name, backend->component->phases);
       hwloc_backend_disable(backend);
       errno = EBUSY;
@@ -983,7 +988,7 @@ hwloc_backend_enable(struct hwloc_backend *backend)
   }
 
   if (hwloc_components_verbose)
-    fprintf(stderr, "Enabling discovery component `%s' with phases 0x%x (among 0x%x)\n",
+    fprintf(stderr, "hwloc: Enabling discovery component `%s' with phases 0x%x (among 0x%x)\n",
 	    backend->component->name, backend->phases, backend->component->phases);
 
   /* enqueue at the end */
@@ -1067,7 +1072,7 @@ hwloc_backends_disable_all(struct hwloc_topology *topology)
   while (NULL != (backend = topology->backends)) {
     struct hwloc_backend *next = backend->next;
     if (hwloc_components_verbose)
-      fprintf(stderr, "Disabling discovery component `%s'\n",
+      fprintf(stderr, "hwloc: Disabling discovery component `%s'\n",
 	      backend->component->name);
     hwloc_backend_disable(backend);
     topology->backends = next;
diff --git a/src/3rdparty/hwloc/src/cpukinds.c b/src/3rdparty/hwloc/src/cpukinds.c
index ef6297d7e..074b7a732 100644
--- a/src/3rdparty/hwloc/src/cpukinds.c
+++ b/src/3rdparty/hwloc/src/cpukinds.c
@@ -343,7 +343,8 @@ enum hwloc_cpukinds_ranking {
   HWLOC_CPUKINDS_RANKING_DEFAULT, /* forced + frequency on ARM, forced + coretype_frequency otherwise */
   HWLOC_CPUKINDS_RANKING_NO_FORCED_EFFICIENCY, /* default without forced */
   HWLOC_CPUKINDS_RANKING_FORCED_EFFICIENCY,
-  HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY,
+  HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY, /* either coretype or frequency or both */
+  HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY_STRICT, /* both coretype and frequency are required */
   HWLOC_CPUKINDS_RANKING_CORETYPE,
   HWLOC_CPUKINDS_RANKING_FREQUENCY,
   HWLOC_CPUKINDS_RANKING_FREQUENCY_MAX,
@@ -358,9 +359,9 @@ hwloc__cpukinds_try_rank_by_info(struct hwloc_topology *topology,
 {
   unsigned i;
 
-  if (HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY == heuristics) {
-    hwloc_debug("Trying to rank cpukinds by coretype+frequency...\n");
-    /* we need intel_core_type + (base or max freq) for all kinds */
+  if (HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY_STRICT == heuristics) {
+    hwloc_debug("Trying to rank cpukinds by coretype+frequency_strict...\n");
+    /* we need intel_core_type AND (base or max freq) for all kinds */
     if (!summary->have_intel_core_type
         || (!summary->have_max_freq && !summary->have_base_freq))
       return -1;
@@ -373,6 +374,21 @@ hwloc__cpukinds_try_rank_by_info(struct hwloc_topology *topology,
         kind->ranking_value = (summary->summaries[i].intel_core_type << 20) + summary->summaries[i].max_freq;
     }
 
+  } else if (HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY == heuristics) {
+    hwloc_debug("Trying to rank cpukinds by coretype+frequency...\n");
+    /* we need intel_core_type OR (base or max freq) for all kinds */
+    if (!summary->have_intel_core_type
+        && (!summary->have_max_freq && !summary->have_base_freq))
+      return -1;
+    /* rank first by coretype (Core>>Atom) then by frequency, base if available, max otherwise */
+    for(i=0; i<topology->nr_cpukinds; i++) {
+      struct hwloc_internal_cpukind_s *kind = &topology->cpukinds[i];
+      if (summary->have_base_freq)
+        kind->ranking_value = (summary->summaries[i].intel_core_type << 20) + summary->summaries[i].base_freq;
+      else
+        kind->ranking_value = (summary->summaries[i].intel_core_type << 20) + summary->summaries[i].max_freq;
+    }
+
   } else if (HWLOC_CPUKINDS_RANKING_CORETYPE == heuristics) {
     hwloc_debug("Trying to rank cpukinds by coretype...\n");
     /* we need intel_core_type */
@@ -469,6 +485,8 @@ hwloc_internal_cpukinds_rank(struct hwloc_topology *topology)
       heuristics = HWLOC_CPUKINDS_RANKING_NONE;
     else if (!strcmp(env, "coretype+frequency"))
       heuristics = HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY;
+    else if (!strcmp(env, "coretype+frequency_strict"))
+      heuristics = HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY_STRICT;
     else if (!strcmp(env, "coretype"))
       heuristics = HWLOC_CPUKINDS_RANKING_CORETYPE;
     else if (!strcmp(env, "frequency"))
@@ -481,16 +499,14 @@ hwloc_internal_cpukinds_rank(struct hwloc_topology *topology)
       heuristics = HWLOC_CPUKINDS_RANKING_FORCED_EFFICIENCY;
     else if (!strcmp(env, "no_forced_efficiency"))
       heuristics = HWLOC_CPUKINDS_RANKING_NO_FORCED_EFFICIENCY;
-    else if (!hwloc_hide_errors())
-      fprintf(stderr, "Failed to recognize HWLOC_CPUKINDS_RANKING value %s\n", env);
+    else if (hwloc_hide_errors() < 2)
+      fprintf(stderr, "hwloc: Failed to recognize HWLOC_CPUKINDS_RANKING value %s\n", env);
   }
 
   if (heuristics == HWLOC_CPUKINDS_RANKING_DEFAULT
       || heuristics == HWLOC_CPUKINDS_RANKING_NO_FORCED_EFFICIENCY) {
     /* default is forced_efficiency first */
     struct hwloc_cpukinds_info_summary summary;
-    enum hwloc_cpukinds_ranking subheuristics;
-    const char *arch;
 
     if (heuristics == HWLOC_CPUKINDS_RANKING_DEFAULT)
       hwloc_debug("Using default ranking strategy...\n");
@@ -508,16 +524,7 @@ hwloc_internal_cpukinds_rank(struct hwloc_topology *topology)
       goto failed;
     hwloc__cpukinds_summarize_info(topology, &summary);
 
-    arch = hwloc_obj_get_info_by_name(topology->levels[0][0], "Architecture");
-    /* TODO: rather coretype_frequency only on x86/Intel? */
-    if (arch && (!strncmp(arch, "arm", 3) || !strncmp(arch, "aarch", 5)))
-      /* then frequency on ARM */
-      subheuristics = HWLOC_CPUKINDS_RANKING_FREQUENCY;
-    else
-      /* or coretype+frequency otherwise */
-      subheuristics = HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY;
-
-    err = hwloc__cpukinds_try_rank_by_info(topology, subheuristics, &summary);
+    err = hwloc__cpukinds_try_rank_by_info(topology, HWLOC_CPUKINDS_RANKING_CORETYPE_FREQUENCY, &summary);
     free(summary.summaries);
     if (!err)
       goto ready;
diff --git a/src/3rdparty/hwloc/src/distances.c b/src/3rdparty/hwloc/src/distances.c
index c4854956b..252c253e5 100644
--- a/src/3rdparty/hwloc/src/distances.c
+++ b/src/3rdparty/hwloc/src/distances.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2010-2020 Inria.  All rights reserved.
+ * Copyright © 2010-2021 Inria.  All rights reserved.
  * Copyright © 2011-2012 Université Bordeaux
  * Copyright © 2011 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -17,6 +17,37 @@
 static struct hwloc_internal_distances_s *
 hwloc__internal_distances_from_public(hwloc_topology_t topology, struct hwloc_distances_s *distances);
 
+static void
+hwloc__groups_by_distances(struct hwloc_topology *topology, unsigned nbobjs, struct hwloc_obj **objs, uint64_t *values, unsigned long kind, unsigned nbaccuracies, float *accuracies, int needcheck);
+
+static void
+hwloc_internal_distances_restrict(hwloc_obj_t *objs,
+				  uint64_t *indexes,
+				  hwloc_obj_type_t *different_types,
+				  uint64_t *values,
+				  unsigned nbobjs, unsigned disappeared);
+
+static void
+hwloc_internal_distances_print_matrix(struct hwloc_internal_distances_s *dist)
+{
+  unsigned nbobjs = dist->nbobjs;
+  hwloc_obj_t *objs = dist->objs;
+  hwloc_uint64_t *values = dist->values;
+  int gp = !HWLOC_DIST_TYPE_USE_OS_INDEX(dist->unique_type);
+  unsigned i, j;
+
+  fprintf(stderr, "%s", gp ? "gp_index" : "os_index");
+  for(j=0; j<nbobjs; j++)
+    fprintf(stderr, " % 5d", (int)(gp ? objs[j]->gp_index : objs[j]->os_index));
+  fprintf(stderr, "\n");
+  for(i=0; i<nbobjs; i++) {
+    fprintf(stderr, "  % 5d", (int)(gp ? objs[i]->gp_index : objs[i]->os_index));
+    for(j=0; j<nbobjs; j++)
+      fprintf(stderr, " % 5lld", (long long) values[i*nbobjs + j]);
+    fprintf(stderr, "\n");
+  }
+}
+
 /******************************************************
  * Global init, prepare, destroy, dup
  */
@@ -244,27 +275,33 @@ int hwloc_distances_release_remove(hwloc_topology_t topology,
   return 0;
 }
 
-/******************************************************
- * Add distances to the topology
+/*********************************************************
+ * Backend functions for adding distances to the topology
  */
 
+/* cancel a distances handle. only needed internally for now */
 static void
-hwloc__groups_by_distances(struct hwloc_topology *topology, unsigned nbobjs, struct hwloc_obj **objs, uint64_t *values, unsigned long kind, unsigned nbaccuracies, float *accuracies, int needcheck);
+hwloc_backend_distances_add__cancel(struct hwloc_internal_distances_s *dist)
+{
+  /* everything is set to NULL in hwloc_backend_distances_add_create() */
+  free(dist->name);
+  free(dist->indexes);
+  free(dist->objs);
+  free(dist->different_types);
+  free(dist->values);
+  free(dist);
+}
 
-/* insert a distance matrix in the topology.
- * the caller gives us the distances and objs pointers, we'll free them later.
+/* prepare a distances handle for later commit in the topology.
+ * we duplicate the caller's name.
  */
-static int
-hwloc_internal_distances__add(hwloc_topology_t topology, const char *name,
-			      hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types,
-			      unsigned nbobjs, hwloc_obj_t *objs, uint64_t *indexes, uint64_t *values,
-			      unsigned long kind, unsigned iflags)
+hwloc_backend_distances_add_handle_t
+hwloc_backend_distances_add_create(hwloc_topology_t topology,
+                                   const char *name, unsigned long kind, unsigned long flags)
 {
   struct hwloc_internal_distances_s *dist;
 
-  if (different_types) {
-    kind |= HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES; /* the user isn't forced to give it */
-  } else if (kind & HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES) {
+  if (flags) {
     errno = EINVAL;
     goto err;
   }
@@ -273,110 +310,54 @@ hwloc_internal_distances__add(hwloc_topology_t topology, const char *name,
   if (!dist)
     goto err;
 
-  if (name)
+  if (name) {
     dist->name = strdup(name); /* ignore failure */
-
-  dist->unique_type = unique_type;
-  dist->different_types = different_types;
-  dist->nbobjs = nbobjs;
-  dist->kind = kind;
-  dist->iflags = iflags;
-
-  assert(!!(iflags & HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID) == !!objs);
-
-  if (!objs) {
-    assert(indexes);
-    /* we only have indexes, we'll refresh objs from there */
-    dist->indexes = indexes;
-    dist->objs = calloc(nbobjs, sizeof(hwloc_obj_t));
-    if (!dist->objs)
+    if (!dist->name)
       goto err_with_dist;
-
-  } else {
-    unsigned i;
-    assert(!indexes);
-    /* we only have objs, generate the indexes arrays so that we can refresh objs later */
-    dist->objs = objs;
-    dist->indexes = malloc(nbobjs * sizeof(*dist->indexes));
-    if (!dist->indexes)
-      goto err_with_dist;
-    if (HWLOC_DIST_TYPE_USE_OS_INDEX(dist->unique_type)) {
-      for(i=0; i<nbobjs; i++)
-	dist->indexes[i] = objs[i]->os_index;
-    } else {
-      for(i=0; i<nbobjs; i++)
-	dist->indexes[i] = objs[i]->gp_index;
-    }
   }
 
-  dist->values = values;
+  dist->kind = kind;
+  dist->iflags = HWLOC_INTERNAL_DIST_FLAG_NOT_COMMITTED;
+
+  dist->unique_type = HWLOC_OBJ_TYPE_NONE;
+  dist->different_types = NULL;
+  dist->nbobjs = 0;
+  dist->indexes = NULL;
+  dist->objs = NULL;
+  dist->values = NULL;
 
   dist->id = topology->next_dist_id++;
-
-  if (topology->last_dist)
-    topology->last_dist->next = dist;
-  else
-    topology->first_dist = dist;
-  dist->prev = topology->last_dist;
-  dist->next = NULL;
-  topology->last_dist = dist;
-  return 0;
+  return dist;
 
  err_with_dist:
-  if (name)
-    free(dist->name);
-  free(dist);
+  hwloc_backend_distances_add__cancel(dist);
  err:
-  free(different_types);
-  free(objs);
-  free(indexes);
-  free(values);
-  return -1;
+  return NULL;
 }
 
-int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, const char *name,
-					  hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types, unsigned nbobjs, uint64_t *indexes, uint64_t *values,
-					  unsigned long kind, unsigned long flags)
+/* attach objects and values to a distances handle.
+ * on success, objs and values arrays are attached and will be freed with the distances.
+ * on failure, the handle is freed.
+ */
+int
+hwloc_backend_distances_add_values(hwloc_topology_t topology __hwloc_attribute_unused,
+                                   hwloc_backend_distances_add_handle_t handle,
+                                   unsigned nbobjs, hwloc_obj_t *objs,
+                                   hwloc_uint64_t *values,
+                                   unsigned long flags)
 {
-  unsigned iflags = 0; /* objs not valid */
-
-  if (nbobjs < 2) {
-    errno = EINVAL;
-    goto err;
-  }
-
-  /* cannot group without objects,
-   * and we don't group from XML anyway since the hwloc that generated the XML should have grouped already.
-   */
-  if (flags & HWLOC_DISTANCES_ADD_FLAG_GROUP) {
-    errno = EINVAL;
-    goto err;
-  }
-
-  return hwloc_internal_distances__add(topology, name, unique_type, different_types, nbobjs, NULL, indexes, values, kind, iflags);
-
- err:
-  free(indexes);
-  free(values);
-  free(different_types);
-  return -1;
-}
-
-static void
-hwloc_internal_distances_restrict(hwloc_obj_t *objs,
-				  uint64_t *indexes,
-				  uint64_t *values,
-				  unsigned nbobjs, unsigned disappeared);
-
-int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name,
-				 unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values,
-				 unsigned long kind, unsigned long flags)
-{
-  hwloc_obj_type_t unique_type, *different_types;
+  struct hwloc_internal_distances_s *dist = handle;
+  hwloc_obj_type_t unique_type, *different_types = NULL;
+  hwloc_uint64_t *indexes = NULL;
   unsigned i, disappeared = 0;
-  unsigned iflags = HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID;
 
-  if (nbobjs < 2) {
+  if (dist->nbobjs || !(dist->iflags & HWLOC_INTERNAL_DIST_FLAG_NOT_COMMITTED)) {
+    /* target distances is already set */
+    errno = EINVAL;
+    goto err;
+  }
+
+  if (flags || nbobjs < 2 || !objs || !values) {
     errno = EINVAL;
     goto err;
   }
@@ -389,15 +370,18 @@ int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name,
     /* some objects are NULL */
     if (disappeared == nbobjs) {
       /* nothing left, drop the matrix */
-      free(objs);
-      free(values);
-      return 0;
+      errno = ENOENT;
+      goto err;
     }
     /* restrict the matrix */
-    hwloc_internal_distances_restrict(objs, NULL, values, nbobjs, disappeared);
+    hwloc_internal_distances_restrict(objs, NULL, NULL, values, nbobjs, disappeared);
     nbobjs -= disappeared;
   }
 
+  indexes = malloc(nbobjs * sizeof(*indexes));
+  if (!indexes)
+    goto err;
+
   unique_type = objs[0]->type;
   for(i=1; i<nbobjs; i++)
     if (objs[i]->type != unique_type) {
@@ -408,16 +392,108 @@ int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name,
     /* heterogeneous types */
     different_types = malloc(nbobjs * sizeof(*different_types));
     if (!different_types)
-      goto err;
+      goto err_with_indexes;
     for(i=0; i<nbobjs; i++)
       different_types[i] = objs[i]->type;
-
-  } else {
-    /* homogeneous types */
-    different_types = NULL;
   }
 
-  if (topology->grouping && (flags & HWLOC_DISTANCES_ADD_FLAG_GROUP) && !different_types) {
+  dist->nbobjs = nbobjs;
+  dist->objs = objs;
+  dist->iflags |= HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID;
+  dist->indexes = indexes;
+  dist->unique_type = unique_type;
+  dist->different_types = different_types;
+  dist->values = values;
+
+  if (different_types)
+    dist->kind |= HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES;
+
+  if (HWLOC_DIST_TYPE_USE_OS_INDEX(dist->unique_type)) {
+      for(i=0; i<nbobjs; i++)
+	dist->indexes[i] = objs[i]->os_index;
+    } else {
+      for(i=0; i<nbobjs; i++)
+	dist->indexes[i] = objs[i]->gp_index;
+    }
+
+  return 0;
+
+ err_with_indexes:
+  free(indexes);
+ err:
+  hwloc_backend_distances_add__cancel(dist);
+  return -1;
+}
+
+/* attach objects and values to a distance handle.
+ * on success, objs and values arrays are attached and will be freed with the distances.
+ * on failure, the handle is freed.
+ */
+static int
+hwloc_backend_distances_add_values_by_index(hwloc_topology_t topology __hwloc_attribute_unused,
+                                            hwloc_backend_distances_add_handle_t handle,
+                                            unsigned nbobjs, hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types, hwloc_uint64_t *indexes,
+                                            hwloc_uint64_t *values)
+{
+  struct hwloc_internal_distances_s *dist = handle;
+  hwloc_obj_t *objs;
+
+  if (dist->nbobjs || !(dist->iflags & HWLOC_INTERNAL_DIST_FLAG_NOT_COMMITTED)) {
+    /* target distances is already set */
+    errno = EINVAL;
+    goto err;
+  }
+  if (nbobjs < 2 || !indexes || !values || (unique_type == HWLOC_OBJ_TYPE_NONE && !different_types)) {
+    errno = EINVAL;
+    goto err;
+  }
+
+  objs = malloc(nbobjs * sizeof(*objs));
+  if (!objs)
+    goto err;
+
+  dist->nbobjs = nbobjs;
+  dist->objs = objs;
+  dist->indexes = indexes;
+  dist->unique_type = unique_type;
+  dist->different_types = different_types;
+  dist->values = values;
+
+  if (different_types)
+    dist->kind |= HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES;
+
+  return 0;
+
+ err:
+  hwloc_backend_distances_add__cancel(dist);
+  return -1;
+}
+
+/* commit a distances handle.
+ * on failure, the handle is freed with its objects and values arrays.
+ */
+int
+hwloc_backend_distances_add_commit(hwloc_topology_t topology,
+                                   hwloc_backend_distances_add_handle_t handle,
+                                   unsigned long flags)
+{
+  struct hwloc_internal_distances_s *dist = handle;
+
+  if (!dist->nbobjs || !(dist->iflags & HWLOC_INTERNAL_DIST_FLAG_NOT_COMMITTED)) {
+    /* target distances not ready for commit */
+    errno = EINVAL;
+    goto err;
+  }
+
+  if ((flags & HWLOC_DISTANCES_ADD_FLAG_GROUP) && !dist->objs) {
+    /* cannot group without objects,
+     * and we don't group from XML anyway since the hwloc that generated the XML should have grouped already.
+     */
+    errno = EINVAL;
+    goto err;
+  }
+
+  if (topology->grouping && (flags & HWLOC_DISTANCES_ADD_FLAG_GROUP) && !dist->different_types) {
     float full_accuracy = 0.f;
     float *accuracies;
     unsigned nbaccuracies;
@@ -431,26 +507,94 @@ int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name,
     }
 
     if (topology->grouping_verbose) {
-      unsigned j;
-      int gp = !HWLOC_DIST_TYPE_USE_OS_INDEX(unique_type);
       fprintf(stderr, "Trying to group objects using distance matrix:\n");
-      fprintf(stderr, "%s", gp ? "gp_index" : "os_index");
-      for(j=0; j<nbobjs; j++)
-	fprintf(stderr, " % 5d", (int)(gp ? objs[j]->gp_index : objs[j]->os_index));
-      fprintf(stderr, "\n");
-      for(i=0; i<nbobjs; i++) {
-	fprintf(stderr, "  % 5d", (int)(gp ? objs[i]->gp_index : objs[i]->os_index));
-	for(j=0; j<nbobjs; j++)
-	  fprintf(stderr, " % 5lld", (long long) values[i*nbobjs + j]);
-	fprintf(stderr, "\n");
-      }
+      hwloc_internal_distances_print_matrix(dist);
     }
 
-    hwloc__groups_by_distances(topology, nbobjs, objs, values,
-			       kind, nbaccuracies, accuracies, 1 /* check the first matrice */);
+    hwloc__groups_by_distances(topology, dist->nbobjs, dist->objs, dist->values,
+			       dist->kind, nbaccuracies, accuracies, 1 /* check the first matrix */);
   }
 
-  return hwloc_internal_distances__add(topology, name, unique_type, different_types, nbobjs, objs, NULL, values, kind, iflags);
+  if (topology->last_dist)
+    topology->last_dist->next = dist;
+  else
+    topology->first_dist = dist;
+  dist->prev = topology->last_dist;
+  dist->next = NULL;
+  topology->last_dist = dist;
+
+  dist->iflags &= ~HWLOC_INTERNAL_DIST_FLAG_NOT_COMMITTED;
+  return 0;
+
+ err:
+  hwloc_backend_distances_add__cancel(dist);
+  return -1;
+}
+
+/* all-in-one backend function not exported to plugins, only used by XML for now */
+int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, const char *name,
+                                          hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types, unsigned nbobjs, uint64_t *indexes, uint64_t *values,
+                                          unsigned long kind, unsigned long flags)
+{
+  hwloc_backend_distances_add_handle_t handle;
+  int err;
+
+  handle = hwloc_backend_distances_add_create(topology, name, kind, 0);
+  if (!handle)
+    goto err;
+
+  err = hwloc_backend_distances_add_values_by_index(topology, handle,
+                                                    nbobjs, unique_type, different_types, indexes,
+                                                    values);
+  if (err < 0)
+    goto err;
+
+  /* arrays are now attached to the handle */
+  indexes = NULL;
+  different_types = NULL;
+  values = NULL;
+
+  err = hwloc_backend_distances_add_commit(topology, handle, flags);
+  if (err < 0)
+    goto err;
+
+  return 0;
+
+ err:
+  free(indexes);
+  free(different_types);
+  free(values);
+  return -1;
+}
+
+/* all-in-one backend function not exported to plugins, used by OS backends */
+int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name,
+                                 unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values,
+                                 unsigned long kind, unsigned long flags)
+{
+  hwloc_backend_distances_add_handle_t handle;
+  int err;
+
+  handle = hwloc_backend_distances_add_create(topology, name, kind, 0);
+  if (!handle)
+    goto err;
+
+  err = hwloc_backend_distances_add_values(topology, handle,
+                                           nbobjs, objs,
+                                           values,
+                                           0);
+  if (err < 0)
+    goto err;
+
+  /* arrays are now attached to the handle */
+  objs = NULL;
+  values = NULL;
+
+  err = hwloc_backend_distances_add_commit(topology, handle, flags);
+  if (err < 0)
+    goto err;
+
+  return 0;
 
  err:
   free(objs);
@@ -458,44 +602,54 @@ int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name,
   return -1;
 }
 
+/********************************
+ * User API for adding distances
+ */
+
 #define HWLOC_DISTANCES_KIND_FROM_ALL (HWLOC_DISTANCES_KIND_FROM_OS|HWLOC_DISTANCES_KIND_FROM_USER)
 #define HWLOC_DISTANCES_KIND_MEANS_ALL (HWLOC_DISTANCES_KIND_MEANS_LATENCY|HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH)
-#define HWLOC_DISTANCES_KIND_ALL (HWLOC_DISTANCES_KIND_FROM_ALL|HWLOC_DISTANCES_KIND_MEANS_ALL)
+#define HWLOC_DISTANCES_KIND_ALL (HWLOC_DISTANCES_KIND_FROM_ALL|HWLOC_DISTANCES_KIND_MEANS_ALL|HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES)
 #define HWLOC_DISTANCES_ADD_FLAG_ALL (HWLOC_DISTANCES_ADD_FLAG_GROUP|HWLOC_DISTANCES_ADD_FLAG_GROUP_INACCURATE)
 
-/* The actual function exported to the user
- */
-int hwloc_distances_add(hwloc_topology_t topology,
-			unsigned nbobjs, hwloc_obj_t *objs, hwloc_uint64_t *values,
-			unsigned long kind, unsigned long flags)
+void * hwloc_distances_add_create(hwloc_topology_t topology,
+                                  const char *name, unsigned long kind,
+                                  unsigned long flags)
+{
+  if (!topology->is_loaded) {
+    errno = EINVAL;
+    return NULL;
+  }
+  if (topology->adopted_shmem_addr) {
+    errno = EPERM;
+    return NULL;
+  }
+  if ((kind & ~HWLOC_DISTANCES_KIND_ALL)
+      || hwloc_weight_long(kind & HWLOC_DISTANCES_KIND_FROM_ALL) != 1
+      || hwloc_weight_long(kind & HWLOC_DISTANCES_KIND_MEANS_ALL) != 1) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  return hwloc_backend_distances_add_create(topology, name, kind, flags);
+}
+
+int hwloc_distances_add_values(hwloc_topology_t topology,
+                               void *handle,
+                               unsigned nbobjs, hwloc_obj_t *objs,
+                               hwloc_uint64_t *values,
+                               unsigned long flags)
 {
   unsigned i;
   uint64_t *_values;
   hwloc_obj_t *_objs;
   int err;
 
-  if (nbobjs < 2 || !objs || !values || !topology->is_loaded) {
-    errno = EINVAL;
-    return -1;
-  }
-  if (topology->adopted_shmem_addr) {
-    errno = EPERM;
-    return -1;
-  }
-  if ((kind & ~HWLOC_DISTANCES_KIND_ALL)
-      || hwloc_weight_long(kind & HWLOC_DISTANCES_KIND_FROM_ALL) != 1
-      || hwloc_weight_long(kind & HWLOC_DISTANCES_KIND_MEANS_ALL) != 1
-      || (flags & ~HWLOC_DISTANCES_ADD_FLAG_ALL)) {
-    errno = EINVAL;
-    return -1;
-  }
-
   /* no strict need to check for duplicates, things shouldn't break */
 
   for(i=1; i<nbobjs; i++)
     if (!objs[i]) {
       errno = EINVAL;
-      return -1;
+      goto out;
     }
 
   /* copy the input arrays and give them to the topology */
@@ -506,22 +660,78 @@ int hwloc_distances_add(hwloc_topology_t topology,
 
   memcpy(_objs, objs, nbobjs*sizeof(hwloc_obj_t));
   memcpy(_values, values, nbobjs*nbobjs*sizeof(*_values));
-  err = hwloc_internal_distances_add(topology, NULL, nbobjs, _objs, _values, kind, flags);
-  if (err < 0)
-    goto out; /* _objs and _values freed in hwloc_internal_distances_add() */
+
+  err = hwloc_backend_distances_add_values(topology, handle, nbobjs, _objs, _values, flags);
+  if (err < 0) {
+    /* handle was canceled inside hwloc_backend_distances_add_values */
+    handle = NULL;
+    goto out_with_arrays;
+  }
+
+  return 0;
+
+ out_with_arrays:
+  free(_objs);
+  free(_values);
+ out:
+  if (handle)
+    hwloc_backend_distances_add__cancel(handle);
+  return -1;
+}
+
+int
+hwloc_distances_add_commit(hwloc_topology_t topology,
+                           void *handle,
+                           unsigned long flags)
+{
+  int err;
+
+  if (flags & ~HWLOC_DISTANCES_ADD_FLAG_ALL) {
+    errno = EINVAL;
+    goto out;
+  }
+
+  err = hwloc_backend_distances_add_commit(topology, handle, flags);
+  if (err < 0) {
+    /* handle was canceled inside hwloc_backend_distances_add_commit */
+    handle = NULL;
+    goto out;
+  }
 
   /* in case we added some groups, see if we need to reconnect */
   hwloc_topology_reconnect(topology, 0);
 
   return 0;
 
- out_with_arrays:
-  free(_values);
-  free(_objs);
  out:
+  if (handle)
+    hwloc_backend_distances_add__cancel(handle);
   return -1;
 }
 
+/* deprecated all-in-one user function */
+int hwloc_distances_add(hwloc_topology_t topology,
+			unsigned nbobjs, hwloc_obj_t *objs, hwloc_uint64_t *values,
+			unsigned long kind, unsigned long flags)
+{
+  void *handle;
+  int err;
+
+  handle = hwloc_distances_add_create(topology, NULL, kind, 0);
+  if (!handle)
+    return -1;
+
+  err = hwloc_distances_add_values(topology, handle, nbobjs, objs, values, 0);
+  if (err < 0)
+    return -1;
+
+  err = hwloc_distances_add_commit(topology, handle, flags);
+  if (err < 0)
+    return -1;
+
+  return 0;
+}
+
 /******************************************************
  * Refresh objects in distances
  */
@@ -529,6 +739,7 @@ int hwloc_distances_add(hwloc_topology_t topology,
 static void
 hwloc_internal_distances_restrict(hwloc_obj_t *objs,
 				  uint64_t *indexes,
+                                  hwloc_obj_type_t *different_types,
 				  uint64_t *values,
 				  unsigned nbobjs, unsigned disappeared)
 {
@@ -550,6 +761,8 @@ hwloc_internal_distances_restrict(hwloc_obj_t *objs,
       objs[newi] = objs[i];
       if (indexes)
 	indexes[newi] = indexes[i];
+      if (different_types)
+        different_types[newi] = different_types[i];
       newi++;
     }
 }
@@ -594,7 +807,7 @@ hwloc_internal_distances_refresh_one(hwloc_topology_t topology,
     return -1;
 
   if (disappeared) {
-    hwloc_internal_distances_restrict(objs, dist->indexes, dist->values, nbobjs, disappeared);
+    hwloc_internal_distances_restrict(objs, dist->indexes, dist->different_types, dist->values, nbobjs, disappeared);
     dist->nbobjs -= disappeared;
   }
 
@@ -1087,3 +1300,210 @@ hwloc__groups_by_distances(struct hwloc_topology *topology,
  out_with_groupids:
   free(groupids);
 }
+
+static int
+hwloc__distances_transform_remove_null(struct hwloc_distances_s *distances)
+{
+  hwloc_uint64_t *values = distances->values;
+  hwloc_obj_t *objs = distances->objs;
+  unsigned i, nb, nbobjs = distances->nbobjs;
+  hwloc_obj_type_t unique_type;
+
+  for(i=0, nb=0; i<nbobjs; i++)
+    if (objs[i])
+      nb++;
+
+  if (nb < 2) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (nb == nbobjs)
+    return 0;
+
+  hwloc_internal_distances_restrict(objs, NULL, NULL, values, nbobjs, nbobjs-nb);
+  distances->nbobjs = nb;
+
+  /* update HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES for convenience */
+  unique_type = objs[0]->type;
+  for(i=1; i<nb; i++)
+    if (objs[i]->type != unique_type) {
+      unique_type = HWLOC_OBJ_TYPE_NONE;
+      break;
+    }
+  if (unique_type == HWLOC_OBJ_TYPE_NONE)
+    distances->kind |= HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES;
+  else
+    distances->kind &= ~HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES;
+
+  return 0;
+}
+
+static int
+hwloc__distances_transform_links(struct hwloc_distances_s *distances)
+{
+  /* FIXME: we should look for the greatest common denominator
+   * but we just use the smallest positive value, that's enough for current use-cases.
+   * We'll return -1 in other cases.
+   */
+  hwloc_uint64_t divider, *values = distances->values;
+  unsigned i, nbobjs = distances->nbobjs;
+
+  if (!(distances->kind & HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH)) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  for(i=0; i<nbobjs; i++)
+    values[i*nbobjs+i] = 0;
+
+  /* find the smallest positive value */
+  divider = 0;
+  for(i=0; i<nbobjs*nbobjs; i++)
+    if (values[i] && (!divider || values[i] < divider))
+      divider = values[i];
+
+  if (!divider)
+    /* only zeroes? do nothing */
+    return 0;
+
+  /* check it divides all values */
+  for(i=0; i<nbobjs*nbobjs; i++)
+    if (values[i]%divider) {
+      errno = ENOENT;
+      return -1;
+    }
+
+  /* ok, now divide for real */
+  for(i=0; i<nbobjs*nbobjs; i++)
+    values[i] /= divider;
+
+  return 0;
+}
+
+static __hwloc_inline int is_nvswitch(hwloc_obj_t obj)
+{
+  return obj && obj->subtype && !strcmp(obj->subtype, "NVSwitch");
+}
+
+static int
+hwloc__distances_transform_merge_switch_ports(hwloc_topology_t topology,
+                                              struct hwloc_distances_s *distances)
+{
+  struct hwloc_internal_distances_s *dist = hwloc__internal_distances_from_public(topology, distances);
+  hwloc_obj_t *objs = distances->objs;
+  hwloc_uint64_t *values = distances->values;
+  unsigned first, i, j, nbobjs = distances->nbobjs;
+
+  if (strcmp(dist->name, "NVLinkBandwidth")) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  /* find the first port */
+  first = (unsigned) -1;
+  for(i=0; i<nbobjs; i++)
+    if (is_nvswitch(objs[i])) {
+      first = i;
+      break;
+    }
+  if (first == (unsigned)-1) {
+    errno = ENOENT;
+    return -1;
+  }
+
+  for(j=i+1; j<nbobjs; j++) {
+    if (is_nvswitch(objs[j])) {
+      /* another port, merge it */
+      unsigned k;
+      for(k=0; k<nbobjs; k++) {
+        if (k==i || k==j)
+          continue;
+        values[k*nbobjs+i] += values[k*nbobjs+j];
+        values[k*nbobjs+j] = 0;
+        values[i*nbobjs+k] += values[j*nbobjs+k];
+        values[j*nbobjs+k] = 0;
+      }
+      values[i*nbobjs+i] += values[j*nbobjs+j];
+      values[j*nbobjs+j] = 0;
+    }
+    /* the caller will also call REMOVE_NULL to remove other ports */
+    objs[j] = NULL;
+  }
+
+  return 0;
+}
+
+static int
+hwloc__distances_transform_transitive_closure(hwloc_topology_t topology,
+                                              struct hwloc_distances_s *distances)
+{
+  struct hwloc_internal_distances_s *dist = hwloc__internal_distances_from_public(topology, distances);
+  hwloc_obj_t *objs = distances->objs;
+  hwloc_uint64_t *values = distances->values;
+  unsigned nbobjs = distances->nbobjs;
+  unsigned i, j, k;
+
+  if (strcmp(dist->name, "NVLinkBandwidth")) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  for(i=0; i<nbobjs; i++) {
+    hwloc_uint64_t bw_i2sw = 0;
+    if (is_nvswitch(objs[i]))
+      continue;
+    /* count our BW to the switch */
+    for(k=0; k<nbobjs; k++)
+      if (is_nvswitch(objs[k]))
+        bw_i2sw += values[i*nbobjs+k];
+
+    for(j=0; j<nbobjs; j++) {
+      hwloc_uint64_t bw_sw2j = 0;
+      if (i == j || is_nvswitch(objs[j]))
+        continue;
+      /* count our BW from the switch */
+      for(k=0; k<nbobjs; k++)
+        if (is_nvswitch(objs[k]))
+          bw_sw2j += values[k*nbobjs+j];
+
+      /* bandwidth from i to j is now min(i2sw,sw2j) */
+      values[i*nbobjs+j] = bw_i2sw > bw_sw2j ? bw_sw2j : bw_i2sw;
+    }
+  }
+
+  return 0;
+}
+
+int
+hwloc_distances_transform(hwloc_topology_t topology,
+                          struct hwloc_distances_s *distances,
+                          enum hwloc_distances_transform_e transform,
+                          void *transform_attr,
+                          unsigned long flags)
+{
+  if (flags || transform_attr) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  switch (transform) {
+  case HWLOC_DISTANCES_TRANSFORM_REMOVE_NULL:
+    return hwloc__distances_transform_remove_null(distances);
+  case HWLOC_DISTANCES_TRANSFORM_LINKS:
+    return hwloc__distances_transform_links(distances);
+  case HWLOC_DISTANCES_TRANSFORM_MERGE_SWITCH_PORTS:
+  {
+    int err;
+    err = hwloc__distances_transform_merge_switch_ports(topology, distances);
+    if (!err)
+      err = hwloc__distances_transform_remove_null(distances);
+    return err;
+  }
+  case HWLOC_DISTANCES_TRANSFORM_TRANSITIVE_CLOSURE:
+    return hwloc__distances_transform_transitive_closure(topology, distances);
+  default:
+    errno = EINVAL;
+    return -1;
+  }
+}
diff --git a/src/3rdparty/hwloc/src/pci-common.c b/src/3rdparty/hwloc/src/pci-common.c
index 1149113b7..24626860a 100644
--- a/src/3rdparty/hwloc/src/pci-common.c
+++ b/src/3rdparty/hwloc/src/pci-common.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2009-2020 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * See COPYING in top-level directory.
  */
 
@@ -146,8 +146,9 @@ hwloc_pci_discovery_prepare(struct hwloc_topology *topology)
 	  }
 	  free(buffer);
 	} else {
-	  fprintf(stderr, "Ignoring HWLOC_PCI_LOCALITY file `%s' too large (%lu bytes)\n",
-		  env, (unsigned long) st.st_size);
+          if (hwloc_hide_errors() < 2)
+            fprintf(stderr, "hwloc/pci: Ignoring HWLOC_PCI_LOCALITY file `%s' too large (%lu bytes)\n",
+                    env, (unsigned long) st.st_size);
 	}
       }
       close(fd);
@@ -206,8 +207,11 @@ hwloc_pci_traverse_print_cb(void * cbdata __hwloc_attribute_unused,
     else
       hwloc_debug("%s Bridge [%04x:%04x]", busid,
 		  pcidev->attr->pcidev.vendor_id, pcidev->attr->pcidev.device_id);
-    hwloc_debug(" to %04x:[%02x:%02x]\n",
-		pcidev->attr->bridge.downstream.pci.domain, pcidev->attr->bridge.downstream.pci.secondary_bus, pcidev->attr->bridge.downstream.pci.subordinate_bus);
+    if (pcidev->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI)
+      hwloc_debug(" to %04x:[%02x:%02x]\n",
+                  pcidev->attr->bridge.downstream.pci.domain, pcidev->attr->bridge.downstream.pci.secondary_bus, pcidev->attr->bridge.downstream.pci.subordinate_bus);
+    else
+      assert(0);
   } else
     hwloc_debug("%s Device [%04x:%04x (%04x:%04x) rev=%02x class=%04x]\n", busid,
 		pcidev->attr->pcidev.vendor_id, pcidev->attr->pcidev.device_id,
@@ -251,11 +255,11 @@ hwloc_pci_compare_busids(struct hwloc_obj *a, struct hwloc_obj *b)
   if (a->attr->pcidev.domain > b->attr->pcidev.domain)
     return HWLOC_PCI_BUSID_HIGHER;
 
-  if (a->type == HWLOC_OBJ_BRIDGE
+  if (a->type == HWLOC_OBJ_BRIDGE && a->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI
       && b->attr->pcidev.bus >= a->attr->bridge.downstream.pci.secondary_bus
       && b->attr->pcidev.bus <= a->attr->bridge.downstream.pci.subordinate_bus)
     return HWLOC_PCI_BUSID_SUPERSET;
-  if (b->type == HWLOC_OBJ_BRIDGE
+  if (b->type == HWLOC_OBJ_BRIDGE && b->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI
       && a->attr->pcidev.bus >= b->attr->bridge.downstream.pci.secondary_bus
       && a->attr->pcidev.bus <= b->attr->bridge.downstream.pci.subordinate_bus)
     return HWLOC_PCI_BUSID_INCLUDED;
@@ -302,7 +306,7 @@ hwloc_pci_add_object(struct hwloc_obj *parent, struct hwloc_obj **parent_io_firs
       new->next_sibling = *curp;
       *curp = new;
       new->parent = parent;
-      if (new->type == HWLOC_OBJ_BRIDGE) {
+      if (new->type == HWLOC_OBJ_BRIDGE && new->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI) {
 	/* look at remaining siblings and move some below new */
 	childp = &new->io_first_child;
 	curp = &new->next_sibling;
@@ -329,7 +333,7 @@ hwloc_pci_add_object(struct hwloc_obj *parent, struct hwloc_obj **parent_io_firs
     }
     case HWLOC_PCI_BUSID_EQUAL: {
       static int reported = 0;
-      if (!reported && !hwloc_hide_errors()) {
+      if (!reported && hwloc_hide_errors() < 2) {
         fprintf(stderr, "*********************************************************\n");
         fprintf(stderr, "* hwloc %s received invalid PCI information.\n", HWLOC_VERSION);
         fprintf(stderr, "*\n");
@@ -411,7 +415,7 @@ hwloc_pcidisc_add_hostbridges(struct hwloc_topology *topology,
     dstnextp = &child->next_sibling;
 
     /* compute hostbridge secondary/subordinate buses */
-    if (child->type == HWLOC_OBJ_BRIDGE
+    if (child->type == HWLOC_OBJ_BRIDGE && child->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI
 	&& child->attr->bridge.downstream.pci.subordinate_bus > current_subordinate)
       current_subordinate = child->attr->bridge.downstream.pci.subordinate_bus;
 
@@ -486,7 +490,8 @@ hwloc__pci_find_busid_parent(struct hwloc_topology *topology, struct hwloc_pcide
     if (env) {
       static int reported = 0;
       if (!topology->pci_has_forced_locality && !reported) {
-	fprintf(stderr, "Environment variable %s is deprecated, please use HWLOC_PCI_LOCALITY instead.\n", env);
+        if (!hwloc_hide_errors())
+          fprintf(stderr, "hwloc/pci: Environment variable %s is deprecated, please use HWLOC_PCI_LOCALITY instead.\n", env);
 	reported = 1;
       }
       if (*env) {
@@ -565,7 +570,7 @@ hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *tre
     assert(pciobj->type == HWLOC_OBJ_PCI_DEVICE
 	   || (pciobj->type == HWLOC_OBJ_BRIDGE && pciobj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI));
 
-    if (obj->type == HWLOC_OBJ_BRIDGE) {
+    if (obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI) {
       domain = obj->attr->bridge.downstream.pci.domain;
       bus_min = obj->attr->bridge.downstream.pci.secondary_bus;
       bus_max = obj->attr->bridge.downstream.pci.subordinate_bus;
diff --git a/src/3rdparty/hwloc/src/topology-windows.c b/src/3rdparty/hwloc/src/topology-windows.c
index b6458b6f9..d67c6b994 100644
--- a/src/3rdparty/hwloc/src/topology-windows.c
+++ b/src/3rdparty/hwloc/src/topology-windows.c
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009 CNRS
- * Copyright © 2009-2020 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * Copyright © 2009-2012, 2020 Université Bordeaux
  * Copyright © 2011 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -11,6 +11,7 @@
 
 #include "private/autogen/config.h"
 #include "hwloc.h"
+#include "hwloc/windows.h"
 #include "private/private.h"
 #include "private/debug.h"
 
@@ -190,9 +191,6 @@ typedef struct _PROCESSOR_NUMBER {
 typedef WORD (WINAPI *PFN_GETACTIVEPROCESSORGROUPCOUNT)(void);
 static PFN_GETACTIVEPROCESSORGROUPCOUNT GetActiveProcessorGroupCountProc;
 
-static unsigned long nr_processor_groups = 1;
-static unsigned long max_numanode_index = 0;
-
 typedef WORD (WINAPI *PFN_GETACTIVEPROCESSORCOUNT)(WORD);
 static PFN_GETACTIVEPROCESSORCOUNT GetActiveProcessorCountProc;
 
@@ -270,9 +268,6 @@ static void hwloc_win_get_function_ptrs(void)
 	(PFN_VIRTUALFREEEX) GetProcAddress(kernel32, "VirtualFreeEx");
     }
 
-    if (GetActiveProcessorGroupCountProc)
-      nr_processor_groups = GetActiveProcessorGroupCountProc();
-
     if (!QueryWorkingSetExProc) {
       HMODULE psapi = LoadLibrary("psapi.dll");
       if (psapi)
@@ -363,6 +358,171 @@ static int hwloc_bitmap_to_single_ULONG_PTR(hwloc_const_bitmap_t set, unsigned *
   return 0;
 }
 
+/**********************
+ * Processor Groups
+ */
+
+static unsigned long max_numanode_index = 0;
+
+static unsigned long nr_processor_groups = 1;
+static hwloc_cpuset_t * processor_group_cpusets = NULL;
+
+static void
+hwloc_win_get_processor_groups(void)
+{
+  PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX procInfoTotal, tmpprocInfoTotal, procInfo;
+  DWORD length;
+  unsigned i;
+
+  hwloc_debug("querying windows processor groups\n");
+
+  if (!GetActiveProcessorGroupCountProc || !GetLogicalProcessorInformationExProc)
+    goto error;
+
+  nr_processor_groups = GetActiveProcessorGroupCountProc();
+  if (!nr_processor_groups)
+    goto error;
+
+  hwloc_debug("found %lu windows processor groups\n", nr_processor_groups);
+
+  if (nr_processor_groups > 1 && SIZEOF_VOID_P == 4) {
+    if (!hwloc_hide_errors())
+      fprintf(stderr, "hwloc: multiple processor groups found on 32bits Windows, topology may be invalid/incomplete.\n");
+  }
+
+  length = 0;
+  procInfoTotal = NULL;
+
+  while (1) {
+    if (GetLogicalProcessorInformationExProc(RelationGroup, procInfoTotal, &length))
+      break;
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+      goto error;
+    tmpprocInfoTotal = realloc(procInfoTotal, length);
+    if (!tmpprocInfoTotal)
+      goto error_with_procinfo;
+    procInfoTotal = tmpprocInfoTotal;
+  }
+
+  processor_group_cpusets = calloc(nr_processor_groups, sizeof(*processor_group_cpusets));
+  if (!processor_group_cpusets)
+    goto error_with_procinfo;
+
+  for (procInfo = procInfoTotal;
+       (void*) procInfo < (void*) ((uintptr_t) procInfoTotal + length);
+       procInfo = (void*) ((uintptr_t) procInfo + procInfo->Size)) {
+    unsigned id;
+
+    assert(procInfo->Relationship == RelationGroup);
+
+    for (id = 0; id < procInfo->Group.ActiveGroupCount; id++) {
+      KAFFINITY mask;
+      hwloc_bitmap_t set;
+
+      set = hwloc_bitmap_alloc();
+      if (!set)
+        goto error_with_cpusets;
+
+      mask = procInfo->Group.GroupInfo[id].ActiveProcessorMask;
+      hwloc_debug("group %u %d cpus mask %lx\n", id,
+                  procInfo->Group.GroupInfo[id].ActiveProcessorCount, mask);
+      /* KAFFINITY is ULONG_PTR */
+      hwloc_bitmap_set_ith_ULONG_PTR(set, id, mask);
+      /* FIXME: what if running 32bits on a 64bits windows with 64-processor groups?
+       * ULONG_PTR is 32bits, so half the group is invisible?
+       * maybe scale id to id*8/sizeof(ULONG_PTR) so that groups are 64-PU aligned?
+       */
+      hwloc_debug_2args_bitmap("group %u %d bitmap %s\n", id, procInfo->Group.GroupInfo[id].ActiveProcessorCount, set);
+      processor_group_cpusets[id] = set;
+    }
+  }
+
+  free(procInfoTotal);
+  return;
+
+ error_with_cpusets:
+  for(i=0; i<nr_processor_groups; i++) {
+    if (processor_group_cpusets[i])
+      hwloc_bitmap_free(processor_group_cpusets[i]);
+  }
+  free(processor_group_cpusets);
+  processor_group_cpusets = NULL;
+ error_with_procinfo:
+  free(procInfoTotal);
+ error:
+  /* on error set nr to 1 and keep cpusets NULL. We'll use the topology cpuset whenever needed */
+  nr_processor_groups = 1;
+}
+
+static void
+hwloc_win_free_processor_groups(void)
+{
+  unsigned i;
+  for(i=0; i<nr_processor_groups; i++) {
+    if (processor_group_cpusets[i])
+      hwloc_bitmap_free(processor_group_cpusets[i]);
+  }
+  free(processor_group_cpusets);
+  processor_group_cpusets = NULL;
+  nr_processor_groups = 1;
+}
+
+
+int
+hwloc_windows_get_nr_processor_groups(hwloc_topology_t topology, unsigned long flags)
+{
+  if (!topology->is_loaded || !topology->is_thissystem) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (flags) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  return nr_processor_groups;
+}
+
+int
+hwloc_windows_get_processor_group_cpuset(hwloc_topology_t topology, unsigned pg_index, hwloc_cpuset_t cpuset, unsigned long flags)
+{
+  if (!topology->is_loaded || !topology->is_thissystem) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (!cpuset) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (flags) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (pg_index >= nr_processor_groups) {
+    errno = ENOENT;
+    return -1;
+  }
+
+  if (!processor_group_cpusets) {
+    assert(nr_processor_groups == 1);
+    /* we found no processor groups, return the entire topology as a single one */
+    hwloc_bitmap_copy(cpuset, topology->levels[0][0]->cpuset);
+    return 0;
+  }
+
+  if (!processor_group_cpusets[pg_index]) {
+    errno = ENOENT;
+    return -1;
+  }
+
+  hwloc_bitmap_copy(cpuset, processor_group_cpusets[pg_index]);
+  return 0;
+}
+
 /**************************************************************
  * hwloc PU numbering with respect to Windows processor groups
  *
@@ -1328,11 +1488,13 @@ hwloc_set_windows_hooks(struct hwloc_binding_hooks *hooks,
 static int hwloc_windows_component_init(unsigned long flags __hwloc_attribute_unused)
 {
   hwloc_win_get_function_ptrs();
+  hwloc_win_get_processor_groups();
   return 0;
 }
 
 static void hwloc_windows_component_finalize(unsigned long flags __hwloc_attribute_unused)
 {
+  hwloc_win_free_processor_groups();
 }
 
 static struct hwloc_backend *
diff --git a/src/3rdparty/hwloc/src/topology-x86.c b/src/3rdparty/hwloc/src/topology-x86.c
index 71e396e29..c326371b1 100644
--- a/src/3rdparty/hwloc/src/topology-x86.c
+++ b/src/3rdparty/hwloc/src/topology-x86.c
@@ -7,11 +7,14 @@
  *
  * This backend is only used when the operating system does not export
  * the necessary hardware topology information to user-space applications.
- * Currently, only the FreeBSD backend relies on this x86 backend.
+ * Currently, FreeBSD and NetBSD only add PUs and then fallback to this
+ * backend for CPU/Cache discovery.
  *
  * Other backends such as Linux have their own way to retrieve various
  * pieces of hardware topology information from the operating system
  * on various architectures, without having to use this x86-specific code.
+ * But this backend is still used after them to annotate some objects with
+ * additional details (CPU info in Package, Inclusiveness in Caches).
  */
 
 #include "private/autogen/config.h"
@@ -1257,7 +1260,8 @@ static int
 look_procs(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags,
 	   unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type,
 	   int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags),
-	   int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags))
+	   int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags),
+           hwloc_bitmap_t restrict_set)
 {
   struct hwloc_x86_backend_data_s *data = backend->private_data;
   struct hwloc_topology *topology = backend->topology;
@@ -1277,6 +1281,12 @@ look_procs(struct hwloc_backend *backend, struct procinfo *infos, unsigned long
 
   for (i = 0; i < nbprocs; i++) {
     struct cpuiddump *src_cpuiddump = NULL;
+
+    if (restrict_set && !hwloc_bitmap_isset(restrict_set, i)) {
+      /* skip this CPU outside of the binding mask */
+      continue;
+    }
+
     if (data->src_cpuiddump_path) {
       src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, i);
       if (!src_cpuiddump)
@@ -1410,6 +1420,7 @@ static
 int hwloc_look_x86(struct hwloc_backend *backend, unsigned long flags)
 {
   struct hwloc_x86_backend_data_s *data = backend->private_data;
+  struct hwloc_topology *topology = backend->topology;
   unsigned nbprocs = data->nbprocs;
   unsigned eax, ebx, ecx = 0, edx;
   unsigned i;
@@ -1425,9 +1436,21 @@ int hwloc_look_x86(struct hwloc_backend *backend, unsigned long flags)
   struct hwloc_topology_membind_support memsupport __hwloc_attribute_unused;
   int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags) = NULL;
   int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags) = NULL;
+  hwloc_bitmap_t restrict_set = NULL;
   struct cpuiddump *src_cpuiddump = NULL;
   int ret = -1;
 
+  /* check if binding works */
+  memset(&hooks, 0, sizeof(hooks));
+  support.membind = &memsupport;
+  /* We could just copy the main hooks (except in some corner cases),
+   * but the current overhead is negligible, so just always reget them.
+   */
+  hwloc_set_native_binding_hooks(&hooks, &support);
+  /* in theory, those are only needed if !data->src_cpuiddump_path || HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_BINDING
+   * but that's the vast majority of cases anyway, and the overhead is very small.
+   */
+
   if (data->src_cpuiddump_path) {
     /* Just read cpuid from the dump (implies !topology->is_thissystem by default) */
     src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, 0);
@@ -1440,13 +1463,6 @@ int hwloc_look_x86(struct hwloc_backend *backend, unsigned long flags)
      * we may still force use this backend when debugging with !thissystem.
      */
 
-    /* check if binding works */
-    memset(&hooks, 0, sizeof(hooks));
-    support.membind = &memsupport;
-    /* We could just copy the main hooks (except in some corner cases),
-     * but the current overhead is negligible, so just always reget them.
-     */
-    hwloc_set_native_binding_hooks(&hooks, &support);
     if (hooks.get_thisthread_cpubind && hooks.set_thisthread_cpubind) {
       get_cpubind = hooks.get_thisthread_cpubind;
       set_cpubind = hooks.set_thisthread_cpubind;
@@ -1466,6 +1482,20 @@ int hwloc_look_x86(struct hwloc_backend *backend, unsigned long flags)
     }
   }
 
+  if (topology->flags & HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING) {
+    restrict_set = hwloc_bitmap_alloc();
+    if (!restrict_set)
+      goto out;
+    if (hooks.get_thisproc_cpubind)
+      hooks.get_thisproc_cpubind(topology, restrict_set, 0);
+    else if (hooks.get_thisthread_cpubind)
+      hooks.get_thisthread_cpubind(topology, restrict_set, 0);
+    if (hwloc_bitmap_iszero(restrict_set)) {
+      hwloc_bitmap_free(restrict_set);
+      restrict_set = NULL;
+    }
+  }
+
   if (!src_cpuiddump && !hwloc_have_x86_cpuid())
     goto out;
 
@@ -1530,7 +1560,7 @@ int hwloc_look_x86(struct hwloc_backend *backend, unsigned long flags)
 
   ret = look_procs(backend, infos, flags,
 		   highest_cpuid, highest_ext_cpuid, features, cpuid_type,
-		   get_cpubind, set_cpubind);
+		   get_cpubind, set_cpubind, restrict_set);
   if (!ret)
     /* success, we're done */
     goto out_with_os_state;
@@ -1555,6 +1585,7 @@ out_with_infos:
   }
 
 out:
+  hwloc_bitmap_free(restrict_set);
   if (src_cpuiddump)
     cpuiddump_free(src_cpuiddump);
   return ret;
@@ -1571,6 +1602,11 @@ hwloc_x86_discover(struct hwloc_backend *backend, struct hwloc_disc_status *dsta
 
   assert(dstatus->phase == HWLOC_DISC_PHASE_CPU);
 
+  if (topology->flags & HWLOC_TOPOLOGY_FLAG_DONT_CHANGE_BINDING) {
+    /* TODO: Things would work if there's a single PU, no need to rebind */
+    return 0;
+  }
+
   if (getenv("HWLOC_X86_TOPOEXT_NUMANODES")) {
     flags |= HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES;
   }
diff --git a/src/3rdparty/hwloc/src/topology-xml.c b/src/3rdparty/hwloc/src/topology-xml.c
index 6aacc052a..87e91010c 100644
--- a/src/3rdparty/hwloc/src/topology-xml.c
+++ b/src/3rdparty/hwloc/src/topology-xml.c
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009 CNRS
- * Copyright © 2009-2020 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * Copyright © 2009-2011, 2020 Université Bordeaux
  * Copyright © 2009-2018 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -192,8 +192,9 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology,
 	  || lvalue == HWLOC_OBJ_CACHE_INSTRUCTION)
 	obj->attr->cache.type = (hwloc_obj_cache_type_t) lvalue;
       else
-	fprintf(stderr, "%s: ignoring invalid cache_type attribute %lu\n",
-		state->global->msgprefix, lvalue);
+        if (hwloc__xml_verbose())
+          fprintf(stderr, "%s: ignoring invalid cache_type attribute %lu\n",
+                  state->global->msgprefix, lvalue);
     } else if (hwloc__xml_verbose())
       fprintf(stderr, "%s: ignoring cache_type attribute for non-cache object type\n",
 	      state->global->msgprefix);
@@ -262,8 +263,8 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology,
 #ifndef HWLOC_HAVE_32BITS_PCI_DOMAIN
       } else if (domain > 0xffff) {
 	static int warned = 0;
-	if (!warned && !hwloc_hide_errors())
-	  fprintf(stderr, "Ignoring PCI device with non-16bit domain.\nPass --enable-32bits-pci-domain to configure to support such devices\n(warning: it would break the library ABI, don't enable unless really needed).\n");
+	if (!warned && hwloc_hide_errors() < 2)
+	  fprintf(stderr, "hwloc/xml: Ignoring PCI device with non-16bit domain.\nPass --enable-32bits-pci-domain to configure to support such devices\n(warning: it would break the library ABI, don't enable unless really needed).\n");
 	warned = 1;
 	*ignore = 1;
 #endif
@@ -337,6 +338,7 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology,
       } else {
 	obj->attr->bridge.upstream_type = (hwloc_obj_bridge_type_t) upstream_type;
 	obj->attr->bridge.downstream_type = (hwloc_obj_bridge_type_t) downstream_type;
+        /* FIXME verify that upstream/downstream type is valid */
       };
       break;
     }
@@ -361,12 +363,13 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology,
 #ifndef HWLOC_HAVE_32BITS_PCI_DOMAIN
       } else if (domain > 0xffff) {
 	static int warned = 0;
-	if (!warned && !hwloc_hide_errors())
-	  fprintf(stderr, "Ignoring bridge to PCI with non-16bit domain.\nPass --enable-32bits-pci-domain to configure to support such devices\n(warning: it would break the library ABI, don't enable unless really needed).\n");
+	if (!warned && hwloc_hide_errors() < 2)
+	  fprintf(stderr, "hwloc/xml: Ignoring bridge to PCI with non-16bit domain.\nPass --enable-32bits-pci-domain to configure to support such devices\n(warning: it would break the library ABI, don't enable unless really needed).\n");
 	warned = 1;
 	*ignore = 1;
 #endif
       } else {
+        /* FIXME verify that downstream type vs pci info are valid */
 	obj->attr->bridge.downstream.pci.domain = domain;
 	obj->attr->bridge.downstream.pci.secondary_bus = secbus;
 	obj->attr->bridge.downstream.pci.subordinate_bus = subbus;
@@ -1232,7 +1235,7 @@ hwloc__xml_import_object(hwloc_topology_t topology,
 	/* next should be before cur */
 	if (!childrengotignored) {
 	  static int reported = 0;
-	  if (!reported && !hwloc_hide_errors()) {
+	  if (!reported && hwloc_hide_errors() < 2) {
 	    hwloc__xml_import_report_outoforder(topology, next, cur);
 	    reported = 1;
 	  }
@@ -1565,7 +1568,7 @@ hwloc__xml_v2import_distances(hwloc_topology_t topology,
     }
   }
 
-  hwloc_internal_distances_add_by_index(topology, name, unique_type, different_types, nbobjs, indexes, u64values, kind, 0);
+  hwloc_internal_distances_add_by_index(topology, name, unique_type, different_types, nbobjs, indexes, u64values, kind, 0 /* assume grouping was applied when this matrix was discovered before exporting to XML */);
 
   /* prevent freeing below */
   indexes = NULL;
@@ -2647,7 +2650,8 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo
 
       logical_to_v2array = malloc(nbobjs * sizeof(*logical_to_v2array));
       if (!logical_to_v2array) {
-	fprintf(stderr, "xml/export/v1: failed to allocated logical_to_v2array\n");
+        if (!hwloc_hide_errors())
+          fprintf(stderr, "hwloc/xml/export/v1: failed to allocated logical_to_v2array\n");
 	continue;
       }
 
diff --git a/src/3rdparty/hwloc/src/topology.c b/src/3rdparty/hwloc/src/topology.c
index 3944f3c19..01e5a863c 100644
--- a/src/3rdparty/hwloc/src/topology.c
+++ b/src/3rdparty/hwloc/src/topology.c
@@ -52,6 +52,42 @@
 #include <windows.h>
 #endif
 
+/*
+ * Define ZES_ENABLE_SYSMAN=1 early so that the LevelZero backend gets Sysman enabled.
+ * Use the constructor if supported and/or the Windows DllMain callback.
+ * Do it in the main hwloc library instead of the levelzero component because
+ * the latter could be loaded later as a plugin.
+ *
+ * L0 seems to be using getenv() to check this variable on Windows
+ * (at least in the Intel Compute-Runtime of March 2021),
+ * so use putenv() to set the variable.
+ *
+ * For the record, Get/SetEnvironmentVariable() is not exactly the same as getenv/putenv():
+ * - getenv() doesn't see what was set with SetEnvironmentVariable()
+ * - GetEnvironmentVariable() doesn't see putenv() in cygwin (while it does in MSVC and MinGW).
+ * Hence, if L0 ever switches from getenv() to GetEnvironmentVariable(),
+ * it will break in cygwin, we'll have to use both putenv() and SetEnvironmentVariable().
+ * Hopefully L0 will be provide a way to enable Sysman without env vars before it happens.
+ */
+#ifdef HWLOC_HAVE_ATTRIBUTE_CONSTRUCTOR
+static void hwloc_constructor(void) __attribute__((constructor));
+static void hwloc_constructor(void)
+{
+  if (!getenv("ZES_ENABLE_SYSMAN"))
+    putenv((char *) "ZES_ENABLE_SYSMAN=1");
+}
+#endif
+#ifdef HWLOC_WIN_SYS
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
+{
+  if (fdwReason == DLL_PROCESS_ATTACH) {
+    if (!getenv("ZES_ENABLE_SYSMAN"))
+      putenv((char *) "ZES_ENABLE_SYSMAN=1");
+  }
+  return TRUE;
+}
+#endif
+
 unsigned hwloc_get_api_version(void)
 {
   return HWLOC_API_VERSION;
@@ -64,7 +100,7 @@ int hwloc_topology_abi_check(hwloc_topology_t topology)
 
 int hwloc_hide_errors(void)
 {
-  static int hide = 0;
+  static int hide = 1; /* only show critical errors by default. lstopo will show others */
   static int checked = 0;
   if (!checked) {
     const char *envvar = getenv("HWLOC_HIDE_ERRORS");
@@ -106,7 +142,7 @@ static void report_insert_error(hwloc_obj_t new, hwloc_obj_t old, const char *ms
 {
   static int reported = 0;
 
-  if (reason && !reported && !hwloc_hide_errors()) {
+  if (reason && !reported && hwloc_hide_errors() < 2) {
     char newstr[512];
     char oldstr[512];
     report_insert_error_format_obj(newstr, sizeof(newstr), new);
@@ -2307,9 +2343,15 @@ hwloc__filter_bridges(hwloc_topology_t topology, hwloc_obj_t root, unsigned dept
 
     child->attr->bridge.depth = depth;
 
-    if (child->type == HWLOC_OBJ_BRIDGE
-	&& filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT
-	&& !child->io_first_child) {
+    /* remove bridges that have no child,
+     * and pci-to-non-pci bridges (pcidev) that no child either.
+     * keep NVSwitch since they may be used in NVLink matrices.
+     */
+    if (filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT
+	&& !child->io_first_child
+        && (child->type == HWLOC_OBJ_BRIDGE
+            || (child->type == HWLOC_OBJ_PCI_DEVICE && (child->attr->pcidev.class_id >> 8) == 0x06
+                && (!child->subtype || strcmp(child->subtype, "NVSwitch"))))) {
       unlink_and_free_single_object(pchild);
       topology->modified = 1;
     }
@@ -3088,7 +3130,8 @@ hwloc_connect_levels(hwloc_topology_t topology)
       tmpnbobjs = realloc(topology->level_nbobjects,
 			  2 * topology->nb_levels_allocated * sizeof(*topology->level_nbobjects));
       if (!tmplevels || !tmpnbobjs) {
-	fprintf(stderr, "hwloc failed to realloc level arrays to %u\n", topology->nb_levels_allocated * 2);
+        if (hwloc_hide_errors() < 2)
+          fprintf(stderr, "hwloc: failed to realloc level arrays to %u\n", topology->nb_levels_allocated * 2);
 
 	/* if one realloc succeeded, make sure the caller will free the new buffer */
 	if (tmplevels)
@@ -3470,15 +3513,18 @@ hwloc_discover(struct hwloc_topology *topology,
   hwloc_debug("%s", "\nRemoving empty objects\n");
   remove_empty(topology, &topology->levels[0][0]);
   if (!topology->levels[0][0]) {
-    fprintf(stderr, "Topology became empty, aborting!\n");
+    if (hwloc_hide_errors() < 2)
+      fprintf(stderr, "hwloc: Topology became empty, aborting!\n");
     return -1;
   }
   if (hwloc_bitmap_iszero(topology->levels[0][0]->cpuset)) {
-    fprintf(stderr, "Topology does not contain any PU, aborting!\n");
+    if (hwloc_hide_errors() < 2)
+      fprintf(stderr, "hwloc: Topology does not contain any PU, aborting!\n");
     return -1;
   }
   if (hwloc_bitmap_iszero(topology->levels[0][0]->nodeset)) {
-    fprintf(stderr, "Topology does not contain any NUMA node, aborting!\n");
+    if (hwloc_hide_errors() < 2)
+      fprintf(stderr, "hwloc: Topology does not contain any NUMA node, aborting!\n");
     return -1;
   }
   hwloc_debug_print_objects(0, topology->levels[0][0]);
@@ -3716,7 +3762,18 @@ hwloc_topology_set_flags (struct hwloc_topology *topology, unsigned long flags)
     return -1;
   }
 
-  if (flags & ~(HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED|HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM|HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES|HWLOC_TOPOLOGY_FLAG_IMPORT_SUPPORT)) {
+  if (flags & ~(HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED|HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM|HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES|HWLOC_TOPOLOGY_FLAG_IMPORT_SUPPORT|HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING|HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_MEMBINDING|HWLOC_TOPOLOGY_FLAG_DONT_CHANGE_BINDING)) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if ((flags & (HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING|HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM)) == HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING) {
+    /* RESTRICT_TO_CPUBINDING requires THISSYSTEM for binding */
+    errno = EINVAL;
+    return -1;
+  }
+  if ((flags & (HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_MEMBINDING|HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM)) == HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_MEMBINDING) {
+    /* RESTRICT_TO_MEMBINDING requires THISSYSTEM for binding */
     errno = EINVAL;
     return -1;
   }
@@ -4003,6 +4060,31 @@ hwloc_topology_load (struct hwloc_topology *topology)
 
   topology->is_loaded = 1;
 
+  if (topology->flags & HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING) {
+    /* FIXME: filter directly in backends during the discovery.
+     * Only x86 does it because binding may cause issues on Windows.
+     */
+    hwloc_bitmap_t set = hwloc_bitmap_alloc();
+    if (set) {
+      err = hwloc_get_cpubind(topology, set, HWLOC_CPUBIND_STRICT);
+      if (!err)
+        hwloc_topology_restrict(topology, set, 0);
+      hwloc_bitmap_free(set);
+    }
+  }
+  if (topology->flags & HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_MEMBINDING) {
+    /* FIXME: filter directly in backends during the discovery.
+     */
+    hwloc_bitmap_t set = hwloc_bitmap_alloc();
+    hwloc_membind_policy_t policy;
+    if (set) {
+      err = hwloc_get_membind(topology, set, &policy, HWLOC_MEMBIND_STRICT | HWLOC_MEMBIND_BYNODESET);
+      if (!err)
+        hwloc_topology_restrict(topology, set, HWLOC_RESTRICT_FLAG_BYNODESET);
+      hwloc_bitmap_free(set);
+    }
+  }
+
   if (topology->backend_phases & HWLOC_DISC_PHASE_TWEAK) {
     dstatus.phase = HWLOC_DISC_PHASE_TWEAK;
     hwloc_discover_by_phase(topology, &dstatus, "TWEAK");
diff --git a/src/3rdparty/hwloc/src/traversal.c b/src/3rdparty/hwloc/src/traversal.c
index f9076ab53..6765d702e 100644
--- a/src/3rdparty/hwloc/src/traversal.c
+++ b/src/3rdparty/hwloc/src/traversal.c
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2009 CNRS
- * Copyright © 2009-2020 Inria.  All rights reserved.
+ * Copyright © 2009-2021 Inria.  All rights reserved.
  * Copyright © 2009-2010, 2020 Université Bordeaux
  * Copyright © 2009-2011 Cisco Systems, Inc.  All rights reserved.
  * See COPYING in top-level directory.
@@ -395,6 +395,8 @@ hwloc_type_sscanf(const char *string, hwloc_obj_type_t *typep,
   } else if (hwloc__type_match(string, "pcibridge", 5)) {
     type = HWLOC_OBJ_BRIDGE;
     ubtype = HWLOC_OBJ_BRIDGE_PCI;
+    /* if downstream_type can ever be non-PCI, we'll have to make strings more precise,
+     * or relax the hwloc_type_sscanf test */
 
   } else if (hwloc__type_match(string, "pcidev", 3)) {
     type = HWLOC_OBJ_PCI_DEVICE;
@@ -448,7 +450,9 @@ hwloc_type_sscanf(const char *string, hwloc_obj_type_t *typep,
       attrp->group.depth = depthattr;
     } else if (type == HWLOC_OBJ_BRIDGE && attrsize >= sizeof(attrp->bridge)) {
       attrp->bridge.upstream_type = ubtype;
-      attrp->bridge.downstream_type = HWLOC_OBJ_BRIDGE_PCI; /* nothing else so far */
+      attrp->bridge.downstream_type = HWLOC_OBJ_BRIDGE_PCI;
+      /* if downstream_type can ever be non-PCI, we'll have to make strings more precise,
+       * or relax the hwloc_type_sscanf test */
     } else if (type == HWLOC_OBJ_OS_DEVICE && attrsize >= sizeof(attrp->osdev)) {
       attrp->osdev.type = ostype;
     }
@@ -531,6 +535,9 @@ hwloc_obj_type_snprintf(char * __hwloc_restrict string, size_t size, hwloc_obj_t
     else
       return hwloc_snprintf(string, size, "%s", hwloc_obj_type_string(type));
   case HWLOC_OBJ_BRIDGE:
+    /* if downstream_type can ever be non-PCI, we'll have to make strings more precise,
+     * or relax the hwloc_type_sscanf test */
+    assert(obj->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI);
     return hwloc_snprintf(string, size, obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI ? "PCIBridge" : "HostBridge");
   case HWLOC_OBJ_PCI_DEVICE:
     return hwloc_snprintf(string, size, "PCI");
@@ -648,8 +655,11 @@ hwloc_obj_attr_snprintf(char * __hwloc_restrict string, size_t size, hwloc_obj_t
       } else
         *up = '\0';
       /* downstream is_PCI */
-      snprintf(down, sizeof(down), "buses=%04x:[%02x-%02x]",
-	       obj->attr->bridge.downstream.pci.domain, obj->attr->bridge.downstream.pci.secondary_bus, obj->attr->bridge.downstream.pci.subordinate_bus);
+      if (obj->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI) {
+        snprintf(down, sizeof(down), "buses=%04x:[%02x-%02x]",
+                 obj->attr->bridge.downstream.pci.domain, obj->attr->bridge.downstream.pci.secondary_bus, obj->attr->bridge.downstream.pci.subordinate_bus);
+      } else
+        assert(0);
       if (*up)
 	res = hwloc_snprintf(string, size, "%s%s%s", up, separator, down);
       else
@@ -736,3 +746,92 @@ int hwloc_bitmap_singlify_per_core(hwloc_topology_t topology, hwloc_bitmap_t cpu
   }
   return 0;
 }
+
+hwloc_obj_t
+hwloc_get_obj_with_same_locality(hwloc_topology_t topology, hwloc_obj_t src,
+                                 hwloc_obj_type_t type, const char *subtype, const char *nameprefix,
+                                 unsigned long flags)
+{
+  if (flags) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  if (hwloc_obj_type_is_normal(src->type) || hwloc_obj_type_is_memory(src->type)) {
+    /* normal/memory type, look for normal/memory type with same sets */
+    hwloc_obj_t obj;
+
+    if (!hwloc_obj_type_is_normal(type) && !hwloc_obj_type_is_memory(type)) {
+      errno = EINVAL;
+      return NULL;
+    }
+
+    obj = NULL;
+    while ((obj = hwloc_get_next_obj_by_type(topology, type, obj)) != NULL) {
+      if (!hwloc_bitmap_isequal(src->cpuset, obj->cpuset)
+          || !hwloc_bitmap_isequal(src->nodeset, obj->nodeset))
+        continue;
+      if (subtype && (!obj->subtype || strcasecmp(subtype, obj->subtype)))
+        continue;
+      if (nameprefix && (!obj->name || hwloc_strncasecmp(nameprefix, obj->name, strlen(nameprefix))))
+        continue;
+      return obj;
+    }
+    errno = ENOENT;
+    return NULL;
+
+  } else if (hwloc_obj_type_is_io(src->type)) {
+    /* I/O device, look for PCI/OS in same PCI */
+    hwloc_obj_t pci;
+
+    if ((src->type != HWLOC_OBJ_OS_DEVICE && src->type != HWLOC_OBJ_PCI_DEVICE)
+        || (type != HWLOC_OBJ_OS_DEVICE && type != HWLOC_OBJ_PCI_DEVICE)) {
+      errno = EINVAL;
+      return NULL;
+    }
+
+    /* walk up to find the container */
+    pci = src;
+    while (pci->type == HWLOC_OBJ_OS_DEVICE)
+      pci = pci->parent;
+
+    if (type == HWLOC_OBJ_PCI_DEVICE) {
+      if (pci->type != HWLOC_OBJ_PCI_DEVICE) {
+        errno = ENOENT;
+        return NULL;
+      }
+      if (subtype && (!pci->subtype || strcasecmp(subtype, pci->subtype))) {
+        errno = ENOENT;
+        return NULL;
+      }
+      if (nameprefix && (!pci->name || hwloc_strncasecmp(nameprefix, pci->name, strlen(nameprefix)))) {
+        errno = ENOENT;
+        return NULL;
+      }
+      return pci;
+
+    } else {
+      /* find a matching osdev child */
+      assert(type == HWLOC_OBJ_OS_DEVICE);
+      /* FIXME: won't work if we ever store osdevs in osdevs */
+      hwloc_obj_t child;
+      for(child = pci->io_first_child; child; child = child->next_sibling) {
+        if (child->type != HWLOC_OBJ_OS_DEVICE)
+          /* FIXME: should never occur currently */
+          continue;
+        if (subtype && (!child->subtype || strcasecmp(subtype, child->subtype)))
+          continue;
+        if (nameprefix && (!child->name || hwloc_strncasecmp(nameprefix, child->name, strlen(nameprefix))))
+          continue;
+        return child;
+      }
+    }
+    errno = ENOENT;
+    return NULL;
+
+  } else {
+    /* nothing for Misc */
+    errno = EINVAL;
+    return NULL;
+  }
+}

From b52c2899314a452e2797a03a2dc55e3778a4a144 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sat, 28 Aug 2021 12:32:57 +0700
Subject: [PATCH 13/20] Increase RANDOMX_PROGRAM_MAX_SIZE

---
 src/crypto/randomx/configuration.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/crypto/randomx/configuration.h b/src/crypto/randomx/configuration.h
index 3681a783e..be344c494 100644
--- a/src/crypto/randomx/configuration.h
+++ b/src/crypto/randomx/configuration.h
@@ -41,7 +41,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define RANDOMX_DATASET_MAX_SIZE  2181038080
 
 // Increase it if some configs use larger programs
-#define RANDOMX_PROGRAM_MAX_SIZE       256
+#define RANDOMX_PROGRAM_MAX_SIZE       280
 
 // Increase it if some configs use larger scratchpad
 #define RANDOMX_SCRATCHPAD_L3_MAX_SIZE      2097152

From 6e4fea34a47336857380522f16e37f72e9b81c4a Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sat, 28 Aug 2021 13:10:48 +0700
Subject: [PATCH 14/20] #2555 Update deps.

---
 scripts/build.hwloc.sh   | 4 ++--
 scripts/build.openssl.sh | 2 +-
 scripts/build.uv.sh      | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/scripts/build.hwloc.sh b/scripts/build.hwloc.sh
index 668090758..022f67fe2 100755
--- a/scripts/build.hwloc.sh
+++ b/scripts/build.hwloc.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-HWLOC_VERSION="2.4.1"
+HWLOC_VERSION="2.5.0"
 
 mkdir -p deps
 mkdir -p deps/include
@@ -8,7 +8,7 @@ mkdir -p deps/lib
 
 mkdir -p build && cd build
 
-wget https://download.open-mpi.org/release/hwloc/v2.4/hwloc-${HWLOC_VERSION}.tar.gz -O hwloc-${HWLOC_VERSION}.tar.gz
+wget https://download.open-mpi.org/release/hwloc/v2.5/hwloc-${HWLOC_VERSION}.tar.gz -O hwloc-${HWLOC_VERSION}.tar.gz
 tar -xzf hwloc-${HWLOC_VERSION}.tar.gz
 
 cd hwloc-${HWLOC_VERSION}
diff --git a/scripts/build.openssl.sh b/scripts/build.openssl.sh
index 8263b366c..580557d64 100755
--- a/scripts/build.openssl.sh
+++ b/scripts/build.openssl.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-OPENSSL_VERSION="1.1.1k"
+OPENSSL_VERSION="1.1.1l"
 
 mkdir -p deps
 mkdir -p deps/include
diff --git a/scripts/build.uv.sh b/scripts/build.uv.sh
index 6fb5d9bf9..6179b2d20 100755
--- a/scripts/build.uv.sh
+++ b/scripts/build.uv.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-UV_VERSION="1.41.0"
+UV_VERSION="1.42.0"
 
 mkdir -p deps
 mkdir -p deps/include

From 838996a0fc1dfb54d4d297925fde3b2df01c0166 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sat, 28 Aug 2021 19:53:28 +0700
Subject: [PATCH 15/20] v6.15.0-dev

---
 src/version.h | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/src/version.h b/src/version.h
index e251dff68..f3be8a79b 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,12 +1,6 @@
 /* XMRig
- * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
- * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
- * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
- * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
- * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
- * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
- * Copyright 2018-2021 SChernykh   <https://github.com/SChernykh>
- * Copyright 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ * Copyright (c) 2018-2021 SChernykh   <https://github.com/SChernykh>
+ * Copyright (c) 2016-2021 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -28,15 +22,15 @@
 #define APP_ID        "xmrig"
 #define APP_NAME      "XMRig"
 #define APP_DESC      "XMRig miner"
-#define APP_VERSION   "6.14.2-dev"
+#define APP_VERSION   "6.15.0-dev"
 #define APP_DOMAIN    "xmrig.com"
 #define APP_SITE      "www.xmrig.com"
 #define APP_COPYRIGHT "Copyright (C) 2016-2021 xmrig.com"
 #define APP_KIND      "miner"
 
 #define APP_VER_MAJOR  6
-#define APP_VER_MINOR  14
-#define APP_VER_PATCH  2
+#define APP_VER_MINOR  15
+#define APP_VER_PATCH  0
 
 #ifdef _MSC_VER
 #   if (_MSC_VER >= 1920)

From 123c7ab140120a4e107ee49aa3da1730f21d280a Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sun, 29 Aug 2021 14:22:19 +0700
Subject: [PATCH 16/20] Added support for new CUDA plugin API.

---
 src/backend/cuda/wrappers/CudaLib.cpp | 45 ++++++++++++++++++++-------
 src/base/crypto/Algorithm.cpp         |  5 ---
 src/base/crypto/Algorithm.h           |  2 +-
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/src/backend/cuda/wrappers/CudaLib.cpp b/src/backend/cuda/wrappers/CudaLib.cpp
index 6e947bce5..4848f8c84 100644
--- a/src/backend/cuda/wrappers/CudaLib.cpp
+++ b/src/backend/cuda/wrappers/CudaLib.cpp
@@ -54,6 +54,7 @@ static const char *kAstroBWTHash                        = "astroBWTHash";
 static const char *kAstroBWTPrepare                     = "astroBWTPrepare";
 static const char *kCnHash                              = "cnHash";
 static const char *kDeviceCount                         = "deviceCount";
+static const char *kDeviceInfo                          = "deviceInfo";
 static const char *kDeviceInfo_v2                       = "deviceInfo_v2";
 static const char *kDeviceInit                          = "deviceInit";
 static const char *kDeviceInt                           = "deviceInt";
@@ -61,14 +62,15 @@ static const char *kDeviceName                          = "deviceName";
 static const char *kDeviceUint                          = "deviceUint";
 static const char *kDeviceUlong                         = "deviceUlong";
 static const char *kInit                                = "init";
+static const char *kKawPowHash                          = "kawPowHash";
+static const char *kKawPowPrepare_v2                    = "kawPowPrepare_v2";
+static const char *kKawPowStopHash                      = "kawPowStopHash";
 static const char *kLastError                           = "lastError";
 static const char *kPluginVersion                       = "pluginVersion";
 static const char *kRelease                             = "release";
 static const char *kRxHash                              = "rxHash";
 static const char *kRxPrepare                           = "rxPrepare";
-static const char *kKawPowHash                          = "kawPowHash";
-static const char *kKawPowPrepare_v2                    = "kawPowPrepare_v2";
-static const char *kKawPowStopHash                      = "kawPowStopHash";
+static const char *kSetJob                              = "setJob";
 static const char *kSetJob_v2                           = "setJob_v2";
 static const char *kVersion                             = "version";
 
@@ -78,6 +80,7 @@ using astroBWTHash_t                                    = bool (*)(nvid_ctx *, u
 using astroBWTPrepare_t                                 = bool (*)(nvid_ctx *, uint32_t);
 using cnHash_t                                          = bool (*)(nvid_ctx *, uint32_t, uint64_t, uint64_t, uint32_t *, uint32_t *);
 using deviceCount_t                                     = uint32_t (*)();
+using deviceInfo_t                                      = bool (*)(nvid_ctx *, int32_t, int32_t, uint32_t, int32_t);
 using deviceInfo_v2_t                                   = bool (*)(nvid_ctx *, int32_t, int32_t, const char *, int32_t);
 using deviceInit_t                                      = bool (*)(nvid_ctx *);
 using deviceInt_t                                       = int32_t (*)(nvid_ctx *, CudaLib::DeviceProperty);
@@ -85,14 +88,15 @@ using deviceName_t                                      = const char * (*)(nvid_
 using deviceUint_t                                      = uint32_t (*)(nvid_ctx *, CudaLib::DeviceProperty);
 using deviceUlong_t                                     = uint64_t (*)(nvid_ctx *, CudaLib::DeviceProperty);
 using init_t                                            = void (*)();
+using kawPowHash_t                                      = bool (*)(nvid_ctx *, uint8_t*, uint64_t, uint32_t *, uint32_t *, uint32_t *);
+using kawPowPrepare_v2_t                                = bool (*)(nvid_ctx *, const void *, size_t, const void *, size_t, uint32_t, const uint64_t*);
+using kawPowStopHash_t                                  = bool (*)(nvid_ctx *);
 using lastError_t                                       = const char * (*)(nvid_ctx *);
 using pluginVersion_t                                   = const char * (*)();
 using release_t                                         = void (*)(nvid_ctx *);
 using rxHash_t                                          = bool (*)(nvid_ctx *, uint32_t, uint64_t, uint32_t *, uint32_t *);
 using rxPrepare_t                                       = bool (*)(nvid_ctx *, const void *, size_t, bool, uint32_t);
-using kawPowHash_t                                      = bool (*)(nvid_ctx *, uint8_t*, uint64_t, uint32_t *, uint32_t *, uint32_t *);
-using kawPowPrepare_v2_t                                = bool (*)(nvid_ctx *, const void *, size_t, const void *, size_t, uint32_t, const uint64_t*);
-using kawPowStopHash_t                                  = bool (*)(nvid_ctx *);
+using setJob_t                                          = bool (*)(nvid_ctx *, const void *, size_t, uint32_t);
 using setJob_v2_t                                       = bool (*)(nvid_ctx *, const void *, size_t, const char *);
 using version_t                                         = uint32_t (*)(Version);
 
@@ -102,6 +106,7 @@ static astroBWTHash_t pAstroBWTHash                     = nullptr;
 static astroBWTPrepare_t pAstroBWTPrepare               = nullptr;
 static cnHash_t pCnHash                                 = nullptr;
 static deviceCount_t pDeviceCount                       = nullptr;
+static deviceInfo_t pDeviceInfo                         = nullptr;
 static deviceInfo_v2_t pDeviceInfo_v2                   = nullptr;
 static deviceInit_t pDeviceInit                         = nullptr;
 static deviceInt_t pDeviceInt                           = nullptr;
@@ -109,14 +114,15 @@ static deviceName_t pDeviceName                         = nullptr;
 static deviceUint_t pDeviceUint                         = nullptr;
 static deviceUlong_t pDeviceUlong                       = nullptr;
 static init_t pInit                                     = nullptr;
+static kawPowHash_t pKawPowHash                         = nullptr;
+static kawPowPrepare_v2_t pKawPowPrepare_v2             = nullptr;
+static kawPowStopHash_t pKawPowStopHash                 = nullptr;
 static lastError_t pLastError                           = nullptr;
 static pluginVersion_t pPluginVersion                   = nullptr;
 static release_t pRelease                               = nullptr;
 static rxHash_t pRxHash                                 = nullptr;
 static rxPrepare_t pRxPrepare                           = nullptr;
-static kawPowHash_t pKawPowHash                         = nullptr;
-static kawPowPrepare_v2_t pKawPowPrepare_v2             = nullptr;
-static kawPowStopHash_t pKawPowStopHash                 = nullptr;
+static setJob_t pSetJob                                 = nullptr;
 static setJob_v2_t pSetJob_v2                           = nullptr;
 static version_t pVersion                               = nullptr;
 
@@ -192,6 +198,10 @@ bool xmrig::CudaLib::deviceInfo(nvid_ctx *ctx, int32_t blocks, int32_t threads,
 {
     const Algorithm algo = RxAlgo::id(algorithm);
 
+    if (pDeviceInfo) {
+        return pDeviceInfo(ctx, blocks, threads, algo, dataset_host);
+    }
+
     return pDeviceInfo_v2(ctx, blocks, threads, algo.isValid() ? algo.name() : nullptr, dataset_host);
 }
 
@@ -235,6 +245,9 @@ bool xmrig::CudaLib::kawPowStopHash(nvid_ctx *ctx) noexcept
 bool xmrig::CudaLib::setJob(nvid_ctx *ctx, const void *data, size_t size, const Algorithm &algorithm) noexcept
 {
     const Algorithm algo = RxAlgo::id(algorithm);
+    if (pSetJob) {
+        return pSetJob(ctx, data, size, algo);
+    }
 
     return pSetJob_v2(ctx, data, size, algo.name());
 }
@@ -378,7 +391,8 @@ void xmrig::CudaLib::load()
 {
     DLSYM(Version);
 
-    if (pVersion(ApiVersion) != 3U) {
+    const uint32_t api = pVersion(ApiVersion);
+    if (api < 3U || api > 4U) {
         throw std::runtime_error("API version mismatch");
     }
 
@@ -401,8 +415,15 @@ void xmrig::CudaLib::load()
     DLSYM(KawPowHash);
     DLSYM(KawPowPrepare_v2);
     DLSYM(KawPowStopHash);
-    DLSYM(DeviceInfo_v2);
-    DLSYM(SetJob_v2);
+
+    if (api == 4U) {
+        DLSYM(DeviceInfo);
+        DLSYM(SetJob);
+    }
+    else if (api == 3U) {
+        DLSYM(DeviceInfo_v2);
+        DLSYM(SetJob_v2);
+    }
 
     pInit();
 }
diff --git a/src/base/crypto/Algorithm.cpp b/src/base/crypto/Algorithm.cpp
index dd2df673d..8c3a1ad36 100644
--- a/src/base/crypto/Algorithm.cpp
+++ b/src/base/crypto/Algorithm.cpp
@@ -107,11 +107,6 @@ const char *Algorithm::kKAWPOW_RVN      = "kawpow";
 #define ALGO_ALIAS_AUTO(ALGO)   { Algorithm::k##ALGO, Algorithm::ALGO }
 
 
-#ifdef _MSC_VER
-#   define strcasecmp _stricmp
-#endif
-
-
 static const std::map<uint32_t, const char *> kAlgorithmNames = {
     ALGO_NAME(CN_0),
     ALGO_NAME(CN_1),
diff --git a/src/base/crypto/Algorithm.h b/src/base/crypto/Algorithm.h
index 15df2a2c0..ca1588979 100644
--- a/src/base/crypto/Algorithm.h
+++ b/src/base/crypto/Algorithm.h
@@ -166,7 +166,7 @@ public:
     static inline constexpr bool isCN(Id id)                { return (id & 0xff000000) == CN_ANY; }
     static inline constexpr Id base(Id id)                  { return isCN(id) ? static_cast<Id>(CN_0 | (id & 0xff00)) : INVALID; }
     static inline constexpr size_t l2(Id id)                { return family(id) == RANDOM_X ? (1U << ((id >> 8) & 0xff)) : 0U; }
-    static inline constexpr size_t l3(Id id)                { return 1U << ((id >> 16) & 0xff); }
+    static inline constexpr size_t l3(Id id)                { return 1ULL << ((id >> 16) & 0xff); }
     static inline constexpr uint32_t family(Id id)          { return id & (isCN(id) ? 0xffff0000 : 0xff000000); }
 
     inline bool isCN() const                                { return isCN(m_id); }

From 3dc192f63e2746b4ca3df362358cbee657e2afdc Mon Sep 17 00:00:00 2001
From: SChernykh <sergey.v.chernykh@gmail.com>
Date: Sun, 29 Aug 2021 10:35:43 +0200
Subject: [PATCH 17/20] AstroBWT: add AVX2 Salsa20 implementation

+4.5% speedup on Ryzen 5 5600X
---
 cmake/astrobwt.cmake                          |   1 +
 src/crypto/astrobwt/AstroBWT.cpp              |  31 +-
 .../astrobwt/xmm6int/salsa20_xmm6int-avx2.c   | 105 ++++
 src/crypto/astrobwt/xmm6int/u0.h              | 193 ++++++
 src/crypto/astrobwt/xmm6int/u1.h              | 207 +++++++
 src/crypto/astrobwt/xmm6int/u4.h              | 547 ++++++++++++++++++
 src/crypto/astrobwt/xmm6int/u8.h              | 477 +++++++++++++++
 7 files changed, 1556 insertions(+), 5 deletions(-)
 create mode 100644 src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c
 create mode 100644 src/crypto/astrobwt/xmm6int/u0.h
 create mode 100644 src/crypto/astrobwt/xmm6int/u1.h
 create mode 100644 src/crypto/astrobwt/xmm6int/u4.h
 create mode 100644 src/crypto/astrobwt/xmm6int/u8.h

diff --git a/cmake/astrobwt.cmake b/cmake/astrobwt.cmake
index 064857792..6f48d13e7 100644
--- a/cmake/astrobwt.cmake
+++ b/cmake/astrobwt.cmake
@@ -23,6 +23,7 @@ if (WITH_ASTROBWT)
     else()
         if (CMAKE_SIZEOF_VOID_P EQUAL 8)
             add_definitions(/DASTROBWT_AVX2)
+            list(APPEND SOURCES_CRYPTO src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c)
             if (CMAKE_C_COMPILER_ID MATCHES MSVC)
                 enable_language(ASM_MASM)
                 list(APPEND SOURCES_CRYPTO src/crypto/astrobwt/sha3_256_avx2.asm)
diff --git a/src/crypto/astrobwt/AstroBWT.cpp b/src/crypto/astrobwt/AstroBWT.cpp
index 26b802251..5dee11657 100644
--- a/src/crypto/astrobwt/AstroBWT.cpp
+++ b/src/crypto/astrobwt/AstroBWT.cpp
@@ -70,7 +70,17 @@ static void Salsa20_XORKeyStream(const void* key, void* output, size_t size)
 {
 	const uint64_t iv = 0;
 	ZeroTier::Salsa20 s(key, &iv);
-	s.XORKeyStream(output, size);
+	s.XORKeyStream(output, static_cast<uint32_t>(size));
+	memset(static_cast<uint8_t*>(output) - 16, 0, 16);
+	memset(static_cast<uint8_t*>(output) + size, 0, 16);
+}
+
+extern "C" int salsa20_stream_avx2(void* c, uint64_t clen, const void* iv, const void* key);
+
+static void Salsa20_XORKeyStream_AVX256(const void* key, void* output, size_t size)
+{
+	const uint64_t iv = 0;
+	salsa20_stream_avx2(output, size, &iv, key);
 	memset(static_cast<uint8_t*>(output) - 16, 0, 16);
 	memset(static_cast<uint8_t*>(output) + size, 0, 16);
 }
@@ -167,13 +177,16 @@ bool xmrig::astrobwt::astrobwt_dero(const void* input_data, uint32_t input_size,
 	uint8_t* stage2_result = (uint8_t*)(tmp_indices);
 
 #ifdef ASTROBWT_AVX2
-	if (hasAVX2 && avx2)
+	if (hasAVX2 && avx2) {
 		SHA3_256_AVX2_ASM(input_data, input_size, key);
+		Salsa20_XORKeyStream_AVX256(key, stage1_output, STAGE1_SIZE);
+	}
 	else
 #endif
+	{
 		sha3_HashBuffer(256, SHA3_FLAGS_NONE, input_data, input_size, key, sizeof(key));
-
-	Salsa20_XORKeyStream(key, stage1_output, STAGE1_SIZE);
+		Salsa20_XORKeyStream(key, stage1_output, STAGE1_SIZE);
+	}
 
 	sort_indices(STAGE1_SIZE + 1, stage1_output, indices, tmp_indices);
 
@@ -196,7 +209,15 @@ bool xmrig::astrobwt::astrobwt_dero(const void* input_data, uint32_t input_size,
 		return false;
 	}
 
-	Salsa20_XORKeyStream(key, stage2_output, stage2_size);
+#ifdef ASTROBWT_AVX2
+	if (hasAVX2 && avx2) {
+		Salsa20_XORKeyStream_AVX256(key, stage2_output, stage2_size);
+	}
+	else
+#endif
+	{
+		Salsa20_XORKeyStream(key, stage2_output, stage2_size);
+	}
 
 	sort_indices(stage2_size + 1, stage2_output, indices, tmp_indices);
 
diff --git a/src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c b/src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c
new file mode 100644
index 000000000..fe2047d41
--- /dev/null
+++ b/src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c
@@ -0,0 +1,105 @@
+/*
+ * ISC License
+ *
+ * Copyright (c) 2013-2021
+ * Frank Denis <j at pureftpd dot org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __GNUC__
+#pragma GCC target("sse2")
+#pragma GCC target("ssse3")
+#pragma GCC target("sse4.1")
+#pragma GCC target("avx2")
+#endif
+
+#include <emmintrin.h>
+#include <immintrin.h>
+#include <smmintrin.h>
+#include <tmmintrin.h>
+
+#define ROUNDS 20
+
+typedef struct salsa_ctx {
+    uint32_t input[16];
+} salsa_ctx;
+
+static const int TR[16] = {
+    0, 5, 10, 15, 12, 1, 6, 11, 8, 13, 2, 7, 4, 9, 14, 3
+};
+
+#define LOAD32_LE(p) *((uint32_t*)(p))
+#define STORE32_LE(dst, src) memcpy((dst), &(src), sizeof(uint32_t))
+
+static void
+salsa_keysetup(salsa_ctx *ctx, const uint8_t *k)
+{
+    ctx->input[TR[1]]  = LOAD32_LE(k + 0);
+    ctx->input[TR[2]]  = LOAD32_LE(k + 4);
+    ctx->input[TR[3]]  = LOAD32_LE(k + 8);
+    ctx->input[TR[4]]  = LOAD32_LE(k + 12);
+    ctx->input[TR[11]] = LOAD32_LE(k + 16);
+    ctx->input[TR[12]] = LOAD32_LE(k + 20);
+    ctx->input[TR[13]] = LOAD32_LE(k + 24);
+    ctx->input[TR[14]] = LOAD32_LE(k + 28);
+    ctx->input[TR[0]]  = 0x61707865;
+    ctx->input[TR[5]]  = 0x3320646e;
+    ctx->input[TR[10]] = 0x79622d32;
+    ctx->input[TR[15]] = 0x6b206574;
+}
+
+static void
+salsa_ivsetup(salsa_ctx *ctx, const uint8_t *iv, const uint8_t *counter)
+{
+    ctx->input[TR[6]] = LOAD32_LE(iv + 0);
+    ctx->input[TR[7]] = LOAD32_LE(iv + 4);
+    ctx->input[TR[8]] = counter == NULL ? 0 : LOAD32_LE(counter + 0);
+    ctx->input[TR[9]] = counter == NULL ? 0 : LOAD32_LE(counter + 4);
+}
+
+static void
+salsa20_encrypt_bytes(salsa_ctx *ctx, const uint8_t *m, uint8_t *c,
+                      unsigned long long bytes)
+{
+    uint32_t * const x = &ctx->input[0];
+
+    if (!bytes) {
+        return; /* LCOV_EXCL_LINE */
+    }
+
+#include "u8.h"
+#include "u4.h"
+#include "u1.h"
+#include "u0.h"
+}
+
+int salsa20_stream_avx2(void* c, uint64_t clen, const void* iv, const void* key)
+{
+	struct salsa_ctx ctx;
+
+	if (!clen) {
+		return 0;
+	}
+
+	salsa_keysetup(&ctx, (const uint8_t*)key);
+	salsa_ivsetup(&ctx, (const uint8_t*)iv, NULL);
+	memset(c, 0, clen);
+	salsa20_encrypt_bytes(&ctx, (const uint8_t*)c, (uint8_t*)c, clen);
+
+	return 0;
+}
diff --git a/src/crypto/astrobwt/xmm6int/u0.h b/src/crypto/astrobwt/xmm6int/u0.h
new file mode 100644
index 000000000..ab93f7425
--- /dev/null
+++ b/src/crypto/astrobwt/xmm6int/u0.h
@@ -0,0 +1,193 @@
+if (bytes > 0) {
+    __m128i diag0 = _mm_loadu_si128((const __m128i *) (x + 0));
+    __m128i diag1 = _mm_loadu_si128((const __m128i *) (x + 4));
+    __m128i diag2 = _mm_loadu_si128((const __m128i *) (x + 8));
+    __m128i diag3 = _mm_loadu_si128((const __m128i *) (x + 12));
+    __m128i a0, a1, a2, a3, a4, a5, a6, a7;
+    __m128i b0, b1, b2, b3, b4, b5, b6, b7;
+    uint8_t partialblock[64];
+
+    unsigned int i;
+
+    a0 = diag1;
+    for (i = 0; i < ROUNDS; i += 4) {
+        a0    = _mm_add_epi32(a0, diag0);
+        a1    = diag0;
+        b0    = a0;
+        a0    = _mm_slli_epi32(a0, 7);
+        b0    = _mm_srli_epi32(b0, 25);
+        diag3 = _mm_xor_si128(diag3, a0);
+
+        diag3 = _mm_xor_si128(diag3, b0);
+
+        a1    = _mm_add_epi32(a1, diag3);
+        a2    = diag3;
+        b1    = a1;
+        a1    = _mm_slli_epi32(a1, 9);
+        b1    = _mm_srli_epi32(b1, 23);
+        diag2 = _mm_xor_si128(diag2, a1);
+        diag3 = _mm_shuffle_epi32(diag3, 0x93);
+        diag2 = _mm_xor_si128(diag2, b1);
+
+        a2    = _mm_add_epi32(a2, diag2);
+        a3    = diag2;
+        b2    = a2;
+        a2    = _mm_slli_epi32(a2, 13);
+        b2    = _mm_srli_epi32(b2, 19);
+        diag1 = _mm_xor_si128(diag1, a2);
+        diag2 = _mm_shuffle_epi32(diag2, 0x4e);
+        diag1 = _mm_xor_si128(diag1, b2);
+
+        a3    = _mm_add_epi32(a3, diag1);
+        a4    = diag3;
+        b3    = a3;
+        a3    = _mm_slli_epi32(a3, 18);
+        b3    = _mm_srli_epi32(b3, 14);
+        diag0 = _mm_xor_si128(diag0, a3);
+        diag1 = _mm_shuffle_epi32(diag1, 0x39);
+        diag0 = _mm_xor_si128(diag0, b3);
+
+        a4    = _mm_add_epi32(a4, diag0);
+        a5    = diag0;
+        b4    = a4;
+        a4    = _mm_slli_epi32(a4, 7);
+        b4    = _mm_srli_epi32(b4, 25);
+        diag1 = _mm_xor_si128(diag1, a4);
+
+        diag1 = _mm_xor_si128(diag1, b4);
+
+        a5    = _mm_add_epi32(a5, diag1);
+        a6    = diag1;
+        b5    = a5;
+        a5    = _mm_slli_epi32(a5, 9);
+        b5    = _mm_srli_epi32(b5, 23);
+        diag2 = _mm_xor_si128(diag2, a5);
+        diag1 = _mm_shuffle_epi32(diag1, 0x93);
+        diag2 = _mm_xor_si128(diag2, b5);
+
+        a6    = _mm_add_epi32(a6, diag2);
+        a7    = diag2;
+        b6    = a6;
+        a6    = _mm_slli_epi32(a6, 13);
+        b6    = _mm_srli_epi32(b6, 19);
+        diag3 = _mm_xor_si128(diag3, a6);
+        diag2 = _mm_shuffle_epi32(diag2, 0x4e);
+        diag3 = _mm_xor_si128(diag3, b6);
+
+        a7    = _mm_add_epi32(a7, diag3);
+        a0    = diag1;
+        b7    = a7;
+        a7    = _mm_slli_epi32(a7, 18);
+        b7    = _mm_srli_epi32(b7, 14);
+        diag0 = _mm_xor_si128(diag0, a7);
+        diag3 = _mm_shuffle_epi32(diag3, 0x39);
+        diag0 = _mm_xor_si128(diag0, b7);
+
+        a0    = _mm_add_epi32(a0, diag0);
+        a1    = diag0;
+        b0    = a0;
+        a0    = _mm_slli_epi32(a0, 7);
+        b0    = _mm_srli_epi32(b0, 25);
+        diag3 = _mm_xor_si128(diag3, a0);
+
+        diag3 = _mm_xor_si128(diag3, b0);
+
+        a1    = _mm_add_epi32(a1, diag3);
+        a2    = diag3;
+        b1    = a1;
+        a1    = _mm_slli_epi32(a1, 9);
+        b1    = _mm_srli_epi32(b1, 23);
+        diag2 = _mm_xor_si128(diag2, a1);
+        diag3 = _mm_shuffle_epi32(diag3, 0x93);
+        diag2 = _mm_xor_si128(diag2, b1);
+
+        a2    = _mm_add_epi32(a2, diag2);
+        a3    = diag2;
+        b2    = a2;
+        a2    = _mm_slli_epi32(a2, 13);
+        b2    = _mm_srli_epi32(b2, 19);
+        diag1 = _mm_xor_si128(diag1, a2);
+        diag2 = _mm_shuffle_epi32(diag2, 0x4e);
+        diag1 = _mm_xor_si128(diag1, b2);
+
+        a3    = _mm_add_epi32(a3, diag1);
+        a4    = diag3;
+        b3    = a3;
+        a3    = _mm_slli_epi32(a3, 18);
+        b3    = _mm_srli_epi32(b3, 14);
+        diag0 = _mm_xor_si128(diag0, a3);
+        diag1 = _mm_shuffle_epi32(diag1, 0x39);
+        diag0 = _mm_xor_si128(diag0, b3);
+
+        a4    = _mm_add_epi32(a4, diag0);
+        a5    = diag0;
+        b4    = a4;
+        a4    = _mm_slli_epi32(a4, 7);
+        b4    = _mm_srli_epi32(b4, 25);
+        diag1 = _mm_xor_si128(diag1, a4);
+
+        diag1 = _mm_xor_si128(diag1, b4);
+
+        a5    = _mm_add_epi32(a5, diag1);
+        a6    = diag1;
+        b5    = a5;
+        a5    = _mm_slli_epi32(a5, 9);
+        b5    = _mm_srli_epi32(b5, 23);
+        diag2 = _mm_xor_si128(diag2, a5);
+        diag1 = _mm_shuffle_epi32(diag1, 0x93);
+        diag2 = _mm_xor_si128(diag2, b5);
+
+        a6    = _mm_add_epi32(a6, diag2);
+        a7    = diag2;
+        b6    = a6;
+        a6    = _mm_slli_epi32(a6, 13);
+        b6    = _mm_srli_epi32(b6, 19);
+        diag3 = _mm_xor_si128(diag3, a6);
+        diag2 = _mm_shuffle_epi32(diag2, 0x4e);
+        diag3 = _mm_xor_si128(diag3, b6);
+
+        a7    = _mm_add_epi32(a7, diag3);
+        a0    = diag1;
+        b7    = a7;
+        a7    = _mm_slli_epi32(a7, 18);
+        b7    = _mm_srli_epi32(b7, 14);
+        diag0 = _mm_xor_si128(diag0, a7);
+        diag3 = _mm_shuffle_epi32(diag3, 0x39);
+        diag0 = _mm_xor_si128(diag0, b7);
+    }
+
+    diag0 = _mm_add_epi32(diag0, _mm_loadu_si128((const __m128i *) (x + 0)));
+    diag1 = _mm_add_epi32(diag1, _mm_loadu_si128((const __m128i *) (x + 4)));
+    diag2 = _mm_add_epi32(diag2, _mm_loadu_si128((const __m128i *) (x + 8)));
+    diag3 = _mm_add_epi32(diag3, _mm_loadu_si128((const __m128i *) (x + 12)));
+
+#define ONEQUAD_SHUFFLE(A, B, C, D)                                              \
+    do {                                                                         \
+        uint32_t in##A                         = _mm_cvtsi128_si32(diag0);       \
+        uint32_t in##B                         = _mm_cvtsi128_si32(diag1);       \
+        uint32_t in##C                         = _mm_cvtsi128_si32(diag2);       \
+        uint32_t in##D                         = _mm_cvtsi128_si32(diag3);       \
+        diag0                                  = _mm_shuffle_epi32(diag0, 0x39); \
+        diag1                                  = _mm_shuffle_epi32(diag1, 0x39); \
+        diag2                                  = _mm_shuffle_epi32(diag2, 0x39); \
+        diag3                                  = _mm_shuffle_epi32(diag3, 0x39); \
+        *(uint32_t *) (partialblock + (A * 4)) = in##A;                          \
+        *(uint32_t *) (partialblock + (B * 4)) = in##B;                          \
+        *(uint32_t *) (partialblock + (C * 4)) = in##C;                          \
+        *(uint32_t *) (partialblock + (D * 4)) = in##D;                          \
+    } while (0)
+
+#define ONEQUAD(A, B, C, D) ONEQUAD_SHUFFLE(A, B, C, D)
+
+    ONEQUAD(0, 12, 8, 4);
+    ONEQUAD(5, 1, 13, 9);
+    ONEQUAD(10, 6, 2, 14);
+    ONEQUAD(15, 11, 7, 3);
+
+#undef ONEQUAD
+#undef ONEQUAD_SHUFFLE
+
+    for (i = 0; i < bytes; i++) {
+        c[i] = m[i] ^ partialblock[i];
+    }
+}
diff --git a/src/crypto/astrobwt/xmm6int/u1.h b/src/crypto/astrobwt/xmm6int/u1.h
new file mode 100644
index 000000000..e82521cd5
--- /dev/null
+++ b/src/crypto/astrobwt/xmm6int/u1.h
@@ -0,0 +1,207 @@
+while (bytes >= 64) {
+    __m128i diag0 = _mm_loadu_si128((const __m128i *) (x + 0));
+    __m128i diag1 = _mm_loadu_si128((const __m128i *) (x + 4));
+    __m128i diag2 = _mm_loadu_si128((const __m128i *) (x + 8));
+    __m128i diag3 = _mm_loadu_si128((const __m128i *) (x + 12));
+    __m128i a0, a1, a2, a3, a4, a5, a6, a7;
+    __m128i b0, b1, b2, b3, b4, b5, b6, b7;
+
+    uint32_t in8;
+    uint32_t in9;
+    int      i;
+
+    a0 = diag1;
+    for (i = 0; i < ROUNDS; i += 4) {
+        a0    = _mm_add_epi32(a0, diag0);
+        a1    = diag0;
+        b0    = a0;
+        a0    = _mm_slli_epi32(a0, 7);
+        b0    = _mm_srli_epi32(b0, 25);
+        diag3 = _mm_xor_si128(diag3, a0);
+
+        diag3 = _mm_xor_si128(diag3, b0);
+
+        a1    = _mm_add_epi32(a1, diag3);
+        a2    = diag3;
+        b1    = a1;
+        a1    = _mm_slli_epi32(a1, 9);
+        b1    = _mm_srli_epi32(b1, 23);
+        diag2 = _mm_xor_si128(diag2, a1);
+        diag3 = _mm_shuffle_epi32(diag3, 0x93);
+        diag2 = _mm_xor_si128(diag2, b1);
+
+        a2    = _mm_add_epi32(a2, diag2);
+        a3    = diag2;
+        b2    = a2;
+        a2    = _mm_slli_epi32(a2, 13);
+        b2    = _mm_srli_epi32(b2, 19);
+        diag1 = _mm_xor_si128(diag1, a2);
+        diag2 = _mm_shuffle_epi32(diag2, 0x4e);
+        diag1 = _mm_xor_si128(diag1, b2);
+
+        a3    = _mm_add_epi32(a3, diag1);
+        a4    = diag3;
+        b3    = a3;
+        a3    = _mm_slli_epi32(a3, 18);
+        b3    = _mm_srli_epi32(b3, 14);
+        diag0 = _mm_xor_si128(diag0, a3);
+        diag1 = _mm_shuffle_epi32(diag1, 0x39);
+        diag0 = _mm_xor_si128(diag0, b3);
+
+        a4    = _mm_add_epi32(a4, diag0);
+        a5    = diag0;
+        b4    = a4;
+        a4    = _mm_slli_epi32(a4, 7);
+        b4    = _mm_srli_epi32(b4, 25);
+        diag1 = _mm_xor_si128(diag1, a4);
+
+        diag1 = _mm_xor_si128(diag1, b4);
+
+        a5    = _mm_add_epi32(a5, diag1);
+        a6    = diag1;
+        b5    = a5;
+        a5    = _mm_slli_epi32(a5, 9);
+        b5    = _mm_srli_epi32(b5, 23);
+        diag2 = _mm_xor_si128(diag2, a5);
+        diag1 = _mm_shuffle_epi32(diag1, 0x93);
+        diag2 = _mm_xor_si128(diag2, b5);
+
+        a6    = _mm_add_epi32(a6, diag2);
+        a7    = diag2;
+        b6    = a6;
+        a6    = _mm_slli_epi32(a6, 13);
+        b6    = _mm_srli_epi32(b6, 19);
+        diag3 = _mm_xor_si128(diag3, a6);
+        diag2 = _mm_shuffle_epi32(diag2, 0x4e);
+        diag3 = _mm_xor_si128(diag3, b6);
+
+        a7    = _mm_add_epi32(a7, diag3);
+        a0    = diag1;
+        b7    = a7;
+        a7    = _mm_slli_epi32(a7, 18);
+        b7    = _mm_srli_epi32(b7, 14);
+        diag0 = _mm_xor_si128(diag0, a7);
+        diag3 = _mm_shuffle_epi32(diag3, 0x39);
+        diag0 = _mm_xor_si128(diag0, b7);
+
+        a0    = _mm_add_epi32(a0, diag0);
+        a1    = diag0;
+        b0    = a0;
+        a0    = _mm_slli_epi32(a0, 7);
+        b0    = _mm_srli_epi32(b0, 25);
+        diag3 = _mm_xor_si128(diag3, a0);
+
+        diag3 = _mm_xor_si128(diag3, b0);
+
+        a1    = _mm_add_epi32(a1, diag3);
+        a2    = diag3;
+        b1    = a1;
+        a1    = _mm_slli_epi32(a1, 9);
+        b1    = _mm_srli_epi32(b1, 23);
+        diag2 = _mm_xor_si128(diag2, a1);
+        diag3 = _mm_shuffle_epi32(diag3, 0x93);
+        diag2 = _mm_xor_si128(diag2, b1);
+
+        a2    = _mm_add_epi32(a2, diag2);
+        a3    = diag2;
+        b2    = a2;
+        a2    = _mm_slli_epi32(a2, 13);
+        b2    = _mm_srli_epi32(b2, 19);
+        diag1 = _mm_xor_si128(diag1, a2);
+        diag2 = _mm_shuffle_epi32(diag2, 0x4e);
+        diag1 = _mm_xor_si128(diag1, b2);
+
+        a3    = _mm_add_epi32(a3, diag1);
+        a4    = diag3;
+        b3    = a3;
+        a3    = _mm_slli_epi32(a3, 18);
+        b3    = _mm_srli_epi32(b3, 14);
+        diag0 = _mm_xor_si128(diag0, a3);
+        diag1 = _mm_shuffle_epi32(diag1, 0x39);
+        diag0 = _mm_xor_si128(diag0, b3);
+
+        a4    = _mm_add_epi32(a4, diag0);
+        a5    = diag0;
+        b4    = a4;
+        a4    = _mm_slli_epi32(a4, 7);
+        b4    = _mm_srli_epi32(b4, 25);
+        diag1 = _mm_xor_si128(diag1, a4);
+
+        diag1 = _mm_xor_si128(diag1, b4);
+
+        a5    = _mm_add_epi32(a5, diag1);
+        a6    = diag1;
+        b5    = a5;
+        a5    = _mm_slli_epi32(a5, 9);
+        b5    = _mm_srli_epi32(b5, 23);
+        diag2 = _mm_xor_si128(diag2, a5);
+        diag1 = _mm_shuffle_epi32(diag1, 0x93);
+        diag2 = _mm_xor_si128(diag2, b5);
+
+        a6    = _mm_add_epi32(a6, diag2);
+        a7    = diag2;
+        b6    = a6;
+        a6    = _mm_slli_epi32(a6, 13);
+        b6    = _mm_srli_epi32(b6, 19);
+        diag3 = _mm_xor_si128(diag3, a6);
+        diag2 = _mm_shuffle_epi32(diag2, 0x4e);
+        diag3 = _mm_xor_si128(diag3, b6);
+
+        a7    = _mm_add_epi32(a7, diag3);
+        a0    = diag1;
+        b7    = a7;
+        a7    = _mm_slli_epi32(a7, 18);
+        b7    = _mm_srli_epi32(b7, 14);
+        diag0 = _mm_xor_si128(diag0, a7);
+        diag3 = _mm_shuffle_epi32(diag3, 0x39);
+        diag0 = _mm_xor_si128(diag0, b7);
+    }
+
+    diag0 = _mm_add_epi32(diag0, _mm_loadu_si128((const __m128i *) (x + 0)));
+    diag1 = _mm_add_epi32(diag1, _mm_loadu_si128((const __m128i *) (x + 4)));
+    diag2 = _mm_add_epi32(diag2, _mm_loadu_si128((const __m128i *) (x + 8)));
+    diag3 = _mm_add_epi32(diag3, _mm_loadu_si128((const __m128i *) (x + 12)));
+
+#define ONEQUAD_SHUFFLE(A, B, C, D)                      \
+    do {                                                 \
+        uint32_t in##A = _mm_cvtsi128_si32(diag0);       \
+        uint32_t in##B = _mm_cvtsi128_si32(diag1);       \
+        uint32_t in##C = _mm_cvtsi128_si32(diag2);       \
+        uint32_t in##D = _mm_cvtsi128_si32(diag3);       \
+        diag0          = _mm_shuffle_epi32(diag0, 0x39); \
+        diag1          = _mm_shuffle_epi32(diag1, 0x39); \
+        diag2          = _mm_shuffle_epi32(diag2, 0x39); \
+        diag3          = _mm_shuffle_epi32(diag3, 0x39); \
+        in##A ^= *(const uint32_t *) (m + (A * 4));      \
+        in##B ^= *(const uint32_t *) (m + (B * 4));      \
+        in##C ^= *(const uint32_t *) (m + (C * 4));      \
+        in##D ^= *(const uint32_t *) (m + (D * 4));      \
+        *(uint32_t *) (c + (A * 4)) = in##A;             \
+        *(uint32_t *) (c + (B * 4)) = in##B;             \
+        *(uint32_t *) (c + (C * 4)) = in##C;             \
+        *(uint32_t *) (c + (D * 4)) = in##D;             \
+    } while (0)
+
+#define ONEQUAD(A, B, C, D) ONEQUAD_SHUFFLE(A, B, C, D)
+
+    ONEQUAD(0, 12, 8, 4);
+    ONEQUAD(5, 1, 13, 9);
+    ONEQUAD(10, 6, 2, 14);
+    ONEQUAD(15, 11, 7, 3);
+
+#undef ONEQUAD
+#undef ONEQUAD_SHUFFLE
+
+    in8 = x[8];
+    in9 = x[13];
+    in8++;
+    if (in8 == 0) {
+        in9++;
+    }
+    x[8]  = in8;
+    x[13] = in9;
+
+    c += 64;
+    m += 64;
+    bytes -= 64;
+}
diff --git a/src/crypto/astrobwt/xmm6int/u4.h b/src/crypto/astrobwt/xmm6int/u4.h
new file mode 100644
index 000000000..474f48600
--- /dev/null
+++ b/src/crypto/astrobwt/xmm6int/u4.h
@@ -0,0 +1,547 @@
+if (bytes >= 256) {
+    __m128i y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13, y14,
+        y15;
+    __m128i z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, z10, z11, z12, z13, z14,
+        z15;
+    __m128i orig0, orig1, orig2, orig3, orig4, orig5, orig6, orig7, orig8,
+        orig9, orig10, orig11, orig12, orig13, orig14, orig15;
+
+    uint32_t in8;
+    uint32_t in9;
+    int      i;
+
+    /* element broadcast immediate for _mm_shuffle_epi32 are in order:
+       0x00, 0x55, 0xaa, 0xff */
+    z0  = _mm_loadu_si128((const __m128i *) (x + 0));
+    z5  = _mm_shuffle_epi32(z0, 0x55);
+    z10 = _mm_shuffle_epi32(z0, 0xaa);
+    z15 = _mm_shuffle_epi32(z0, 0xff);
+    z0  = _mm_shuffle_epi32(z0, 0x00);
+    z1  = _mm_loadu_si128((const __m128i *) (x + 4));
+    z6  = _mm_shuffle_epi32(z1, 0xaa);
+    z11 = _mm_shuffle_epi32(z1, 0xff);
+    z12 = _mm_shuffle_epi32(z1, 0x00);
+    z1  = _mm_shuffle_epi32(z1, 0x55);
+    z2  = _mm_loadu_si128((const __m128i *) (x + 8));
+    z7  = _mm_shuffle_epi32(z2, 0xff);
+    z13 = _mm_shuffle_epi32(z2, 0x55);
+    z2  = _mm_shuffle_epi32(z2, 0xaa);
+    /* no z8 -> first half of the nonce, will fill later */
+    z3  = _mm_loadu_si128((const __m128i *) (x + 12));
+    z4  = _mm_shuffle_epi32(z3, 0x00);
+    z14 = _mm_shuffle_epi32(z3, 0xaa);
+    z3  = _mm_shuffle_epi32(z3, 0xff);
+    /* no z9 -> second half of the nonce, will fill later */
+    orig0  = z0;
+    orig1  = z1;
+    orig2  = z2;
+    orig3  = z3;
+    orig4  = z4;
+    orig5  = z5;
+    orig6  = z6;
+    orig7  = z7;
+    orig10 = z10;
+    orig11 = z11;
+    orig12 = z12;
+    orig13 = z13;
+    orig14 = z14;
+    orig15 = z15;
+
+    while (bytes >= 256) {
+        /* vector implementation for z8 and z9 */
+        /* not sure if it helps for only 4 blocks */
+        const __m128i addv8 = _mm_set_epi64x(1, 0);
+        const __m128i addv9 = _mm_set_epi64x(3, 2);
+        __m128i       t8, t9;
+        uint64_t      in89;
+
+        in8  = x[8];
+        in9  = x[13];
+        in89 = ((uint64_t) in8) | (((uint64_t) in9) << 32);
+        t8   = _mm_set1_epi64x(in89);
+        t9   = _mm_set1_epi64x(in89);
+
+        z8 = _mm_add_epi64(addv8, t8);
+        z9 = _mm_add_epi64(addv9, t9);
+
+        t8 = _mm_unpacklo_epi32(z8, z9);
+        t9 = _mm_unpackhi_epi32(z8, z9);
+
+        z8 = _mm_unpacklo_epi32(t8, t9);
+        z9 = _mm_unpackhi_epi32(t8, t9);
+
+        orig8 = z8;
+        orig9 = z9;
+
+        in89 += 4;
+
+        x[8]  = in89 & 0xFFFFFFFF;
+        x[13] = (in89 >> 32) & 0xFFFFFFFF;
+
+        z5  = orig5;
+        z10 = orig10;
+        z15 = orig15;
+        z14 = orig14;
+        z3  = orig3;
+        z6  = orig6;
+        z11 = orig11;
+        z1  = orig1;
+
+        z7  = orig7;
+        z13 = orig13;
+        z2  = orig2;
+        z9  = orig9;
+        z0  = orig0;
+        z12 = orig12;
+        z4  = orig4;
+        z8  = orig8;
+
+        for (i = 0; i < ROUNDS; i += 2) {
+            /* the inner loop is a direct translation (regexp search/replace)
+             * from the amd64-xmm6 ASM */
+            __m128i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13,
+                r14, r15;
+
+            y4 = z12;
+            y4 = _mm_add_epi32(y4, z0);
+            r4 = y4;
+            y4 = _mm_slli_epi32(y4, 7);
+            z4 = _mm_xor_si128(z4, y4);
+            r4 = _mm_srli_epi32(r4, 25);
+            z4 = _mm_xor_si128(z4, r4);
+
+            y9 = z1;
+            y9 = _mm_add_epi32(y9, z5);
+            r9 = y9;
+            y9 = _mm_slli_epi32(y9, 7);
+            z9 = _mm_xor_si128(z9, y9);
+            r9 = _mm_srli_epi32(r9, 25);
+            z9 = _mm_xor_si128(z9, r9);
+
+            y8 = z0;
+            y8 = _mm_add_epi32(y8, z4);
+            r8 = y8;
+            y8 = _mm_slli_epi32(y8, 9);
+            z8 = _mm_xor_si128(z8, y8);
+            r8 = _mm_srli_epi32(r8, 23);
+            z8 = _mm_xor_si128(z8, r8);
+
+            y13 = z5;
+            y13 = _mm_add_epi32(y13, z9);
+            r13 = y13;
+            y13 = _mm_slli_epi32(y13, 9);
+            z13 = _mm_xor_si128(z13, y13);
+            r13 = _mm_srli_epi32(r13, 23);
+            z13 = _mm_xor_si128(z13, r13);
+
+            y12 = z4;
+            y12 = _mm_add_epi32(y12, z8);
+            r12 = y12;
+            y12 = _mm_slli_epi32(y12, 13);
+            z12 = _mm_xor_si128(z12, y12);
+            r12 = _mm_srli_epi32(r12, 19);
+            z12 = _mm_xor_si128(z12, r12);
+
+            y1 = z9;
+            y1 = _mm_add_epi32(y1, z13);
+            r1 = y1;
+            y1 = _mm_slli_epi32(y1, 13);
+            z1 = _mm_xor_si128(z1, y1);
+            r1 = _mm_srli_epi32(r1, 19);
+            z1 = _mm_xor_si128(z1, r1);
+
+            y0 = z8;
+            y0 = _mm_add_epi32(y0, z12);
+            r0 = y0;
+            y0 = _mm_slli_epi32(y0, 18);
+            z0 = _mm_xor_si128(z0, y0);
+            r0 = _mm_srli_epi32(r0, 14);
+            z0 = _mm_xor_si128(z0, r0);
+
+            y5 = z13;
+            y5 = _mm_add_epi32(y5, z1);
+            r5 = y5;
+            y5 = _mm_slli_epi32(y5, 18);
+            z5 = _mm_xor_si128(z5, y5);
+            r5 = _mm_srli_epi32(r5, 14);
+            z5 = _mm_xor_si128(z5, r5);
+
+            y14 = z6;
+            y14 = _mm_add_epi32(y14, z10);
+            r14 = y14;
+            y14 = _mm_slli_epi32(y14, 7);
+            z14 = _mm_xor_si128(z14, y14);
+            r14 = _mm_srli_epi32(r14, 25);
+            z14 = _mm_xor_si128(z14, r14);
+
+            y3 = z11;
+            y3 = _mm_add_epi32(y3, z15);
+            r3 = y3;
+            y3 = _mm_slli_epi32(y3, 7);
+            z3 = _mm_xor_si128(z3, y3);
+            r3 = _mm_srli_epi32(r3, 25);
+            z3 = _mm_xor_si128(z3, r3);
+
+            y2 = z10;
+            y2 = _mm_add_epi32(y2, z14);
+            r2 = y2;
+            y2 = _mm_slli_epi32(y2, 9);
+            z2 = _mm_xor_si128(z2, y2);
+            r2 = _mm_srli_epi32(r2, 23);
+            z2 = _mm_xor_si128(z2, r2);
+
+            y7 = z15;
+            y7 = _mm_add_epi32(y7, z3);
+            r7 = y7;
+            y7 = _mm_slli_epi32(y7, 9);
+            z7 = _mm_xor_si128(z7, y7);
+            r7 = _mm_srli_epi32(r7, 23);
+            z7 = _mm_xor_si128(z7, r7);
+
+            y6 = z14;
+            y6 = _mm_add_epi32(y6, z2);
+            r6 = y6;
+            y6 = _mm_slli_epi32(y6, 13);
+            z6 = _mm_xor_si128(z6, y6);
+            r6 = _mm_srli_epi32(r6, 19);
+            z6 = _mm_xor_si128(z6, r6);
+
+            y11 = z3;
+            y11 = _mm_add_epi32(y11, z7);
+            r11 = y11;
+            y11 = _mm_slli_epi32(y11, 13);
+            z11 = _mm_xor_si128(z11, y11);
+            r11 = _mm_srli_epi32(r11, 19);
+            z11 = _mm_xor_si128(z11, r11);
+
+            y10 = z2;
+            y10 = _mm_add_epi32(y10, z6);
+            r10 = y10;
+            y10 = _mm_slli_epi32(y10, 18);
+            z10 = _mm_xor_si128(z10, y10);
+            r10 = _mm_srli_epi32(r10, 14);
+            z10 = _mm_xor_si128(z10, r10);
+
+            y1 = z3;
+            y1 = _mm_add_epi32(y1, z0);
+            r1 = y1;
+            y1 = _mm_slli_epi32(y1, 7);
+            z1 = _mm_xor_si128(z1, y1);
+            r1 = _mm_srli_epi32(r1, 25);
+            z1 = _mm_xor_si128(z1, r1);
+
+            y15 = z7;
+            y15 = _mm_add_epi32(y15, z11);
+            r15 = y15;
+            y15 = _mm_slli_epi32(y15, 18);
+            z15 = _mm_xor_si128(z15, y15);
+            r15 = _mm_srli_epi32(r15, 14);
+            z15 = _mm_xor_si128(z15, r15);
+
+            y6 = z4;
+            y6 = _mm_add_epi32(y6, z5);
+            r6 = y6;
+            y6 = _mm_slli_epi32(y6, 7);
+            z6 = _mm_xor_si128(z6, y6);
+            r6 = _mm_srli_epi32(r6, 25);
+            z6 = _mm_xor_si128(z6, r6);
+
+            y2 = z0;
+            y2 = _mm_add_epi32(y2, z1);
+            r2 = y2;
+            y2 = _mm_slli_epi32(y2, 9);
+            z2 = _mm_xor_si128(z2, y2);
+            r2 = _mm_srli_epi32(r2, 23);
+            z2 = _mm_xor_si128(z2, r2);
+
+            y7 = z5;
+            y7 = _mm_add_epi32(y7, z6);
+            r7 = y7;
+            y7 = _mm_slli_epi32(y7, 9);
+            z7 = _mm_xor_si128(z7, y7);
+            r7 = _mm_srli_epi32(r7, 23);
+            z7 = _mm_xor_si128(z7, r7);
+
+            y3 = z1;
+            y3 = _mm_add_epi32(y3, z2);
+            r3 = y3;
+            y3 = _mm_slli_epi32(y3, 13);
+            z3 = _mm_xor_si128(z3, y3);
+            r3 = _mm_srli_epi32(r3, 19);
+            z3 = _mm_xor_si128(z3, r3);
+
+            y4 = z6;
+            y4 = _mm_add_epi32(y4, z7);
+            r4 = y4;
+            y4 = _mm_slli_epi32(y4, 13);
+            z4 = _mm_xor_si128(z4, y4);
+            r4 = _mm_srli_epi32(r4, 19);
+            z4 = _mm_xor_si128(z4, r4);
+
+            y0 = z2;
+            y0 = _mm_add_epi32(y0, z3);
+            r0 = y0;
+            y0 = _mm_slli_epi32(y0, 18);
+            z0 = _mm_xor_si128(z0, y0);
+            r0 = _mm_srli_epi32(r0, 14);
+            z0 = _mm_xor_si128(z0, r0);
+
+            y5 = z7;
+            y5 = _mm_add_epi32(y5, z4);
+            r5 = y5;
+            y5 = _mm_slli_epi32(y5, 18);
+            z5 = _mm_xor_si128(z5, y5);
+            r5 = _mm_srli_epi32(r5, 14);
+            z5 = _mm_xor_si128(z5, r5);
+
+            y11 = z9;
+            y11 = _mm_add_epi32(y11, z10);
+            r11 = y11;
+            y11 = _mm_slli_epi32(y11, 7);
+            z11 = _mm_xor_si128(z11, y11);
+            r11 = _mm_srli_epi32(r11, 25);
+            z11 = _mm_xor_si128(z11, r11);
+
+            y12 = z14;
+            y12 = _mm_add_epi32(y12, z15);
+            r12 = y12;
+            y12 = _mm_slli_epi32(y12, 7);
+            z12 = _mm_xor_si128(z12, y12);
+            r12 = _mm_srli_epi32(r12, 25);
+            z12 = _mm_xor_si128(z12, r12);
+
+            y8 = z10;
+            y8 = _mm_add_epi32(y8, z11);
+            r8 = y8;
+            y8 = _mm_slli_epi32(y8, 9);
+            z8 = _mm_xor_si128(z8, y8);
+            r8 = _mm_srli_epi32(r8, 23);
+            z8 = _mm_xor_si128(z8, r8);
+
+            y13 = z15;
+            y13 = _mm_add_epi32(y13, z12);
+            r13 = y13;
+            y13 = _mm_slli_epi32(y13, 9);
+            z13 = _mm_xor_si128(z13, y13);
+            r13 = _mm_srli_epi32(r13, 23);
+            z13 = _mm_xor_si128(z13, r13);
+
+            y9 = z11;
+            y9 = _mm_add_epi32(y9, z8);
+            r9 = y9;
+            y9 = _mm_slli_epi32(y9, 13);
+            z9 = _mm_xor_si128(z9, y9);
+            r9 = _mm_srli_epi32(r9, 19);
+            z9 = _mm_xor_si128(z9, r9);
+
+            y14 = z12;
+            y14 = _mm_add_epi32(y14, z13);
+            r14 = y14;
+            y14 = _mm_slli_epi32(y14, 13);
+            z14 = _mm_xor_si128(z14, y14);
+            r14 = _mm_srli_epi32(r14, 19);
+            z14 = _mm_xor_si128(z14, r14);
+
+            y10 = z8;
+            y10 = _mm_add_epi32(y10, z9);
+            r10 = y10;
+            y10 = _mm_slli_epi32(y10, 18);
+            z10 = _mm_xor_si128(z10, y10);
+            r10 = _mm_srli_epi32(r10, 14);
+            z10 = _mm_xor_si128(z10, r10);
+
+            y15 = z13;
+            y15 = _mm_add_epi32(y15, z14);
+            r15 = y15;
+            y15 = _mm_slli_epi32(y15, 18);
+            z15 = _mm_xor_si128(z15, y15);
+            r15 = _mm_srli_epi32(r15, 14);
+            z15 = _mm_xor_si128(z15, r15);
+        }
+
+/* store data ; this macro replicates the original amd64-xmm6 code */
+#define ONEQUAD_SHUFFLE(A, B, C, D)        \
+    z##A  = _mm_add_epi32(z##A, orig##A);  \
+    z##B  = _mm_add_epi32(z##B, orig##B);  \
+    z##C  = _mm_add_epi32(z##C, orig##C);  \
+    z##D  = _mm_add_epi32(z##D, orig##D);  \
+    in##A = _mm_cvtsi128_si32(z##A);       \
+    in##B = _mm_cvtsi128_si32(z##B);       \
+    in##C = _mm_cvtsi128_si32(z##C);       \
+    in##D = _mm_cvtsi128_si32(z##D);       \
+    z##A  = _mm_shuffle_epi32(z##A, 0x39); \
+    z##B  = _mm_shuffle_epi32(z##B, 0x39); \
+    z##C  = _mm_shuffle_epi32(z##C, 0x39); \
+    z##D  = _mm_shuffle_epi32(z##D, 0x39); \
+                                           \
+    in##A ^= *(uint32_t *) (m + 0);        \
+    in##B ^= *(uint32_t *) (m + 4);        \
+    in##C ^= *(uint32_t *) (m + 8);        \
+    in##D ^= *(uint32_t *) (m + 12);       \
+                                           \
+    *(uint32_t *) (c + 0)  = in##A;        \
+    *(uint32_t *) (c + 4)  = in##B;        \
+    *(uint32_t *) (c + 8)  = in##C;        \
+    *(uint32_t *) (c + 12) = in##D;        \
+                                           \
+    in##A = _mm_cvtsi128_si32(z##A);       \
+    in##B = _mm_cvtsi128_si32(z##B);       \
+    in##C = _mm_cvtsi128_si32(z##C);       \
+    in##D = _mm_cvtsi128_si32(z##D);       \
+    z##A  = _mm_shuffle_epi32(z##A, 0x39); \
+    z##B  = _mm_shuffle_epi32(z##B, 0x39); \
+    z##C  = _mm_shuffle_epi32(z##C, 0x39); \
+    z##D  = _mm_shuffle_epi32(z##D, 0x39); \
+                                           \
+    in##A ^= *(uint32_t *) (m + 64);       \
+    in##B ^= *(uint32_t *) (m + 68);       \
+    in##C ^= *(uint32_t *) (m + 72);       \
+    in##D ^= *(uint32_t *) (m + 76);       \
+    *(uint32_t *) (c + 64) = in##A;        \
+    *(uint32_t *) (c + 68) = in##B;        \
+    *(uint32_t *) (c + 72) = in##C;        \
+    *(uint32_t *) (c + 76) = in##D;        \
+                                           \
+    in##A = _mm_cvtsi128_si32(z##A);       \
+    in##B = _mm_cvtsi128_si32(z##B);       \
+    in##C = _mm_cvtsi128_si32(z##C);       \
+    in##D = _mm_cvtsi128_si32(z##D);       \
+    z##A  = _mm_shuffle_epi32(z##A, 0x39); \
+    z##B  = _mm_shuffle_epi32(z##B, 0x39); \
+    z##C  = _mm_shuffle_epi32(z##C, 0x39); \
+    z##D  = _mm_shuffle_epi32(z##D, 0x39); \
+                                           \
+    in##A ^= *(uint32_t *) (m + 128);      \
+    in##B ^= *(uint32_t *) (m + 132);      \
+    in##C ^= *(uint32_t *) (m + 136);      \
+    in##D ^= *(uint32_t *) (m + 140);      \
+    *(uint32_t *) (c + 128) = in##A;       \
+    *(uint32_t *) (c + 132) = in##B;       \
+    *(uint32_t *) (c + 136) = in##C;       \
+    *(uint32_t *) (c + 140) = in##D;       \
+                                           \
+    in##A = _mm_cvtsi128_si32(z##A);       \
+    in##B = _mm_cvtsi128_si32(z##B);       \
+    in##C = _mm_cvtsi128_si32(z##C);       \
+    in##D = _mm_cvtsi128_si32(z##D);       \
+                                           \
+    in##A ^= *(uint32_t *) (m + 192);      \
+    in##B ^= *(uint32_t *) (m + 196);      \
+    in##C ^= *(uint32_t *) (m + 200);      \
+    in##D ^= *(uint32_t *) (m + 204);      \
+    *(uint32_t *) (c + 192) = in##A;       \
+    *(uint32_t *) (c + 196) = in##B;       \
+    *(uint32_t *) (c + 200) = in##C;       \
+    *(uint32_t *) (c + 204) = in##D
+
+/* store data ; this macro replaces shuffle+mov by a direct extract; not much
+ * difference */
+#define ONEQUAD_EXTRACT(A, B, C, D)       \
+    z##A  = _mm_add_epi32(z##A, orig##A); \
+    z##B  = _mm_add_epi32(z##B, orig##B); \
+    z##C  = _mm_add_epi32(z##C, orig##C); \
+    z##D  = _mm_add_epi32(z##D, orig##D); \
+    in##A = _mm_cvtsi128_si32(z##A);      \
+    in##B = _mm_cvtsi128_si32(z##B);      \
+    in##C = _mm_cvtsi128_si32(z##C);      \
+    in##D = _mm_cvtsi128_si32(z##D);      \
+    in##A ^= *(uint32_t *) (m + 0);       \
+    in##B ^= *(uint32_t *) (m + 4);       \
+    in##C ^= *(uint32_t *) (m + 8);       \
+    in##D ^= *(uint32_t *) (m + 12);      \
+    *(uint32_t *) (c + 0)  = in##A;       \
+    *(uint32_t *) (c + 4)  = in##B;       \
+    *(uint32_t *) (c + 8)  = in##C;       \
+    *(uint32_t *) (c + 12) = in##D;       \
+                                          \
+    in##A = _mm_extract_epi32(z##A, 1);   \
+    in##B = _mm_extract_epi32(z##B, 1);   \
+    in##C = _mm_extract_epi32(z##C, 1);   \
+    in##D = _mm_extract_epi32(z##D, 1);   \
+                                          \
+    in##A ^= *(uint32_t *) (m + 64);      \
+    in##B ^= *(uint32_t *) (m + 68);      \
+    in##C ^= *(uint32_t *) (m + 72);      \
+    in##D ^= *(uint32_t *) (m + 76);      \
+    *(uint32_t *) (c + 64) = in##A;       \
+    *(uint32_t *) (c + 68) = in##B;       \
+    *(uint32_t *) (c + 72) = in##C;       \
+    *(uint32_t *) (c + 76) = in##D;       \
+                                          \
+    in##A = _mm_extract_epi32(z##A, 2);   \
+    in##B = _mm_extract_epi32(z##B, 2);   \
+    in##C = _mm_extract_epi32(z##C, 2);   \
+    in##D = _mm_extract_epi32(z##D, 2);   \
+                                          \
+    in##A ^= *(uint32_t *) (m + 128);     \
+    in##B ^= *(uint32_t *) (m + 132);     \
+    in##C ^= *(uint32_t *) (m + 136);     \
+    in##D ^= *(uint32_t *) (m + 140);     \
+    *(uint32_t *) (c + 128) = in##A;      \
+    *(uint32_t *) (c + 132) = in##B;      \
+    *(uint32_t *) (c + 136) = in##C;      \
+    *(uint32_t *) (c + 140) = in##D;      \
+                                          \
+    in##A = _mm_extract_epi32(z##A, 3);   \
+    in##B = _mm_extract_epi32(z##B, 3);   \
+    in##C = _mm_extract_epi32(z##C, 3);   \
+    in##D = _mm_extract_epi32(z##D, 3);   \
+                                          \
+    in##A ^= *(uint32_t *) (m + 192);     \
+    in##B ^= *(uint32_t *) (m + 196);     \
+    in##C ^= *(uint32_t *) (m + 200);     \
+    in##D ^= *(uint32_t *) (m + 204);     \
+    *(uint32_t *) (c + 192) = in##A;      \
+    *(uint32_t *) (c + 196) = in##B;      \
+    *(uint32_t *) (c + 200) = in##C;      \
+    *(uint32_t *) (c + 204) = in##D
+
+/* store data ; this macro first transpose data in-registers, and then store
+ * them in memory. much faster with icc. */
+#define ONEQUAD_TRANSPOSE(A, B, C, D)                                         \
+    z##A = _mm_add_epi32(z##A, orig##A);                                      \
+    z##B = _mm_add_epi32(z##B, orig##B);                                      \
+    z##C = _mm_add_epi32(z##C, orig##C);                                      \
+    z##D = _mm_add_epi32(z##D, orig##D);                                      \
+    y##A = _mm_unpacklo_epi32(z##A, z##B);                                    \
+    y##B = _mm_unpacklo_epi32(z##C, z##D);                                    \
+    y##C = _mm_unpackhi_epi32(z##A, z##B);                                    \
+    y##D = _mm_unpackhi_epi32(z##C, z##D);                                    \
+    z##A = _mm_unpacklo_epi64(y##A, y##B);                                    \
+    z##B = _mm_unpackhi_epi64(y##A, y##B);                                    \
+    z##C = _mm_unpacklo_epi64(y##C, y##D);                                    \
+    z##D = _mm_unpackhi_epi64(y##C, y##D);                                    \
+    y##A = _mm_xor_si128(z##A, _mm_loadu_si128((const __m128i *) (m + 0)));   \
+    _mm_storeu_si128((__m128i *) (c + 0), y##A);                              \
+    y##B = _mm_xor_si128(z##B, _mm_loadu_si128((const __m128i *) (m + 64)));  \
+    _mm_storeu_si128((__m128i *) (c + 64), y##B);                             \
+    y##C = _mm_xor_si128(z##C, _mm_loadu_si128((const __m128i *) (m + 128))); \
+    _mm_storeu_si128((__m128i *) (c + 128), y##C);                            \
+    y##D = _mm_xor_si128(z##D, _mm_loadu_si128((const __m128i *) (m + 192))); \
+    _mm_storeu_si128((__m128i *) (c + 192), y##D)
+
+#define ONEQUAD(A, B, C, D) ONEQUAD_TRANSPOSE(A, B, C, D)
+
+        ONEQUAD(0, 1, 2, 3);
+        m += 16;
+        c += 16;
+        ONEQUAD(4, 5, 6, 7);
+        m += 16;
+        c += 16;
+        ONEQUAD(8, 9, 10, 11);
+        m += 16;
+        c += 16;
+        ONEQUAD(12, 13, 14, 15);
+        m -= 48;
+        c -= 48;
+
+#undef ONEQUAD
+#undef ONEQUAD_TRANSPOSE
+#undef ONEQUAD_EXTRACT
+#undef ONEQUAD_SHUFFLE
+
+        bytes -= 256;
+        c += 256;
+        m += 256;
+    }
+}
diff --git a/src/crypto/astrobwt/xmm6int/u8.h b/src/crypto/astrobwt/xmm6int/u8.h
new file mode 100644
index 000000000..581b22c21
--- /dev/null
+++ b/src/crypto/astrobwt/xmm6int/u8.h
@@ -0,0 +1,477 @@
+if (bytes >= 512) {
+    __m256i y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13, y14,
+        y15;
+
+    /* the naive way seems as fast (if not a bit faster) than the vector way */
+    __m256i z0  = _mm256_set1_epi32(x[0]);
+    __m256i z5  = _mm256_set1_epi32(x[1]);
+    __m256i z10 = _mm256_set1_epi32(x[2]);
+    __m256i z15 = _mm256_set1_epi32(x[3]);
+    __m256i z12 = _mm256_set1_epi32(x[4]);
+    __m256i z1  = _mm256_set1_epi32(x[5]);
+    __m256i z6  = _mm256_set1_epi32(x[6]);
+    __m256i z11 = _mm256_set1_epi32(x[7]);
+    __m256i z8; /* useless */
+    __m256i z13 = _mm256_set1_epi32(x[9]);
+    __m256i z2  = _mm256_set1_epi32(x[10]);
+    __m256i z7  = _mm256_set1_epi32(x[11]);
+    __m256i z4  = _mm256_set1_epi32(x[12]);
+    __m256i z9; /* useless */
+    __m256i z14 = _mm256_set1_epi32(x[14]);
+    __m256i z3  = _mm256_set1_epi32(x[15]);
+
+    __m256i orig0 = z0;
+    __m256i orig1 = z1;
+    __m256i orig2 = z2;
+    __m256i orig3 = z3;
+    __m256i orig4 = z4;
+    __m256i orig5 = z5;
+    __m256i orig6 = z6;
+    __m256i orig7 = z7;
+    __m256i orig8;
+    __m256i orig9;
+    __m256i orig10 = z10;
+    __m256i orig11 = z11;
+    __m256i orig12 = z12;
+    __m256i orig13 = z13;
+    __m256i orig14 = z14;
+    __m256i orig15 = z15;
+
+    uint32_t in8;
+    uint32_t in9;
+    int      i;
+
+    while (bytes >= 512) {
+        /* vector implementation for z8 and z9 */
+        /* faster than the naive version for 8 blocks */
+        const __m256i addv8   = _mm256_set_epi64x(3, 2, 1, 0);
+        const __m256i addv9   = _mm256_set_epi64x(7, 6, 5, 4);
+        const __m256i permute = _mm256_set_epi32(7, 6, 3, 2, 5, 4, 1, 0);
+
+        __m256i  t8, t9;
+        uint64_t in89;
+
+        in8  = x[8];
+        in9  = x[13]; /* see arrays above for the address translation */
+        in89 = ((uint64_t) in8) | (((uint64_t) in9) << 32);
+
+        z8 = z9 = _mm256_broadcastq_epi64(_mm_cvtsi64_si128(in89));
+
+        t8 = _mm256_add_epi64(addv8, z8);
+        t9 = _mm256_add_epi64(addv9, z9);
+
+        z8 = _mm256_unpacklo_epi32(t8, t9);
+        z9 = _mm256_unpackhi_epi32(t8, t9);
+
+        t8 = _mm256_unpacklo_epi32(z8, z9);
+        t9 = _mm256_unpackhi_epi32(z8, z9);
+
+        /* required because unpack* are intra-lane */
+        z8 = _mm256_permutevar8x32_epi32(t8, permute);
+        z9 = _mm256_permutevar8x32_epi32(t9, permute);
+
+        orig8 = z8;
+        orig9 = z9;
+
+        in89 += 8;
+
+        x[8]  = in89 & 0xFFFFFFFF;
+        x[13] = (in89 >> 32) & 0xFFFFFFFF;
+
+        z5  = orig5;
+        z10 = orig10;
+        z15 = orig15;
+        z14 = orig14;
+        z3  = orig3;
+        z6  = orig6;
+        z11 = orig11;
+        z1  = orig1;
+
+        z7  = orig7;
+        z13 = orig13;
+        z2  = orig2;
+        z9  = orig9;
+        z0  = orig0;
+        z12 = orig12;
+        z4  = orig4;
+        z8  = orig8;
+
+        for (i = 0; i < ROUNDS; i += 2) {
+            /* the inner loop is a direct translation (regexp search/replace)
+             * from the amd64-xmm6 ASM */
+            __m256i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13,
+                r14, r15;
+
+            y4 = z12;
+            y4 = _mm256_add_epi32(y4, z0);
+            r4 = y4;
+            y4 = _mm256_slli_epi32(y4, 7);
+            z4 = _mm256_xor_si256(z4, y4);
+            r4 = _mm256_srli_epi32(r4, 25);
+            z4 = _mm256_xor_si256(z4, r4);
+
+            y9 = z1;
+            y9 = _mm256_add_epi32(y9, z5);
+            r9 = y9;
+            y9 = _mm256_slli_epi32(y9, 7);
+            z9 = _mm256_xor_si256(z9, y9);
+            r9 = _mm256_srli_epi32(r9, 25);
+            z9 = _mm256_xor_si256(z9, r9);
+
+            y8 = z0;
+            y8 = _mm256_add_epi32(y8, z4);
+            r8 = y8;
+            y8 = _mm256_slli_epi32(y8, 9);
+            z8 = _mm256_xor_si256(z8, y8);
+            r8 = _mm256_srli_epi32(r8, 23);
+            z8 = _mm256_xor_si256(z8, r8);
+
+            y13 = z5;
+            y13 = _mm256_add_epi32(y13, z9);
+            r13 = y13;
+            y13 = _mm256_slli_epi32(y13, 9);
+            z13 = _mm256_xor_si256(z13, y13);
+            r13 = _mm256_srli_epi32(r13, 23);
+            z13 = _mm256_xor_si256(z13, r13);
+
+            y12 = z4;
+            y12 = _mm256_add_epi32(y12, z8);
+            r12 = y12;
+            y12 = _mm256_slli_epi32(y12, 13);
+            z12 = _mm256_xor_si256(z12, y12);
+            r12 = _mm256_srli_epi32(r12, 19);
+            z12 = _mm256_xor_si256(z12, r12);
+
+            y1 = z9;
+            y1 = _mm256_add_epi32(y1, z13);
+            r1 = y1;
+            y1 = _mm256_slli_epi32(y1, 13);
+            z1 = _mm256_xor_si256(z1, y1);
+            r1 = _mm256_srli_epi32(r1, 19);
+            z1 = _mm256_xor_si256(z1, r1);
+
+            y0 = z8;
+            y0 = _mm256_add_epi32(y0, z12);
+            r0 = y0;
+            y0 = _mm256_slli_epi32(y0, 18);
+            z0 = _mm256_xor_si256(z0, y0);
+            r0 = _mm256_srli_epi32(r0, 14);
+            z0 = _mm256_xor_si256(z0, r0);
+
+            y5 = z13;
+            y5 = _mm256_add_epi32(y5, z1);
+            r5 = y5;
+            y5 = _mm256_slli_epi32(y5, 18);
+            z5 = _mm256_xor_si256(z5, y5);
+            r5 = _mm256_srli_epi32(r5, 14);
+            z5 = _mm256_xor_si256(z5, r5);
+
+            y14 = z6;
+            y14 = _mm256_add_epi32(y14, z10);
+            r14 = y14;
+            y14 = _mm256_slli_epi32(y14, 7);
+            z14 = _mm256_xor_si256(z14, y14);
+            r14 = _mm256_srli_epi32(r14, 25);
+            z14 = _mm256_xor_si256(z14, r14);
+
+            y3 = z11;
+            y3 = _mm256_add_epi32(y3, z15);
+            r3 = y3;
+            y3 = _mm256_slli_epi32(y3, 7);
+            z3 = _mm256_xor_si256(z3, y3);
+            r3 = _mm256_srli_epi32(r3, 25);
+            z3 = _mm256_xor_si256(z3, r3);
+
+            y2 = z10;
+            y2 = _mm256_add_epi32(y2, z14);
+            r2 = y2;
+            y2 = _mm256_slli_epi32(y2, 9);
+            z2 = _mm256_xor_si256(z2, y2);
+            r2 = _mm256_srli_epi32(r2, 23);
+            z2 = _mm256_xor_si256(z2, r2);
+
+            y7 = z15;
+            y7 = _mm256_add_epi32(y7, z3);
+            r7 = y7;
+            y7 = _mm256_slli_epi32(y7, 9);
+            z7 = _mm256_xor_si256(z7, y7);
+            r7 = _mm256_srli_epi32(r7, 23);
+            z7 = _mm256_xor_si256(z7, r7);
+
+            y6 = z14;
+            y6 = _mm256_add_epi32(y6, z2);
+            r6 = y6;
+            y6 = _mm256_slli_epi32(y6, 13);
+            z6 = _mm256_xor_si256(z6, y6);
+            r6 = _mm256_srli_epi32(r6, 19);
+            z6 = _mm256_xor_si256(z6, r6);
+
+            y11 = z3;
+            y11 = _mm256_add_epi32(y11, z7);
+            r11 = y11;
+            y11 = _mm256_slli_epi32(y11, 13);
+            z11 = _mm256_xor_si256(z11, y11);
+            r11 = _mm256_srli_epi32(r11, 19);
+            z11 = _mm256_xor_si256(z11, r11);
+
+            y10 = z2;
+            y10 = _mm256_add_epi32(y10, z6);
+            r10 = y10;
+            y10 = _mm256_slli_epi32(y10, 18);
+            z10 = _mm256_xor_si256(z10, y10);
+            r10 = _mm256_srli_epi32(r10, 14);
+            z10 = _mm256_xor_si256(z10, r10);
+
+            y1 = z3;
+            y1 = _mm256_add_epi32(y1, z0);
+            r1 = y1;
+            y1 = _mm256_slli_epi32(y1, 7);
+            z1 = _mm256_xor_si256(z1, y1);
+            r1 = _mm256_srli_epi32(r1, 25);
+            z1 = _mm256_xor_si256(z1, r1);
+
+            y15 = z7;
+            y15 = _mm256_add_epi32(y15, z11);
+            r15 = y15;
+            y15 = _mm256_slli_epi32(y15, 18);
+            z15 = _mm256_xor_si256(z15, y15);
+            r15 = _mm256_srli_epi32(r15, 14);
+            z15 = _mm256_xor_si256(z15, r15);
+
+            y6 = z4;
+            y6 = _mm256_add_epi32(y6, z5);
+            r6 = y6;
+            y6 = _mm256_slli_epi32(y6, 7);
+            z6 = _mm256_xor_si256(z6, y6);
+            r6 = _mm256_srli_epi32(r6, 25);
+            z6 = _mm256_xor_si256(z6, r6);
+
+            y2 = z0;
+            y2 = _mm256_add_epi32(y2, z1);
+            r2 = y2;
+            y2 = _mm256_slli_epi32(y2, 9);
+            z2 = _mm256_xor_si256(z2, y2);
+            r2 = _mm256_srli_epi32(r2, 23);
+            z2 = _mm256_xor_si256(z2, r2);
+
+            y7 = z5;
+            y7 = _mm256_add_epi32(y7, z6);
+            r7 = y7;
+            y7 = _mm256_slli_epi32(y7, 9);
+            z7 = _mm256_xor_si256(z7, y7);
+            r7 = _mm256_srli_epi32(r7, 23);
+            z7 = _mm256_xor_si256(z7, r7);
+
+            y3 = z1;
+            y3 = _mm256_add_epi32(y3, z2);
+            r3 = y3;
+            y3 = _mm256_slli_epi32(y3, 13);
+            z3 = _mm256_xor_si256(z3, y3);
+            r3 = _mm256_srli_epi32(r3, 19);
+            z3 = _mm256_xor_si256(z3, r3);
+
+            y4 = z6;
+            y4 = _mm256_add_epi32(y4, z7);
+            r4 = y4;
+            y4 = _mm256_slli_epi32(y4, 13);
+            z4 = _mm256_xor_si256(z4, y4);
+            r4 = _mm256_srli_epi32(r4, 19);
+            z4 = _mm256_xor_si256(z4, r4);
+
+            y0 = z2;
+            y0 = _mm256_add_epi32(y0, z3);
+            r0 = y0;
+            y0 = _mm256_slli_epi32(y0, 18);
+            z0 = _mm256_xor_si256(z0, y0);
+            r0 = _mm256_srli_epi32(r0, 14);
+            z0 = _mm256_xor_si256(z0, r0);
+
+            y5 = z7;
+            y5 = _mm256_add_epi32(y5, z4);
+            r5 = y5;
+            y5 = _mm256_slli_epi32(y5, 18);
+            z5 = _mm256_xor_si256(z5, y5);
+            r5 = _mm256_srli_epi32(r5, 14);
+            z5 = _mm256_xor_si256(z5, r5);
+
+            y11 = z9;
+            y11 = _mm256_add_epi32(y11, z10);
+            r11 = y11;
+            y11 = _mm256_slli_epi32(y11, 7);
+            z11 = _mm256_xor_si256(z11, y11);
+            r11 = _mm256_srli_epi32(r11, 25);
+            z11 = _mm256_xor_si256(z11, r11);
+
+            y12 = z14;
+            y12 = _mm256_add_epi32(y12, z15);
+            r12 = y12;
+            y12 = _mm256_slli_epi32(y12, 7);
+            z12 = _mm256_xor_si256(z12, y12);
+            r12 = _mm256_srli_epi32(r12, 25);
+            z12 = _mm256_xor_si256(z12, r12);
+
+            y8 = z10;
+            y8 = _mm256_add_epi32(y8, z11);
+            r8 = y8;
+            y8 = _mm256_slli_epi32(y8, 9);
+            z8 = _mm256_xor_si256(z8, y8);
+            r8 = _mm256_srli_epi32(r8, 23);
+            z8 = _mm256_xor_si256(z8, r8);
+
+            y13 = z15;
+            y13 = _mm256_add_epi32(y13, z12);
+            r13 = y13;
+            y13 = _mm256_slli_epi32(y13, 9);
+            z13 = _mm256_xor_si256(z13, y13);
+            r13 = _mm256_srli_epi32(r13, 23);
+            z13 = _mm256_xor_si256(z13, r13);
+
+            y9 = z11;
+            y9 = _mm256_add_epi32(y9, z8);
+            r9 = y9;
+            y9 = _mm256_slli_epi32(y9, 13);
+            z9 = _mm256_xor_si256(z9, y9);
+            r9 = _mm256_srli_epi32(r9, 19);
+            z9 = _mm256_xor_si256(z9, r9);
+
+            y14 = z12;
+            y14 = _mm256_add_epi32(y14, z13);
+            r14 = y14;
+            y14 = _mm256_slli_epi32(y14, 13);
+            z14 = _mm256_xor_si256(z14, y14);
+            r14 = _mm256_srli_epi32(r14, 19);
+            z14 = _mm256_xor_si256(z14, r14);
+
+            y10 = z8;
+            y10 = _mm256_add_epi32(y10, z9);
+            r10 = y10;
+            y10 = _mm256_slli_epi32(y10, 18);
+            z10 = _mm256_xor_si256(z10, y10);
+            r10 = _mm256_srli_epi32(r10, 14);
+            z10 = _mm256_xor_si256(z10, r10);
+
+            y15 = z13;
+            y15 = _mm256_add_epi32(y15, z14);
+            r15 = y15;
+            y15 = _mm256_slli_epi32(y15, 18);
+            z15 = _mm256_xor_si256(z15, y15);
+            r15 = _mm256_srli_epi32(r15, 14);
+            z15 = _mm256_xor_si256(z15, r15);
+        }
+
+/* store data ; this macro first transpose data in-registers, and then store
+ * them in memory. much faster with icc. */
+#define ONEQUAD_TRANSPOSE(A, B, C, D)                                    \
+    {                                                                    \
+        __m128i t0, t1, t2, t3;                                          \
+        z##A = _mm256_add_epi32(z##A, orig##A);                          \
+        z##B = _mm256_add_epi32(z##B, orig##B);                          \
+        z##C = _mm256_add_epi32(z##C, orig##C);                          \
+        z##D = _mm256_add_epi32(z##D, orig##D);                          \
+        y##A = _mm256_unpacklo_epi32(z##A, z##B);                        \
+        y##B = _mm256_unpacklo_epi32(z##C, z##D);                        \
+        y##C = _mm256_unpackhi_epi32(z##A, z##B);                        \
+        y##D = _mm256_unpackhi_epi32(z##C, z##D);                        \
+        z##A = _mm256_unpacklo_epi64(y##A, y##B);                        \
+        z##B = _mm256_unpackhi_epi64(y##A, y##B);                        \
+        z##C = _mm256_unpacklo_epi64(y##C, y##D);                        \
+        z##D = _mm256_unpackhi_epi64(y##C, y##D);                        \
+        t0   = _mm_xor_si128(_mm256_extracti128_si256(z##A, 0),          \
+                           _mm_loadu_si128((const __m128i*) (m + 0))); \
+        _mm_storeu_si128((__m128i*) (c + 0), t0);                        \
+        t1 = _mm_xor_si128(_mm256_extracti128_si256(z##B, 0),            \
+                           _mm_loadu_si128((const __m128i*) (m + 64)));  \
+        _mm_storeu_si128((__m128i*) (c + 64), t1);                       \
+        t2 = _mm_xor_si128(_mm256_extracti128_si256(z##C, 0),            \
+                           _mm_loadu_si128((const __m128i*) (m + 128))); \
+        _mm_storeu_si128((__m128i*) (c + 128), t2);                      \
+        t3 = _mm_xor_si128(_mm256_extracti128_si256(z##D, 0),            \
+                           _mm_loadu_si128((const __m128i*) (m + 192))); \
+        _mm_storeu_si128((__m128i*) (c + 192), t3);                      \
+        t0 = _mm_xor_si128(_mm256_extracti128_si256(z##A, 1),            \
+                           _mm_loadu_si128((const __m128i*) (m + 256))); \
+        _mm_storeu_si128((__m128i*) (c + 256), t0);                      \
+        t1 = _mm_xor_si128(_mm256_extracti128_si256(z##B, 1),            \
+                           _mm_loadu_si128((const __m128i*) (m + 320))); \
+        _mm_storeu_si128((__m128i*) (c + 320), t1);                      \
+        t2 = _mm_xor_si128(_mm256_extracti128_si256(z##C, 1),            \
+                           _mm_loadu_si128((const __m128i*) (m + 384))); \
+        _mm_storeu_si128((__m128i*) (c + 384), t2);                      \
+        t3 = _mm_xor_si128(_mm256_extracti128_si256(z##D, 1),            \
+                           _mm_loadu_si128((const __m128i*) (m + 448))); \
+        _mm_storeu_si128((__m128i*) (c + 448), t3);                      \
+    }
+
+#define ONEQUAD(A, B, C, D) ONEQUAD_TRANSPOSE(A, B, C, D)
+
+#define ONEQUAD_UNPCK(A, B, C, D)                 \
+    {                                             \
+        z##A = _mm256_add_epi32(z##A, orig##A);   \
+        z##B = _mm256_add_epi32(z##B, orig##B);   \
+        z##C = _mm256_add_epi32(z##C, orig##C);   \
+        z##D = _mm256_add_epi32(z##D, orig##D);   \
+        y##A = _mm256_unpacklo_epi32(z##A, z##B); \
+        y##B = _mm256_unpacklo_epi32(z##C, z##D); \
+        y##C = _mm256_unpackhi_epi32(z##A, z##B); \
+        y##D = _mm256_unpackhi_epi32(z##C, z##D); \
+        z##A = _mm256_unpacklo_epi64(y##A, y##B); \
+        z##B = _mm256_unpackhi_epi64(y##A, y##B); \
+        z##C = _mm256_unpacklo_epi64(y##C, y##D); \
+        z##D = _mm256_unpackhi_epi64(y##C, y##D); \
+    }
+
+#define ONEOCTO(A, B, C, D, A2, B2, C2, D2)                                    \
+    {                                                                          \
+        ONEQUAD_UNPCK(A, B, C, D);                                             \
+        ONEQUAD_UNPCK(A2, B2, C2, D2);                                         \
+        y##A  = _mm256_permute2x128_si256(z##A, z##A2, 0x20);                  \
+        y##A2 = _mm256_permute2x128_si256(z##A, z##A2, 0x31);                  \
+        y##B  = _mm256_permute2x128_si256(z##B, z##B2, 0x20);                  \
+        y##B2 = _mm256_permute2x128_si256(z##B, z##B2, 0x31);                  \
+        y##C  = _mm256_permute2x128_si256(z##C, z##C2, 0x20);                  \
+        y##C2 = _mm256_permute2x128_si256(z##C, z##C2, 0x31);                  \
+        y##D  = _mm256_permute2x128_si256(z##D, z##D2, 0x20);                  \
+        y##D2 = _mm256_permute2x128_si256(z##D, z##D2, 0x31);                  \
+        y##A  = _mm256_xor_si256(y##A,                                         \
+                                _mm256_loadu_si256((const __m256i*) (m + 0))); \
+        y##B  = _mm256_xor_si256(                                              \
+            y##B, _mm256_loadu_si256((const __m256i*) (m + 64)));              \
+        y##C = _mm256_xor_si256(                                               \
+            y##C, _mm256_loadu_si256((const __m256i*) (m + 128)));             \
+        y##D = _mm256_xor_si256(                                               \
+            y##D, _mm256_loadu_si256((const __m256i*) (m + 192)));             \
+        y##A2 = _mm256_xor_si256(                                              \
+            y##A2, _mm256_loadu_si256((const __m256i*) (m + 256)));            \
+        y##B2 = _mm256_xor_si256(                                              \
+            y##B2, _mm256_loadu_si256((const __m256i*) (m + 320)));            \
+        y##C2 = _mm256_xor_si256(                                              \
+            y##C2, _mm256_loadu_si256((const __m256i*) (m + 384)));            \
+        y##D2 = _mm256_xor_si256(                                              \
+            y##D2, _mm256_loadu_si256((const __m256i*) (m + 448)));            \
+        _mm256_storeu_si256((__m256i*) (c + 0), y##A);                         \
+        _mm256_storeu_si256((__m256i*) (c + 64), y##B);                        \
+        _mm256_storeu_si256((__m256i*) (c + 128), y##C);                       \
+        _mm256_storeu_si256((__m256i*) (c + 192), y##D);                       \
+        _mm256_storeu_si256((__m256i*) (c + 256), y##A2);                      \
+        _mm256_storeu_si256((__m256i*) (c + 320), y##B2);                      \
+        _mm256_storeu_si256((__m256i*) (c + 384), y##C2);                      \
+        _mm256_storeu_si256((__m256i*) (c + 448), y##D2);                      \
+    }
+
+        ONEOCTO(0, 1, 2, 3, 4, 5, 6, 7);
+        m += 32;
+        c += 32;
+        ONEOCTO(8, 9, 10, 11, 12, 13, 14, 15);
+        m -= 32;
+        c -= 32;
+
+#undef ONEQUAD
+#undef ONEQUAD_TRANSPOSE
+#undef ONEQUAD_UNPCK
+#undef ONEOCTO
+
+        bytes -= 512;
+        c += 512;
+        m += 512;
+    }
+}

From cd7c7902a9f97727e92991760543c4dd739b5e65 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sun, 29 Aug 2021 18:52:11 +0700
Subject: [PATCH 18/20] Fixed clang build.

---
 cmake/astrobwt.cmake                               | 5 +++++
 src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c | 7 -------
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/cmake/astrobwt.cmake b/cmake/astrobwt.cmake
index 6f48d13e7..f0ebf5306 100644
--- a/cmake/astrobwt.cmake
+++ b/cmake/astrobwt.cmake
@@ -24,6 +24,11 @@ if (WITH_ASTROBWT)
         if (CMAKE_SIZEOF_VOID_P EQUAL 8)
             add_definitions(/DASTROBWT_AVX2)
             list(APPEND SOURCES_CRYPTO src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c)
+
+            if (CMAKE_C_COMPILER_ID MATCHES GNU OR CMAKE_C_COMPILER_ID MATCHES Clang)
+                set_source_files_properties(src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c PROPERTIES COMPILE_FLAGS -mavx2)
+            endif()
+
             if (CMAKE_C_COMPILER_ID MATCHES MSVC)
                 enable_language(ASM_MASM)
                 list(APPEND SOURCES_CRYPTO src/crypto/astrobwt/sha3_256_avx2.asm)
diff --git a/src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c b/src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c
index fe2047d41..dab2b74d3 100644
--- a/src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c
+++ b/src/crypto/astrobwt/xmm6int/salsa20_xmm6int-avx2.c
@@ -21,13 +21,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#ifdef __GNUC__
-#pragma GCC target("sse2")
-#pragma GCC target("ssse3")
-#pragma GCC target("sse4.1")
-#pragma GCC target("avx2")
-#endif
-
 #include <emmintrin.h>
 #include <immintrin.h>
 #include <smmintrin.h>

From 9a6f773deafab97fb9209777ce32c0c6023ddddf Mon Sep 17 00:00:00 2001
From: xmrig <support@xmrig.com>
Date: Sun, 29 Aug 2021 20:19:41 +0700
Subject: [PATCH 19/20] Update CHANGELOG.md

---
 CHANGELOG.md | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 103fea95d..a8cdb4dda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# v6.15.0
+- [#2548](https://github.com/xmrig/xmrig/pull/2548) Added automatic coin detection for daemon mining.
+- [#2563](https://github.com/xmrig/xmrig/pull/2563) Added new algorithm RandomX Graft (`rx/graft`).
+- [#2565](https://github.com/xmrig/xmrig/pull/2565) AstroBWT: added AVX2 Salsa20 implementation.
+- Added support for new CUDA plugin API (previous API still supported).
+
 # v6.14.1
 - [#2532](https://github.com/xmrig/xmrig/pull/2532) Refactoring: stable (persistent) algorithms IDs.
 - [#2537](https://github.com/xmrig/xmrig/pull/2537) Fixed Termux build.

From d048d5a63950eb22ad8655dae5d2faccc7bd41f7 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Tue, 31 Aug 2021 03:32:36 +0700
Subject: [PATCH 20/20] Fixed class/struct inconsistency.

---
 src/hw/api/HwApi.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/hw/api/HwApi.h b/src/hw/api/HwApi.h
index f434a0a5a..9de8dfd5e 100644
--- a/src/hw/api/HwApi.h
+++ b/src/hw/api/HwApi.h
@@ -29,7 +29,7 @@
 namespace xmrig {
 
 
-struct DmiReader;
+class DmiReader;
 
 
 class HwApi : public IApiListener