Added c++ configurator

This commit is contained in:
Pax1601 2023-12-11 19:44:58 +01:00
parent fbd42ade6e
commit 532a83fe91
17 changed files with 2011 additions and 3 deletions

View File

@ -4,9 +4,9 @@ cd src
msbuild olympus.sln /t:Rebuild /p:Configuration=Release
cd ..
cd scripts/python/configurator
call build_configurator.bat
cd ../../..
cd configurator
msbuild configurator.sln /t:Rebuild /p:Configuration=Release
cd ..
cd client
rmdir /s /q "hgt"

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32929.385
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "configurator", "configurator\configurator.vcxproj", "{FF484257-F6D1-43E9-B6BA-A892B86E8963}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FF484257-F6D1-43E9-B6BA-A892B86E8963}.Debug|x64.ActiveCfg = Debug|x64
{FF484257-F6D1-43E9-B6BA-A892B86E8963}.Debug|x64.Build.0 = Debug|x64
{FF484257-F6D1-43E9-B6BA-A892B86E8963}.Debug|x86.ActiveCfg = Debug|Win32
{FF484257-F6D1-43E9-B6BA-A892B86E8963}.Debug|x86.Build.0 = Debug|Win32
{FF484257-F6D1-43E9-B6BA-A892B86E8963}.Release|x64.ActiveCfg = Release|x64
{FF484257-F6D1-43E9-B6BA-A892B86E8963}.Release|x64.Build.0 = Release|x64
{FF484257-F6D1-43E9-B6BA-A892B86E8963}.Release|x86.ActiveCfg = Release|Win32
{FF484257-F6D1-43E9-B6BA-A892B86E8963}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {19612EF0-B177-4572-A304-ECE142C31BDC}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,170 @@
// configurator.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <fstream>
#include <nlohmann/json.hpp>
#include "argparse/argparse.hpp"
#include "sha256.h"
#include "windows.h"
#include <chrono>
#include <thread>
using json = nlohmann::json;
using namespace std;
struct MyArgs : public argparse::Args {
string& address = kwarg("a", "Address");
int& clientPort = kwarg("c", "Client port");
int& backendPort = kwarg("b", "Backend port");
string& gameMasterPassword = kwarg("p", "Game master password");
string& blueCommanderPassword = kwarg("bp", "Blue commander password");
string& redCommanderPassword = kwarg("rp", "Red commander password");
};
void SetStdinEcho(bool enable = true)
{
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode;
GetConsoleMode(hStdin, &mode);
if (!enable)
mode &= ~ENABLE_ECHO_INPUT;
else
mode |= ENABLE_ECHO_INPUT;
SetConsoleMode(hStdin, mode);
}
int main(int argc, char* argv[])
{
cout << "DCS Olympus configurator v1.0.0" << endl << endl << endl;
SHA256 sha256;
std::ifstream f("olympus.json");
json data = json::parse(f);
string address = data["server"]["address"];
int clientPort = data["client"]["port"];
int backendPort = data["server"]["port"];
string gameMasterPassword = data["authentication"]["gameMasterPassword"];
string blueCommanderPassword = data["authentication"]["blueCommanderPassword"];
string redCommanderPassword = data["authentication"]["redCommanderPassword"];
if (argc > 1) {
auto args = argparse::parse<MyArgs>(argc, argv);
args.print(); // prints all variables
address = args.address;
clientPort = args.clientPort;
backendPort = args.backendPort;
gameMasterPassword = sha256(args.gameMasterPassword);
blueCommanderPassword = sha256(args.blueCommanderPassword);
redCommanderPassword = sha256(args.redCommanderPassword);
} else {
cout << "No arguments provided, running in interactive mode" << endl;
SetStdinEcho(true);
string newValue;
cout << "Insert a new address or press Enter to keep current value: " << address << endl << ">";
getline(cin, newValue);
if (newValue != "")
address = newValue;
while (true) {
cout << "Insert a new client port or press Enter to keep current value: " << clientPort << endl << ">";
getline(cin, newValue);
if (newValue != "") {
try {
clientPort = stoi(newValue);
break;
}
catch (...) {
cout << "Insert a valid integer number" << endl;
}
}
else {
break;
}
}
while (true) {
cout << "Insert a new backend port or press Enter to keep current value: " << backendPort << endl << ">";
getline(cin, newValue);
if (newValue != "") {
try {
backendPort = stoi(newValue);
break;
}
catch (...) {
cout << "Insert a valid integer number" << endl;
}
}
else {
break;
}
};
SetStdinEcho(false);
while (true) {
cout << "Insert a new Game Master password" << endl << ">";
getline(cin, newValue);
cout << endl;
if (newValue != "") {
gameMasterPassword = sha256(newValue);
break;
}
else {
cout << "Value can't be empty" << endl;
}
}
while (true) {
cout << "Insert a new Blue Commander password" << endl << ">";
getline(cin, newValue);
cout << endl;
if (newValue != "") {
blueCommanderPassword = sha256(newValue);
break;
}
else {
cout << "Value can't be empty" << endl;
}
}
while (true) {
cout << "Insert a new Red Commander password" << endl << ">";
getline(cin, newValue);
cout << endl;
if (newValue != "") {
redCommanderPassword = sha256(newValue);
break;
}
else {
cout << "Value can't be empty" << endl;
}
}
}
data["server"]["address"] = address;
data["server"]["port"] = backendPort;
data["authentication"]["gameMasterPassword"] = gameMasterPassword;
data["authentication"]["blueCommanderPassword"] = blueCommanderPassword;
data["authentication"]["redCommanderPassword"] = redCommanderPassword;
data["client"]["port"] = clientPort;
std::ofstream o("olympus.json");
o << std::setw(4) << data << std::endl;
cout << endl;
cout << "Configuration updated successfully, bye!" << endl;
this_thread::sleep_for(chrono::milliseconds(2000));
}

View File

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{ff484257-f6d1-43e9-b6ba-a892b86e8963}</ProjectGuid>
<RootNamespace>configurator</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>third-party\hash-library\include;third-party</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>third-party\hash-library\include;third-party</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="configurator.cpp" />
<ClCompile Include="third-party\hash-library\src\sha256.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="argparse.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="configurator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="third-party\hash-library\src\sha256.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="argparse.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,586 @@
#pragma once
//
// @author : Morris Franken
// https://github.com/morrisfranken/argparse
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#include <cctype> // for isdigit, tolower
#include <sstream>
#include <cstdlib> // for size_t, exit
#include <algorithm> // for max, transform, copy, min
#include <iomanip> // for operator<<, setw
#include <iostream> // for operator<<, basic_ostream, endl, ostream
#include <iterator> // for ostream_iterator
#include <map> // for operator!=, map, _Rb_tree_iterator
#include <memory> // for allocator, shared_ptr, __shared_ptr_ac...
#include <optional> // for optional, nullopt
#include <stdexcept> // for runtime_error, invalid_argument
#include <filesystem> // for getting program_name from path
#include <string> // for string, operator+, basic_string, char_...
#include <type_traits> // for declval, false_type, true_type, is_enum
#include <utility> // for move, pair
#include <vector> // for vector
#if __has_include(<magic_enum.hpp>)
#include <magic_enum.hpp> // for enum_entries
#define HAS_MAGIC_ENUM
#endif
#define ARGPARSE_VERSION 4
namespace argparse {
class Args;
using std::cout, std::cerr, std::endl, std::setw, std::size_t;
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A> struct is_vector<std::vector<T, A>> : public std::true_type {};
template<typename T> struct is_optional : public std::false_type {};
template<typename T> struct is_optional<std::optional<T>> : public std::true_type {};
template<typename T> struct is_shared_ptr : public std::false_type {};
template<typename T> struct is_shared_ptr<std::shared_ptr<T>> : public std::true_type {};
template <typename, typename = void> struct has_ostream_operator : std::false_type {};
template <typename T> struct has_ostream_operator<T, decltype(void(std::declval<std::ostream&>() << std::declval<const T&>()))> : std::true_type {};
inline std::string bold(const std::string& input_str) {
#ifdef _WIN32
return input_str; // no bold for windows
#else
return "\033[1m" + input_str + "\033[0m";
#endif
}
template<typename T> std::string toString(const T &v) {
if constexpr (has_ostream_operator<T>::value) {
return static_cast<std::ostringstream &&>((std::ostringstream() << std::boolalpha << v)).str(); // https://github.com/stan-dev/math/issues/590#issuecomment-550122627
} else {
return "unknown";
}
}
std::vector<std::string> inline split(const std::string &str) {
std::vector<std::string> splits;
std::stringstream ss(str);
std::string key;
while (std::getline(ss, key, ',')) {
if (!key.empty() && key.back() == '\0')
key.pop_back(); // last variables contain a '\0', which is unexpected when comparing to raw string, e.g. value == "test" will fail when the last character is '\0'. Therefore we can remove it
splits.emplace_back(std::move(key));
}
return splits;
}
template <typename T> std::string to_lower(const T &str_) { // both std::string and std::basic_string_view<char> (for magic_enum) are using to_lower
std::string str(str_.size(), '\0');
std::transform(str_.begin(), str_.end(), str.begin(), ::tolower);
return str;
}
template<typename T> inline T get(const std::string &v);
template<> inline std::string get(const std::string &v) { return v; }
template<> inline char get(const std::string &v) { return v.empty()? throw std::invalid_argument("empty string") : v.size() > 1? v.substr(0,2) == "0x"? (char)std::stoul(v, nullptr, 16) : (char)std::stoi(v) : v[0]; }
template<> inline int get(const std::string &v) { return std::stoi(v); }
template<> inline short get(const std::string &v) { return std::stoi(v); }
template<> inline long get(const std::string &v) { return std::stol(v); }
template<> inline bool get(const std::string &v) { return to_lower(v) == "true" || v == "1"; }
template<> inline float get(const std::string &v) { return std::stof(v); }
template<> inline double get(const std::string &v) { return std::stod(v); }
template<> inline unsigned char get(const std::string &v) { return get<char>(v); }
template<> inline unsigned int get(const std::string &v) { return std::stoul(v); }
template<> inline unsigned short get(const std::string &v) { return std::stoul(v); }
template<> inline unsigned long get(const std::string &v) { return std::stoul(v); }
template<typename T> inline T get(const std::string &v) { // remaining types
if constexpr (is_vector<T>::value) {
const std::vector<std::string> splitted = split(v);
T res(splitted.size());
if (!v.empty())
std::transform (splitted.begin(), splitted.end(), res.begin(), get<typename T::value_type>);
return res;
} else if constexpr (std::is_pointer<T>::value) {
return new typename std::remove_pointer<T>::type(get<typename std::remove_pointer<T>::type>(v));
} else if constexpr (is_shared_ptr<T>::value) {
return std::make_shared<typename T::element_type>(get<typename T::element_type>(v));
} else if constexpr (is_optional<T>::value) {
return get<typename T::value_type>(v);
} else if constexpr (std::is_enum<T>::value) { // case-insensitive enum conversion
#ifdef HAS_MAGIC_ENUM
constexpr auto& enum_entries = magic_enum::enum_entries<T>();
const std::string lower_str = to_lower(v);
for (const auto &[value, name] : enum_entries) {
if (to_lower(name) == lower_str)
return value;
}
std::string error = "enum is only accepting [";
for (size_t i = 0; i < enum_entries.size(); i++)
error += (i==0? "" : ", ") + to_lower(enum_entries[i].second);
error += "]";
throw std::runtime_error(error);
#else
throw std::runtime_error("Enum not supported, please install magic_enum (https://github.com/Neargye/magic_enum)");
#endif
} else {
return T(v);
}
}
struct ConvertBase {
virtual ~ConvertBase() = default;
virtual void convert(const std::string &v) = 0;
virtual void set_default(const std::unique_ptr<ConvertBase> &default_value, const std::string &default_string) = 0;
[[nodiscard]] virtual size_t get_type_id() const = 0;
[[nodiscard]] virtual std::string get_allowed_entries() const = 0;
};
template <typename T> struct ConvertType : public ConvertBase {
T data;
~ConvertType() override = default;
ConvertType() : ConvertBase() {};
explicit ConvertType(const T &value) : ConvertBase(), data(value) {};
void convert(const std::string &v) override {
data = get<T>(v);
}
void set_default(const std::unique_ptr<ConvertBase> &default_value, const std::string &default_string) override {
if (this->get_type_id() == default_value->get_type_id()) // When the types do not match exactly. resort to string conversion
data = ((ConvertType<T>*)(default_value.get()))->data;
else
data = get<T>(default_string);
}
[[nodiscard]] size_t get_type_id() const override {
return typeid(T).hash_code();
}
[[nodiscard]] std::string get_allowed_entries() const override {
std::stringstream ss;
#ifdef HAS_MAGIC_ENUM
if constexpr (std::is_enum<T>::value) {
for (const auto &[value, name] : magic_enum::enum_entries<T>()) {
ss << to_lower(name) << ", ";
}
}
#endif
return ss.str();
}
};
struct Entry {
enum ARG_TYPE {ARG, KWARG, FLAG} type;
Entry(ARG_TYPE type, const std::string& key, std::string help, std::optional<std::string> implicit_value=std::nullopt) :
type(type),
keys_(split(key)),
help(std::move(help)),
implicit_value_(std::move(implicit_value)) {
}
// Allow both string inputs and direct-type inputs. Where a string-input will be converted like it would when using the commandline, and the direct approach is to simply use the value provided.
template <typename T> Entry &set_default(const T &default_value) {
this->default_str_ = toString(default_value);
if constexpr (!(std::is_array<T>::value || std::is_same<typename std::remove_all_extents<T>::type, char>::value)) {
data_default = std::make_unique<ConvertType<T>>(default_value);
}
return *this;
}
Entry &multi_argument() {
_is_multi_argument = true;
return *this;
}
// Magically convert the value string to the requested type
template <typename T> operator T&() {
// Automatically set the default to nullptr for pointer types and empty for optional types
if constexpr (is_optional<T>::value || std::is_pointer<T>::value || is_shared_ptr<T>::value) {
if (!default_str_.has_value()) {
default_str_ = "none";
if constexpr(is_optional<T>::value) {
data_default = std::make_unique<ConvertType<T>> (T{std::nullopt});
} else {
data_default = std::make_unique<ConvertType<T>> ((T) nullptr);
}
}
}
datap = std::make_unique<ConvertType<T>>();
return ((ConvertType<T>*)(datap.get()))->data;
}
// Force an ambiguous error when not using a reference.
template <typename T> operator T() {} // When you get here because you received an error, make sure all parameters of argparse are references (e.g. with `&`)
private:
std::vector<std::string> keys_;
std::string help;
std::optional<std::string> value_;
std::optional<std::string> implicit_value_;
std::optional<std::string> default_str_;
std::string error;
std::unique_ptr<ConvertBase> datap;
std::unique_ptr<ConvertBase> data_default;
bool _is_multi_argument = false;
bool is_set_by_user = true;
[[nodiscard]] std::string _get_keys() const {
std::stringstream ss;
for (size_t i = 0; i < keys_.size(); i++)
ss << (i? "," : "") << (type == ARG? "" : (keys_[i].size() > 1 ? "--" : "-")) + keys_[i];
return ss.str();
}
void _convert(const std::string &value) {
try {
this->value_ = value;
datap->convert(value);
} catch (const std::invalid_argument &e) {
error = "Invalid argument, could not convert \"" + value + "\" for " + _get_keys() + " (" + help + ")";
} catch (const std::runtime_error &e) {
error = "Invalid argument \"" + value + "\" for " + _get_keys() + " (" + help + "). Error: " + e.what();
}
}
void _apply_default() {
is_set_by_user = false;
if (data_default != nullptr) {
value_ = *default_str_; // for printing
datap->set_default(data_default, *default_str_);
} else if (default_str_.has_value()) { // in cases where a string is provided to the `set_default` function
_convert(default_str_.value());
} else {
error = "Argument missing: " + _get_keys() + " (" + help + ")";
}
}
[[nodiscard]] std::string info() const {
const std::string allowed_entries = datap->get_allowed_entries();
const std::string default_value = default_str_.has_value() ? "default: " + *default_str_ : "required";
const std::string implicit_value = implicit_value_.has_value() ? "implicit: \"" + *implicit_value_ + "\", ": "";
const std::string allowed_value = !allowed_entries.empty()? "allowed: <" + allowed_entries.substr(0, allowed_entries.size()-2) + ">, ": "";
return " [" + allowed_value + implicit_value + default_value + "]";
}
friend class Args;
};
struct SubcommandEntry {
std::shared_ptr<Args> subargs;
std::string subcommand_name;
explicit SubcommandEntry(std::string subcommand_name) : subcommand_name(std::move(subcommand_name)) {}
template<typename T> operator T &() {
static_assert(std::is_base_of_v<Args, T>, "Subcommand type must be a derivative of argparse::Args");
std::shared_ptr<T> res = std::make_shared<T>();
res->program_name = subcommand_name;
subargs = res;
return *(T*)(subargs.get());
}
// Force an ambiguous error when not using a reference.
template <typename T> operator T() {} // When you get here because you received an error, make sure all parameters of argparse are references (e.g. with `&`)
};
class Args {
private:
size_t _arg_idx = 0;
std::vector<std::string> params;
std::vector<std::shared_ptr<Entry>> all_entries;
std::map<std::string, std::shared_ptr<Entry>> kwarg_entries;
std::vector<std::shared_ptr<Entry>> arg_entries;
std::map<std::string, std::shared_ptr<SubcommandEntry>> subcommand_entries;
public:
std::string program_name;
bool is_valid = false;
virtual ~Args() = default;
/* Add a positional argument, the order in which it is defined equals the order in which they are being read.
* help : Description of the variable
*
* Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()`
*/
Entry &arg(const std::string &help) {
return arg("arg_" + std::to_string(_arg_idx), help);
}
/* Add a *named* positional argument, the order in which it is defined equals the order in which they are being read.
* key : The name of the argument, otherwise arg_<position> will be used
* help : Description of the variable
*
* Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()`
*/
Entry &arg(const std::string& key, const std::string &help) {
std::shared_ptr<Entry> entry = std::make_shared<Entry>(Entry::ARG, key, help);
// Increasing _arg_idx, so that arg2 will be arg_2, irregardless of whether it is preceded by other positional arguments
_arg_idx++;
arg_entries.emplace_back(entry);
all_entries.emplace_back(entry);
return *entry;
}
/* Add a Key-Worded argument that takes a variable.
* key : A comma-separated string, e.g. "k,key", which denotes the short (-k) and long(--key) keys_
* help : Description of the variable
* implicit_value : Implicit values are used when no value is provided.
*
* Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()`
*/
Entry &kwarg(const std::string &key, const std::string &help, const std::optional<std::string>& implicit_value=std::nullopt) {
std::shared_ptr<Entry> entry = std::make_shared<Entry>(Entry::KWARG, key, help, implicit_value);
all_entries.emplace_back(entry);
for (const std::string &k : entry->keys_) {
kwarg_entries[k] = entry;
}
return *entry;
}
/* Add a flag which will be false by default.
* key : A comma-separated string, e.g. "k,key", which denotes the short (-k) and long(--key) keys_
* help : Description of the variable
*
* Returns reference to Entry like kwarg
*/
Entry &flag(const std::string &key, const std::string &help) {
return kwarg(key, help, "true").set_default<bool>(false);
}
/* Add a a subcommand
* command : name of the subcommand, e.g. 'commit', if you wish to implement a function like 'git commit'
*
* Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()`
* Expected type *Must* be an std::shared_ptr of derivative of the argparse::Args class
*/
SubcommandEntry &subcommand(const std::string &command) {
std::shared_ptr<SubcommandEntry> entry = std::make_shared<SubcommandEntry>(command);
subcommand_entries[command] = entry;
return *entry;
}
virtual void welcome() {} // Allow to overwrite the `welcome` function to add a welcome-message to the help output
virtual void help() {
welcome();
cout << "Usage: " << program_name << " ";
for (const auto &entry : arg_entries)
cout << entry->keys_[0] << ' ';
cout << " [options...]";
if (!subcommand_entries.empty()) {
cout << " [SUBCOMMAND: ";
for (const auto &[subcommand, subentry]: subcommand_entries) {
cout << subcommand << ", ";
}
cout << "]";
}
cout << endl;
for (const auto &entry : arg_entries) {
cout << setw(17) << entry->keys_[0] << " : " << entry->help << entry->info() << endl;
}
cout << endl << "Options:" << endl;
for (const auto &entry : all_entries) {
if (entry->type != Entry::ARG) {
cout << setw(17) << entry->_get_keys() << " : " << entry->help << entry->info() << endl;
}
}
for (const auto &[subcommand, subentry] : subcommand_entries) {
cout << endl << endl << bold("Subcommand: ") << bold(subcommand) << endl;
subentry->subargs->help();
}
}
void validate(const bool &raise_on_error) {
for (const auto &entry : all_entries) {
if (!entry->error.empty()) {
if (raise_on_error) {
throw std::runtime_error(entry->error);
} else {
std::cerr << entry->error << std::endl;
exit(-1);
}
}
}
}
/* parse all parameters and also check for the help_flag which was set in this constructor
* Upon error, it will print the error and exit immediately if validation_action is ValidationAction::EXIT_ON_ERROR
*/
void parse(int argc, const char* const *argv, const bool &raise_on_error) {
auto parse_subcommands = [&]() -> int {
for (int i = 1; i < argc; i++) {
for (auto &[subcommand, subentry] : subcommand_entries) {
if (subcommand == argv[i]) {
subentry->subargs->parse(argc - i, argv + i, raise_on_error);
return i;
}
}
}
return argc;
};
argc = parse_subcommands(); // argc_ is the number of arguments that should be parsed after the subcommand has finished parsing
program_name = std::filesystem::path(argv[0]).stem().string();
params = std::vector<std::string>(argv + 1, argv + argc);
bool& _help = flag("?,help", "print help");
auto is_value = [&](const size_t &i) -> bool {
return params.size() > i && (params[i][0] != '-' || (params[i].size() > 1 && std::isdigit(params[i][1]))); // check for number to not accidentally mark negative numbers as non-parameter
};
auto parse_param = [&](size_t &i, const std::string &key, const bool is_short, const std::optional<std::string> &equal_value=std::nullopt) {
auto itt = kwarg_entries.find(key);
if (itt != kwarg_entries.end()) {
auto &entry = itt->second;
if (equal_value.has_value()) {
entry->_convert(equal_value.value());
} else if (entry->implicit_value_.has_value()) {
entry->_convert(*entry->implicit_value_);
} else if (!is_short) { // short values are not allowed to look ahead for the next parameter
if (is_value(i + 1)) {
std::string value = params[++i];
if (entry->_is_multi_argument) {
while (is_value(i + 1))
value += "," + params[++i];
}
entry->_convert(value);
} else if (entry->_is_multi_argument) {
entry->_convert(""); // for multiargument parameters, return an empty vector when not passing any more values
} else {
entry->error = "No value provided for: " + key;
}
} else {
entry->error = "No value provided for: " + key;
}
} else {
cerr << "unrecognised commandline argument: " << key << endl;
}
};
auto add_param = [&](size_t &i, const size_t &start) {
size_t eq_idx = params[i].find('='); // check if value was passed using the '=' sign
if (eq_idx != std::string::npos) { // key/value from = notation
std::string key = params[i].substr(start, eq_idx - start);
std::string value = params[i].substr(eq_idx + 1);
parse_param(i, key, false, value);
} else {
std::string key = std::string(params[i].substr(start));
parse_param(i, key, false);
}
};
std::vector<std::string> arguments_flat;
for (size_t i = 0; i < params.size(); i++) {
if (!is_value(i)) {
if (params[i].size() > 1 && params[i][1] == '-') { // long --
add_param(i, 2);
} else { // short -
const size_t j_end = std::min(params[i].size(), params[i].find('=')) - 1;
for (size_t j = 1; j < j_end; j++) { // add possible other flags
const std::string key = std::string(1, params[i][j]);
parse_param(i, key, true);
}
add_param(i, j_end);
}
} else {
arguments_flat.emplace_back(params[i]);
}
}
// Parse all the positional arguments, making sure multi_argument positional arguments are processed last to enable arguments afterwards
size_t arg_i = 0;
for (; arg_i < arg_entries.size() && !arg_entries[arg_i]->_is_multi_argument; arg_i++) { // iterate over positional arguments until a multi-argument is found
if (arg_i < arguments_flat.size())
arg_entries[arg_i]->_convert(arguments_flat[arg_i]);
}
size_t arg_j = 1;
for (size_t j_end = arg_entries.size() - arg_i; arg_j <= j_end; arg_j++) { // iterate from back to front, to ensure non-multi-arguments in the front and back are given preference
size_t flat_idx = arguments_flat.size() - arg_j;
if (flat_idx < arguments_flat.size() && flat_idx >= arg_i) {
if (arg_entries[arg_entries.size() - arg_j]->_is_multi_argument) {
std::stringstream s; // Combine multiple arguments into 1 comma-separated string for parsing
copy(&arguments_flat[arg_i],&arguments_flat[flat_idx] + 1, std::ostream_iterator<std::string>(s,","));
std::string value = s.str();
value.back() = '\0'; // remove trailing ','
arg_entries[arg_i]->_convert(value);
} else {
arg_entries[arg_entries.size() - arg_j]->_convert(arguments_flat[flat_idx]);
}
}
}
// try to apply default values for arguments which have not been set
for (const auto &entry : all_entries) {
if (!entry->value_.has_value()) {
entry->_apply_default();
}
}
if (_help) {
help();
exit(0);
}
validate(raise_on_error);
is_valid = true;
}
void print() const {
for (const auto &entry : all_entries) {
std::string snip = entry->type == Entry::ARG ? "(" + (entry->help.size() > 10 ? entry->help.substr(0, 7) + "..." : entry->help) + ")" : "";
cout << setw(21) << entry->_get_keys() + snip << " : " << (entry->is_set_by_user? bold(entry->value_.value_or("null")) : entry->value_.value_or("null")) << endl;
}
for (const auto &[subcommand, subentry] : subcommand_entries) {
if (subentry->subargs->is_valid) {
cout << endl << "--- Subcommand: " << subcommand << endl;
subentry->subargs->print();
}
}
}
virtual int run() {return 0;} // For automatically running subcommands
int run_subcommands() {
for (const auto &[subcommand, subentry] : subcommand_entries) {
if (subentry->subargs->is_valid) {
return subentry->subargs->run();
}
}
std::cerr << "No subcommand provided" << std::endl;
help();
return -1;
}
};
template <typename T> T parse(int argc, const char* const *argv, const bool &raise_on_error=false) {
T args = T();
args.parse(argc, argv, raise_on_error);
return args;
}
}

View File

@ -0,0 +1,10 @@
zlib License
Copyright (c) 2014,2015 Stephan Brumme
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@ -0,0 +1,69 @@
// //////////////////////////////////////////////////////////
// crc32.h
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
//#include "hash.h"
#include <string>
// define fixed size integer types
#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int32 uint32_t;
#else
// GCC
#include <stdint.h>
#endif
/// compute CRC32 hash, based on Intel's Slicing-by-8 algorithm
/** Usage:
CRC32 crc32;
std::string myHash = crc32("Hello World"); // std::string
std::string myHash2 = crc32("How are you", 11); // arbitrary data, 11 bytes
// or in a streaming fashion:
CRC32 crc32;
while (more data available)
crc32.add(pointer to fresh data, number of new bytes);
std::string myHash3 = crc32.getHash();
Note:
You can find code for the faster Slicing-by-16 algorithm on my website, too:
http://create.stephan-brumme.com/crc32/
Its unrolled version is about twice as fast but its look-up table doubled in size as well.
*/
class CRC32 //: public Hash
{
public:
/// hash is 4 bytes long
enum { HashBytes = 4 };
/// same as reset()
CRC32();
/// compute CRC32 of a memory block
std::string operator()(const void* data, size_t numBytes);
/// compute CRC32 of a string, excluding final zero
std::string operator()(const std::string& text);
/// add arbitrary number of bytes
void add(const void* data, size_t numBytes);
/// return latest hash as 8 hex characters
std::string getHash();
/// return latest hash as bytes
void getHash(unsigned char buffer[HashBytes]);
/// restart
void reset();
private:
/// hash
uint32_t m_hash;
};

View File

@ -0,0 +1,28 @@
// //////////////////////////////////////////////////////////
// hash.h
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
#include <string>
/// abstract base class
class Hash
{
public:
/// compute hash of a memory block
virtual std::string operator()(const void* data, size_t numBytes) = 0;
/// compute hash of a string, excluding final zero
virtual std::string operator()(const std::string& text) = 0;
/// add arbitrary number of bytes
virtual void add(const void* data, size_t numBytes) = 0;
/// return latest hash as hex characters
virtual std::string getHash() = 0;
/// restart
virtual void reset() = 0;
};

View File

@ -0,0 +1,83 @@
// //////////////////////////////////////////////////////////
// hmac.h
// Copyright (c) 2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
// based on http://tools.ietf.org/html/rfc2104
// see also http://en.wikipedia.org/wiki/Hash-based_message_authentication_code
/** Usage:
std::string msg = "The quick brown fox jumps over the lazy dog";
std::string key = "key";
std::string md5hmac = hmac< MD5 >(msg, key);
std::string sha1hmac = hmac< SHA1 >(msg, key);
std::string sha2hmac = hmac<SHA256>(msg, key);
Note:
To keep my code simple, HMAC computation currently needs the whole message at once.
This is in contrast to the hashes MD5, SHA1, etc. where an add() method is available
for incremental computation.
You can use any hash for HMAC as long as it provides:
- constant HashMethod::BlockSize (typically 64)
- constant HashMethod::HashBytes (length of hash in bytes, e.g. 20 for SHA1)
- HashMethod::add(buffer, bufferSize)
- HashMethod::getHash(unsigned char buffer[HashMethod::BlockSize])
*/
#include <string>
#include <cstring> // memcpy
/// compute HMAC hash of data and key using MD5, SHA1 or SHA256
template <typename HashMethod>
std::string hmac(const void* data, size_t numDataBytes, const void* key, size_t numKeyBytes)
{
// initialize key with zeros
unsigned char usedKey[HashMethod::BlockSize] = {0};
// adjust length of key: must contain exactly blockSize bytes
if (numKeyBytes <= HashMethod::BlockSize)
{
// copy key
memcpy(usedKey, key, numKeyBytes);
}
else
{
// shorten key: usedKey = hashed(key)
HashMethod keyHasher;
keyHasher.add(key, numKeyBytes);
keyHasher.getHash(usedKey);
}
// create initial XOR padding
for (size_t i = 0; i < HashMethod::BlockSize; i++)
usedKey[i] ^= 0x36;
// inside = hash((usedKey ^ 0x36) + data)
unsigned char inside[HashMethod::HashBytes];
HashMethod insideHasher;
insideHasher.add(usedKey, HashMethod::BlockSize);
insideHasher.add(data, numDataBytes);
insideHasher.getHash(inside);
// undo usedKey's previous 0x36 XORing and apply a XOR by 0x5C
for (size_t i = 0; i < HashMethod::BlockSize; i++)
usedKey[i] ^= 0x5C ^ 0x36;
// hash((usedKey ^ 0x5C) + hash((usedKey ^ 0x36) + data))
HashMethod finalHasher;
finalHasher.add(usedKey, HashMethod::BlockSize);
finalHasher.add(inside, HashMethod::HashBytes);
return finalHasher.getHash();
}
/// convenience function for std::string
template <typename HashMethod>
std::string hmac(const std::string& data, const std::string& key)
{
return hmac<HashMethod>(data.c_str(), data.size(), key.c_str(), key.size());
}

View File

@ -0,0 +1,81 @@
// //////////////////////////////////////////////////////////
// keccak.h
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
//#include "hash.h"
#include <string>
// define fixed size integer types
#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include <stdint.h>
#endif
/// compute Keccak hash (designated SHA3)
/** Usage:
Keccak keccak;
std::string myHash = keccak("Hello World"); // std::string
std::string myHash2 = keccak("How are you", 11); // arbitrary data, 11 bytes
// or in a streaming fashion:
Keccak keccak;
while (more data available)
keccak.add(pointer to fresh data, number of new bytes);
std::string myHash3 = keccak.getHash();
*/
class Keccak //: public Hash
{
public:
/// algorithm variants
enum Bits { Keccak224 = 224, Keccak256 = 256, Keccak384 = 384, Keccak512 = 512 };
/// same as reset()
explicit Keccak(Bits bits = Keccak256);
/// compute hash of a memory block
std::string operator()(const void* data, size_t numBytes);
/// compute hash of a string, excluding final zero
std::string operator()(const std::string& text);
/// add arbitrary number of bytes
void add(const void* data, size_t numBytes);
/// return latest hash as hex characters
std::string getHash();
/// restart
void reset();
private:
/// process a full block
void processBlock(const void* data);
/// process everything left in the internal buffer
void processBuffer();
/// 1600 bits, stored as 25x64 bit, BlockSize is no more than 1152 bits (Keccak224)
enum { StateSize = 1600 / (8 * 8),
MaxBlockSize = 200 - 2 * (224 / 8) };
/// hash
uint64_t m_hash[StateSize];
/// size of processed data in bytes
uint64_t m_numBytes;
/// block size (less or equal to MaxBlockSize)
size_t m_blockSize;
/// valid bytes in m_buffer
size_t m_bufferSize;
/// bytes not processed yet
uint8_t m_buffer[MaxBlockSize];
/// variant
Bits m_bits;
};

View File

@ -0,0 +1,78 @@
// //////////////////////////////////////////////////////////
// md5.h
// Copyright (c) 2014 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
//#include "hash.h"
#include <string>
// define fixed size integer types
#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include <stdint.h>
#endif
/// compute MD5 hash
/** Usage:
MD5 md5;
std::string myHash = md5("Hello World"); // std::string
std::string myHash2 = md5("How are you", 11); // arbitrary data, 11 bytes
// or in a streaming fashion:
MD5 md5;
while (more data available)
md5.add(pointer to fresh data, number of new bytes);
std::string myHash3 = md5.getHash();
*/
class MD5 //: public Hash
{
public:
/// split into 64 byte blocks (=> 512 bits), hash is 16 bytes long
enum { BlockSize = 512 / 8, HashBytes = 16 };
/// same as reset()
MD5();
/// compute MD5 of a memory block
std::string operator()(const void* data, size_t numBytes);
/// compute MD5 of a string, excluding final zero
std::string operator()(const std::string& text);
/// add arbitrary number of bytes
void add(const void* data, size_t numBytes);
/// return latest hash as 32 hex characters
std::string getHash();
/// return latest hash as bytes
void getHash(unsigned char buffer[HashBytes]);
/// restart
void reset();
private:
/// process 64 bytes
void processBlock(const void* data);
/// process everything left in the internal buffer
void processBuffer();
/// size of processed data in bytes
uint64_t m_numBytes;
/// valid bytes in m_buffer
size_t m_bufferSize;
/// bytes not processed yet
uint8_t m_buffer[BlockSize];
enum { HashValues = HashBytes / 4 };
/// hash, stored as integers
uint32_t m_hash[HashValues];
};

View File

@ -0,0 +1,78 @@
// //////////////////////////////////////////////////////////
// sha1.h
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
//#include "hash.h"
#include <string>
// define fixed size integer types
#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include <stdint.h>
#endif
/// compute SHA1 hash
/** Usage:
SHA1 sha1;
std::string myHash = sha1("Hello World"); // std::string
std::string myHash2 = sha1("How are you", 11); // arbitrary data, 11 bytes
// or in a streaming fashion:
SHA1 sha1;
while (more data available)
sha1.add(pointer to fresh data, number of new bytes);
std::string myHash3 = sha1.getHash();
*/
class SHA1 //: public Hash
{
public:
/// split into 64 byte blocks (=> 512 bits), hash is 20 bytes long
enum { BlockSize = 512 / 8, HashBytes = 20 };
/// same as reset()
SHA1();
/// compute SHA1 of a memory block
std::string operator()(const void* data, size_t numBytes);
/// compute SHA1 of a string, excluding final zero
std::string operator()(const std::string& text);
/// add arbitrary number of bytes
void add(const void* data, size_t numBytes);
/// return latest hash as 40 hex characters
std::string getHash();
/// return latest hash as bytes
void getHash(unsigned char buffer[HashBytes]);
/// restart
void reset();
private:
/// process 64 bytes
void processBlock(const void* data);
/// process everything left in the internal buffer
void processBuffer();
/// size of processed data in bytes
uint64_t m_numBytes;
/// valid bytes in m_buffer
size_t m_bufferSize;
/// bytes not processed yet
uint8_t m_buffer[BlockSize];
enum { HashValues = HashBytes / 4 };
/// hash, stored as integers
uint32_t m_hash[HashValues];
};

View File

@ -0,0 +1,78 @@
// //////////////////////////////////////////////////////////
// sha256.h
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
//#include "hash.h"
#include <string>
// define fixed size integer types
#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include <stdint.h>
#endif
/// compute SHA256 hash
/** Usage:
SHA256 sha256;
std::string myHash = sha256("Hello World"); // std::string
std::string myHash2 = sha256("How are you", 11); // arbitrary data, 11 bytes
// or in a streaming fashion:
SHA256 sha256;
while (more data available)
sha256.add(pointer to fresh data, number of new bytes);
std::string myHash3 = sha256.getHash();
*/
class SHA256 //: public Hash
{
public:
/// split into 64 byte blocks (=> 512 bits), hash is 32 bytes long
enum { BlockSize = 512 / 8, HashBytes = 32 };
/// same as reset()
SHA256();
/// compute SHA256 of a memory block
std::string operator()(const void* data, size_t numBytes);
/// compute SHA256 of a string, excluding final zero
std::string operator()(const std::string& text);
/// add arbitrary number of bytes
void add(const void* data, size_t numBytes);
/// return latest hash as 64 hex characters
std::string getHash();
/// return latest hash as bytes
void getHash(unsigned char buffer[HashBytes]);
/// restart
void reset();
private:
/// process 64 bytes
void processBlock(const void* data);
/// process everything left in the internal buffer
void processBuffer();
/// size of processed data in bytes
uint64_t m_numBytes;
/// valid bytes in m_buffer
size_t m_bufferSize;
/// bytes not processed yet
uint8_t m_buffer[BlockSize];
enum { HashValues = HashBytes / 4 };
/// hash, stored as integers
uint32_t m_hash[HashValues];
};

View File

@ -0,0 +1,81 @@
// //////////////////////////////////////////////////////////
// sha3.h
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#pragma once
//#include "hash.h"
#include <string>
// define fixed size integer types
#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include <stdint.h>
#endif
/// compute SHA3 hash
/** Usage:
SHA3 sha3;
std::string myHash = sha3("Hello World"); // std::string
std::string myHash2 = sha3("How are you", 11); // arbitrary data, 11 bytes
// or in a streaming fashion:
SHA3 sha3;
while (more data available)
sha3.add(pointer to fresh data, number of new bytes);
std::string myHash3 = sha3.getHash();
*/
class SHA3 //: public Hash
{
public:
/// algorithm variants
enum Bits { Bits224 = 224, Bits256 = 256, Bits384 = 384, Bits512 = 512 };
/// same as reset()
explicit SHA3(Bits bits = Bits256);
/// compute hash of a memory block
std::string operator()(const void* data, size_t numBytes);
/// compute hash of a string, excluding final zero
std::string operator()(const std::string& text);
/// add arbitrary number of bytes
void add(const void* data, size_t numBytes);
/// return latest hash as hex characters
std::string getHash();
/// restart
void reset();
private:
/// process a full block
void processBlock(const void* data);
/// process everything left in the internal buffer
void processBuffer();
/// 1600 bits, stored as 25x64 bit, BlockSize is no more than 1152 bits (Keccak224)
enum { StateSize = 1600 / (8 * 8),
MaxBlockSize = 200 - 2 * (224 / 8) };
/// hash
uint64_t m_hash[StateSize];
/// size of processed data in bytes
uint64_t m_numBytes;
/// block size (less or equal to MaxBlockSize)
size_t m_blockSize;
/// valid bytes in m_buffer
size_t m_bufferSize;
/// bytes not processed yet
uint8_t m_buffer[MaxBlockSize];
/// variant
Bits m_bits;
};

View File

@ -0,0 +1,51 @@
# Portable C++ Hashing Library
This is a mirror of my library hosted at https://create.stephan-brumme.com/hash-library/
In a nutshell:
- computes CRC32, MD5, SHA1 and SHA256 (most common member of the SHA2 functions), Keccak and its SHA3 sibling
- optional HMAC (keyed-hash message authentication code)
- no external dependencies, small code size
- can work chunk-wise (for example when reading streams block-by-block)
- portable: supports Windows and Linux, tested on Little Endian and Big Endian CPUs
- roughly as fast as Linux core hashing functions
- open source, zlib license
You can find code examples, benchmarks and much more on my website https://create.stephan-brumme.com/hash-library/
# How to use
This example computes SHA256 hashes but the API is more or less identical for all hash algorithms:
``` cpp
// SHA2 test program
#include "sha256.h"
#include <iostream> // for std::cout only, not needed for hashing library
int main(int, char**)
{
// create a new hashing object
SHA256 sha256;
// hashing an std::string
std::cout << sha256("Hello World") << std::endl;
// => a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
// hashing a buffer of bytes
const char* buffer = "How are you";
std::cout << sha256(buffer, 11) << std::endl;
// => 9c7d5b046878838da72e40ceb3179580958df544b240869b80d0275cc07209cc
// or in a streaming fashion (re-use "How are you")
SHA256 sha256stream;
const char* url = "create.stephan-brumme.com"; // 25 bytes
int step = 5;
for (int i = 0; i < 25; i += step)
sha256stream.add(url + i, step); // add five bytes at a time
std::cout << sha256stream.getHash() << std::endl;
// => 82aa771f1183c52f973c798c9243a1c73833ea40961c73e55e12430ec77b69f6
return 0;
}
```

View File

@ -0,0 +1,411 @@
// //////////////////////////////////////////////////////////
// sha256.cpp
// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
// see http://create.stephan-brumme.com/disclaimer.html
//
#include "sha256.h"
// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN
#ifndef _MSC_VER
#include <endian.h>
#endif
/// same as reset()
SHA256::SHA256()
{
reset();
}
/// restart
void SHA256::reset()
{
m_numBytes = 0;
m_bufferSize = 0;
// according to RFC 1321
m_hash[0] = 0x6a09e667;
m_hash[1] = 0xbb67ae85;
m_hash[2] = 0x3c6ef372;
m_hash[3] = 0xa54ff53a;
m_hash[4] = 0x510e527f;
m_hash[5] = 0x9b05688c;
m_hash[6] = 0x1f83d9ab;
m_hash[7] = 0x5be0cd19;
}
namespace
{
inline uint32_t rotate(uint32_t a, uint32_t c)
{
return (a >> c) | (a << (32 - c));
}
inline uint32_t swap(uint32_t x)
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_bswap32(x);
#endif
#ifdef MSC_VER
return _byteswap_ulong(x);
#endif
return (x >> 24) |
((x >> 8) & 0x0000FF00) |
((x << 8) & 0x00FF0000) |
(x << 24);
}
// mix functions for processBlock()
inline uint32_t f1(uint32_t e, uint32_t f, uint32_t g)
{
uint32_t term1 = rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25);
uint32_t term2 = (e & f) ^ (~e & g); //(g ^ (e & (f ^ g)))
return term1 + term2;
}
inline uint32_t f2(uint32_t a, uint32_t b, uint32_t c)
{
uint32_t term1 = rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22);
uint32_t term2 = ((a | b) & c) | (a & b); //(a & (b ^ c)) ^ (b & c);
return term1 + term2;
}
}
/// process 64 bytes
void SHA256::processBlock(const void* data)
{
// get last hash
uint32_t a = m_hash[0];
uint32_t b = m_hash[1];
uint32_t c = m_hash[2];
uint32_t d = m_hash[3];
uint32_t e = m_hash[4];
uint32_t f = m_hash[5];
uint32_t g = m_hash[6];
uint32_t h = m_hash[7];
// data represented as 16x 32-bit words
const uint32_t* input = (uint32_t*) data;
// convert to big endian
uint32_t words[64];
int i;
for (i = 0; i < 16; i++)
#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
words[i] = input[i];
#else
words[i] = swap(input[i]);
#endif
uint32_t x,y; // temporaries
// first round
x = h + f1(e,f,g) + 0x428a2f98 + words[ 0]; y = f2(a,b,c); d += x; h = x + y;
x = g + f1(d,e,f) + 0x71374491 + words[ 1]; y = f2(h,a,b); c += x; g = x + y;
x = f + f1(c,d,e) + 0xb5c0fbcf + words[ 2]; y = f2(g,h,a); b += x; f = x + y;
x = e + f1(b,c,d) + 0xe9b5dba5 + words[ 3]; y = f2(f,g,h); a += x; e = x + y;
x = d + f1(a,b,c) + 0x3956c25b + words[ 4]; y = f2(e,f,g); h += x; d = x + y;
x = c + f1(h,a,b) + 0x59f111f1 + words[ 5]; y = f2(d,e,f); g += x; c = x + y;
x = b + f1(g,h,a) + 0x923f82a4 + words[ 6]; y = f2(c,d,e); f += x; b = x + y;
x = a + f1(f,g,h) + 0xab1c5ed5 + words[ 7]; y = f2(b,c,d); e += x; a = x + y;
// secound round
x = h + f1(e,f,g) + 0xd807aa98 + words[ 8]; y = f2(a,b,c); d += x; h = x + y;
x = g + f1(d,e,f) + 0x12835b01 + words[ 9]; y = f2(h,a,b); c += x; g = x + y;
x = f + f1(c,d,e) + 0x243185be + words[10]; y = f2(g,h,a); b += x; f = x + y;
x = e + f1(b,c,d) + 0x550c7dc3 + words[11]; y = f2(f,g,h); a += x; e = x + y;
x = d + f1(a,b,c) + 0x72be5d74 + words[12]; y = f2(e,f,g); h += x; d = x + y;
x = c + f1(h,a,b) + 0x80deb1fe + words[13]; y = f2(d,e,f); g += x; c = x + y;
x = b + f1(g,h,a) + 0x9bdc06a7 + words[14]; y = f2(c,d,e); f += x; b = x + y;
x = a + f1(f,g,h) + 0xc19bf174 + words[15]; y = f2(b,c,d); e += x; a = x + y;
// extend to 24 words
for (; i < 24; i++)
words[i] = words[i-16] +
(rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) +
words[i-7] +
(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
// third round
x = h + f1(e,f,g) + 0xe49b69c1 + words[16]; y = f2(a,b,c); d += x; h = x + y;
x = g + f1(d,e,f) + 0xefbe4786 + words[17]; y = f2(h,a,b); c += x; g = x + y;
x = f + f1(c,d,e) + 0x0fc19dc6 + words[18]; y = f2(g,h,a); b += x; f = x + y;
x = e + f1(b,c,d) + 0x240ca1cc + words[19]; y = f2(f,g,h); a += x; e = x + y;
x = d + f1(a,b,c) + 0x2de92c6f + words[20]; y = f2(e,f,g); h += x; d = x + y;
x = c + f1(h,a,b) + 0x4a7484aa + words[21]; y = f2(d,e,f); g += x; c = x + y;
x = b + f1(g,h,a) + 0x5cb0a9dc + words[22]; y = f2(c,d,e); f += x; b = x + y;
x = a + f1(f,g,h) + 0x76f988da + words[23]; y = f2(b,c,d); e += x; a = x + y;
// extend to 32 words
for (; i < 32; i++)
words[i] = words[i-16] +
(rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) +
words[i-7] +
(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
// fourth round
x = h + f1(e,f,g) + 0x983e5152 + words[24]; y = f2(a,b,c); d += x; h = x + y;
x = g + f1(d,e,f) + 0xa831c66d + words[25]; y = f2(h,a,b); c += x; g = x + y;
x = f + f1(c,d,e) + 0xb00327c8 + words[26]; y = f2(g,h,a); b += x; f = x + y;
x = e + f1(b,c,d) + 0xbf597fc7 + words[27]; y = f2(f,g,h); a += x; e = x + y;
x = d + f1(a,b,c) + 0xc6e00bf3 + words[28]; y = f2(e,f,g); h += x; d = x + y;
x = c + f1(h,a,b) + 0xd5a79147 + words[29]; y = f2(d,e,f); g += x; c = x + y;
x = b + f1(g,h,a) + 0x06ca6351 + words[30]; y = f2(c,d,e); f += x; b = x + y;
x = a + f1(f,g,h) + 0x14292967 + words[31]; y = f2(b,c,d); e += x; a = x + y;
// extend to 40 words
for (; i < 40; i++)
words[i] = words[i-16] +
(rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) +
words[i-7] +
(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
// fifth round
x = h + f1(e,f,g) + 0x27b70a85 + words[32]; y = f2(a,b,c); d += x; h = x + y;
x = g + f1(d,e,f) + 0x2e1b2138 + words[33]; y = f2(h,a,b); c += x; g = x + y;
x = f + f1(c,d,e) + 0x4d2c6dfc + words[34]; y = f2(g,h,a); b += x; f = x + y;
x = e + f1(b,c,d) + 0x53380d13 + words[35]; y = f2(f,g,h); a += x; e = x + y;
x = d + f1(a,b,c) + 0x650a7354 + words[36]; y = f2(e,f,g); h += x; d = x + y;
x = c + f1(h,a,b) + 0x766a0abb + words[37]; y = f2(d,e,f); g += x; c = x + y;
x = b + f1(g,h,a) + 0x81c2c92e + words[38]; y = f2(c,d,e); f += x; b = x + y;
x = a + f1(f,g,h) + 0x92722c85 + words[39]; y = f2(b,c,d); e += x; a = x + y;
// extend to 48 words
for (; i < 48; i++)
words[i] = words[i-16] +
(rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) +
words[i-7] +
(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
// sixth round
x = h + f1(e,f,g) + 0xa2bfe8a1 + words[40]; y = f2(a,b,c); d += x; h = x + y;
x = g + f1(d,e,f) + 0xa81a664b + words[41]; y = f2(h,a,b); c += x; g = x + y;
x = f + f1(c,d,e) + 0xc24b8b70 + words[42]; y = f2(g,h,a); b += x; f = x + y;
x = e + f1(b,c,d) + 0xc76c51a3 + words[43]; y = f2(f,g,h); a += x; e = x + y;
x = d + f1(a,b,c) + 0xd192e819 + words[44]; y = f2(e,f,g); h += x; d = x + y;
x = c + f1(h,a,b) + 0xd6990624 + words[45]; y = f2(d,e,f); g += x; c = x + y;
x = b + f1(g,h,a) + 0xf40e3585 + words[46]; y = f2(c,d,e); f += x; b = x + y;
x = a + f1(f,g,h) + 0x106aa070 + words[47]; y = f2(b,c,d); e += x; a = x + y;
// extend to 56 words
for (; i < 56; i++)
words[i] = words[i-16] +
(rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) +
words[i-7] +
(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
// seventh round
x = h + f1(e,f,g) + 0x19a4c116 + words[48]; y = f2(a,b,c); d += x; h = x + y;
x = g + f1(d,e,f) + 0x1e376c08 + words[49]; y = f2(h,a,b); c += x; g = x + y;
x = f + f1(c,d,e) + 0x2748774c + words[50]; y = f2(g,h,a); b += x; f = x + y;
x = e + f1(b,c,d) + 0x34b0bcb5 + words[51]; y = f2(f,g,h); a += x; e = x + y;
x = d + f1(a,b,c) + 0x391c0cb3 + words[52]; y = f2(e,f,g); h += x; d = x + y;
x = c + f1(h,a,b) + 0x4ed8aa4a + words[53]; y = f2(d,e,f); g += x; c = x + y;
x = b + f1(g,h,a) + 0x5b9cca4f + words[54]; y = f2(c,d,e); f += x; b = x + y;
x = a + f1(f,g,h) + 0x682e6ff3 + words[55]; y = f2(b,c,d); e += x; a = x + y;
// extend to 64 words
for (; i < 64; i++)
words[i] = words[i-16] +
(rotate(words[i-15], 7) ^ rotate(words[i-15], 18) ^ (words[i-15] >> 3)) +
words[i-7] +
(rotate(words[i- 2], 17) ^ rotate(words[i- 2], 19) ^ (words[i- 2] >> 10));
// eigth round
x = h + f1(e,f,g) + 0x748f82ee + words[56]; y = f2(a,b,c); d += x; h = x + y;
x = g + f1(d,e,f) + 0x78a5636f + words[57]; y = f2(h,a,b); c += x; g = x + y;
x = f + f1(c,d,e) + 0x84c87814 + words[58]; y = f2(g,h,a); b += x; f = x + y;
x = e + f1(b,c,d) + 0x8cc70208 + words[59]; y = f2(f,g,h); a += x; e = x + y;
x = d + f1(a,b,c) + 0x90befffa + words[60]; y = f2(e,f,g); h += x; d = x + y;
x = c + f1(h,a,b) + 0xa4506ceb + words[61]; y = f2(d,e,f); g += x; c = x + y;
x = b + f1(g,h,a) + 0xbef9a3f7 + words[62]; y = f2(c,d,e); f += x; b = x + y;
x = a + f1(f,g,h) + 0xc67178f2 + words[63]; y = f2(b,c,d); e += x; a = x + y;
// update hash
m_hash[0] += a;
m_hash[1] += b;
m_hash[2] += c;
m_hash[3] += d;
m_hash[4] += e;
m_hash[5] += f;
m_hash[6] += g;
m_hash[7] += h;
}
/// add arbitrary number of bytes
void SHA256::add(const void* data, size_t numBytes)
{
const uint8_t* current = (const uint8_t*) data;
if (m_bufferSize > 0)
{
while (numBytes > 0 && m_bufferSize < BlockSize)
{
m_buffer[m_bufferSize++] = *current++;
numBytes--;
}
}
// full buffer
if (m_bufferSize == BlockSize)
{
processBlock(m_buffer);
m_numBytes += BlockSize;
m_bufferSize = 0;
}
// no more data ?
if (numBytes == 0)
return;
// process full blocks
while (numBytes >= BlockSize)
{
processBlock(current);
current += BlockSize;
m_numBytes += BlockSize;
numBytes -= BlockSize;
}
// keep remaining bytes in buffer
while (numBytes > 0)
{
m_buffer[m_bufferSize++] = *current++;
numBytes--;
}
}
/// process final block, less than 64 bytes
void SHA256::processBuffer()
{
// the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte
// - append "1" bit to message
// - append "0" bits until message length in bit mod 512 is 448
// - append length as 64 bit integer
// number of bits
size_t paddedLength = m_bufferSize * 8;
// plus one bit set to 1 (always appended)
paddedLength++;
// number of bits must be (numBits % 512) = 448
size_t lower11Bits = paddedLength & 511;
if (lower11Bits <= 448)
paddedLength += 448 - lower11Bits;
else
paddedLength += 512 + 448 - lower11Bits;
// convert from bits to bytes
paddedLength /= 8;
// only needed if additional data flows over into a second block
unsigned char extra[BlockSize];
// append a "1" bit, 128 => binary 10000000
if (m_bufferSize < BlockSize)
m_buffer[m_bufferSize] = 128;
else
extra[0] = 128;
size_t i;
for (i = m_bufferSize + 1; i < BlockSize; i++)
m_buffer[i] = 0;
for (; i < paddedLength; i++)
extra[i - BlockSize] = 0;
// add message length in bits as 64 bit number
uint64_t msgBits = 8 * (m_numBytes + m_bufferSize);
// find right position
unsigned char* addLength;
if (paddedLength < BlockSize)
addLength = m_buffer + paddedLength;
else
addLength = extra + paddedLength - BlockSize;
// must be big endian
*addLength++ = (unsigned char)((msgBits >> 56) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 48) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 40) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 32) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 24) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 16) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 8) & 0xFF);
*addLength = (unsigned char)( msgBits & 0xFF);
// process blocks
processBlock(m_buffer);
// flowed over into a second block ?
if (paddedLength > BlockSize)
processBlock(extra);
}
/// return latest hash as 64 hex characters
std::string SHA256::getHash()
{
// compute hash (as raw bytes)
unsigned char rawHash[HashBytes];
getHash(rawHash);
// convert to hex string
std::string result;
result.reserve(2 * HashBytes);
for (int i = 0; i < HashBytes; i++)
{
static const char dec2hex[16+1] = "0123456789abcdef";
result += dec2hex[(rawHash[i] >> 4) & 15];
result += dec2hex[ rawHash[i] & 15];
}
return result;
}
/// return latest hash as bytes
void SHA256::getHash(unsigned char buffer[SHA256::HashBytes])
{
// save old hash if buffer is partially filled
uint32_t oldHash[HashValues];
for (int i = 0; i < HashValues; i++)
oldHash[i] = m_hash[i];
// process remaining bytes
processBuffer();
unsigned char* current = buffer;
for (int i = 0; i < HashValues; i++)
{
*current++ = (m_hash[i] >> 24) & 0xFF;
*current++ = (m_hash[i] >> 16) & 0xFF;
*current++ = (m_hash[i] >> 8) & 0xFF;
*current++ = m_hash[i] & 0xFF;
// restore old hash
m_hash[i] = oldHash[i];
}
}
/// compute SHA256 of a memory block
std::string SHA256::operator()(const void* data, size_t numBytes)
{
reset();
add(data, numBytes);
return getHash();
}
/// compute SHA256 of a string, excluding final zero
std::string SHA256::operator()(const std::string& text)
{
reset();
add(text.c_str(), text.size());
return getHash();
}