diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0f69877
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+bin/
+obj/
+*.user
+*.suo
+.vs
+
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..2278a91
--- /dev/null
+++ b/makefile
@@ -0,0 +1,77 @@
+#CXX=g++-8
+#CC=gcc-8
+PLATFORM=$(shell uname -i)
+CXXFLAGS=-std=c++17
+CCFLAGS=
+ifeq ($(PLATFORM),x86_64)
+ CXXFLAGS += -maes
+endif
+BINDIR=bin
+SRCDIR=src
+OBJDIR=obj
+LDFLAGS=
+TOBJS=$(addprefix $(OBJDIR)/,instructionsPortable.o TestAluFpu.o)
+ROBJS=$(addprefix $(OBJDIR)/,argon2_core.o argon2_ref.o blake2b.o dataset.o instructionsPortable.o InterpretedVirtualMachine.o main.o Program.o softAes.o VirtualMachine.o)
+SRC1=$(addprefix $(SRCDIR)/,TestAluFpu.cpp instructions.hpp Pcg32.hpp)
+
+all: release test
+
+release: CXXFLAGS += -march=native -O3 -flto
+release: CCFLAGS += -march=native -O3 -flto
+release: $(BINDIR)/randomx
+
+debug: CXXFLAGS += -g
+debug: CCFLAGS += -g
+debug: LDFLAGS += -g
+debug: $(BINDIR)/randomx
+
+test: CXXFLAGS += -O0
+test: $(BINDIR)/AluFpuTest
+
+$(BINDIR)/randomx: $(ROBJS) | $(BINDIR)
+ $(CXX) $(ROBJS) $(LDFLAGS) -o $@
+
+$(BINDIR)/AluFpuTest: $(TOBJS) | $(BINDIR)
+ $(CXX) $(TOBJS) $(LDFLAGS) -o $@
+
+$(OBJDIR)/TestAluFpu.o: $(addprefix $(SRCDIR)/,TestAluFpu.cpp instructions.hpp Pcg32.hpp) | $(OBJDIR)
+ $(CXX) $(CXXFLAGS) -c $(SRCDIR)/TestAluFpu.cpp -o $@
+
+$(OBJDIR)/argon2_core.o: $(addprefix $(SRCDIR)/,argon2_core.c argon2_core.h blake2/blake2.h blake2/blake2-impl.h) | $(OBJDIR)
+ $(CC) $(CCFLAGS) -c $(SRCDIR)/argon2_core.c -o $@
+
+$(OBJDIR)/argon2_ref.o: $(addprefix $(SRCDIR)/,argon2_ref.c argon2.h argon2_core.h blake2/blake2.h blake2/blake2-impl.h blake2/blamka-round-ref.h) | $(OBJDIR)
+ $(CC) $(CCFLAGS) -c $(SRCDIR)/argon2_ref.c -o $@
+
+$(OBJDIR)/blake2b.o: $(addprefix $(SRCDIR)/blake2/,blake2b.c blake2.h blake2-impl.h) | $(OBJDIR)
+ $(CC) $(CCFLAGS) -c $(SRCDIR)/blake2/blake2b.c -o $@
+
+$(OBJDIR)/dataset.o: $(addprefix $(SRCDIR)/,dataset.cpp common.hpp Pcg32.hpp argon2_core.h) | $(OBJDIR)
+ $(CXX) $(CXXFLAGS) -c $(SRCDIR)/dataset.cpp -o $@
+
+$(OBJDIR)/instructionsPortable.o: $(addprefix $(SRCDIR)/,instructionsPortable.cpp instructions.hpp intrinPortable.h) | $(OBJDIR)
+ $(CXX) $(CXXFLAGS) -c $(SRCDIR)/instructionsPortable.cpp -o $@
+
+$(OBJDIR)/InterpretedVirtualMachine.o: $(addprefix $(SRCDIR)/,InterpretedVirtualMachine.cpp InterpretedVirtualMachine.hpp Pcg32.hpp instructions.hpp) | $(OBJDIR)
+ $(CXX) $(CXXFLAGS) -c $(SRCDIR)/InterpretedVirtualMachine.cpp -o $@
+
+$(OBJDIR)/main.o: $(addprefix $(SRCDIR)/,main.cpp InterpretedVirtualMachine.hpp Stopwatch.hpp blake2/blake2.h) | $(OBJDIR)
+ $(CXX) $(CXXFLAGS) -c $(SRCDIR)/main.cpp -o $@
+
+$(OBJDIR)/Program.o: $(addprefix $(SRCDIR)/,Program.cpp Program.hpp Pcg32.hpp) | $(OBJDIR)
+ $(CXX) $(CXXFLAGS) -c $(SRCDIR)/Program.cpp -o $@
+
+$(OBJDIR)/softAes.o: $(addprefix $(SRCDIR)/,softAes.cpp softAes.h) | $(OBJDIR)
+ $(CXX) $(CXXFLAGS) -c $(SRCDIR)/softAes.cpp -o $@
+
+$(OBJDIR)/VirtualMachine.o: $(addprefix $(SRCDIR)/,VirtualMachine.cpp VirtualMachine.hpp common.hpp dataset.hpp) | $(OBJDIR)
+ $(CXX) $(CXXFLAGS) -c $(SRCDIR)/VirtualMachine.cpp -o $@
+
+$(OBJDIR):
+ mkdir $(OBJDIR)
+
+$(BINDIR):
+ mkdir $(BINDIR)
+
+clean:
+ rm -f $(BINDIR)/randomx $(BINDIR)/AluFpuTest $(OBJDIR)/*.o
\ No newline at end of file
diff --git a/src/InterpretedVirtualMachine.cpp b/src/InterpretedVirtualMachine.cpp
new file mode 100644
index 0000000..3be9d7f
--- /dev/null
+++ b/src/InterpretedVirtualMachine.cpp
@@ -0,0 +1,336 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+//#define TRACE
+//#define FPUCHECK
+#include "InterpretedVirtualMachine.hpp"
+#include "Pcg32.hpp"
+#include "instructions.hpp"
+#include
+#include
+#include
+#include
+#include
+
+#ifdef FPUCHECK
+constexpr bool fpuCheck = true;
+#else
+constexpr bool fpuCheck = false;
+#endif
+
+namespace RandomX {
+
+ void InterpretedVirtualMachine::initializeProgram(const void* seed) {
+ Pcg32 gen(seed);
+ for (unsigned i = 0; i < sizeof(reg) / sizeof(Pcg32::result_type); ++i) {
+ *(((uint32_t*)®) + i) = gen();
+ }
+ FPINIT();
+ for (int i = 0; i < 8; ++i) {
+ reg.f[i].f64 = (double)reg.f[i].i64;
+ }
+ p.initialize(gen);
+ mem.ma = (gen() ^ *(((uint32_t*)seed) + 4)) & ~7;
+ mem.mx = *(((uint32_t*)seed) + 5);
+ pc = 0;
+ ic = InstructionCount;
+ stack.clear();
+ }
+
+ void InterpretedVirtualMachine::execute() {
+ while (ic > 0) {
+ auto& inst = p(pc);
+ if(trace) std::cout << p.getName(inst) << " (" << std::dec << pc << ")" << std::endl;
+ pc = (pc + 1) % ProgramLength;
+ auto handler = engine[inst.opcode];
+ (this->*handler)(inst);
+ ic--;
+ }
+ }
+
+ convertible_t InterpretedVirtualMachine::loada(Instruction& inst) {
+ convertible_t& rega = reg.r[inst.rega % RegistersCount];
+ rega.u64 ^= inst.addr0;
+ addr_t addr = rega.u32;
+ switch (inst.loca & 7)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ return readDataset(addr, mem);
+
+ case 4:
+ return scratchpad[addr % ScratchpadL2];
+
+ case 5:
+ case 6:
+ case 7:
+ return scratchpad[addr % ScratchpadL1];
+ }
+ }
+
+ convertible_t InterpretedVirtualMachine::loadbr1(Instruction& inst) {
+ switch (inst.loca & 7)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ return reg.r[inst.regb % RegistersCount];
+ case 6:
+ case 7:
+ convertible_t temp;
+ temp.i64 = inst.imm1;
+ return temp;
+ }
+ }
+
+ convertible_t InterpretedVirtualMachine::loadbr0(Instruction& inst) {
+ switch (inst.locb & 7)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ return reg.r[inst.regb % RegistersCount];
+ case 6:
+ case 7:
+ convertible_t temp;
+ temp.u64 = inst.imm0;
+ return temp;
+ }
+ }
+
+ double InterpretedVirtualMachine::loadbf(Instruction& inst) {
+ switch (inst.locb & 7)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ return reg.f[inst.regb % RegistersCount].f64;
+ case 6:
+ case 7:
+ return (double)inst.imm1;
+ }
+ }
+
+ convertible_t& InterpretedVirtualMachine::getcr(Instruction& inst) {
+ addr_t addr;
+ switch (inst.locc & 7)
+ {
+ case 0:
+ addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addr1;
+ return scratchpad[addr % ScratchpadL2];
+
+ case 1:
+ case 2:
+ case 3:
+ addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addr1;
+ return scratchpad[addr % ScratchpadL1];
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return reg.r[inst.regc % RegistersCount];
+ }
+ }
+
+ convertible_t& InterpretedVirtualMachine::getcf(Instruction& inst) {
+ addr_t addr;
+ switch (inst.locc & 7)
+ {
+ case 0:
+ addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addr1;
+ return scratchpad[addr % ScratchpadL2];
+
+ case 1:
+ case 2:
+ case 3:
+ addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addr1;
+ return scratchpad[addr % ScratchpadL1];
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return reg.f[inst.regc % RegistersCount];
+ }
+ }
+
+#define ALU_RETIRE(x) x(a, b, c); \
+ if(trace) std::cout << std::hex << a.u64 << " " << b.u64 << " " << c.u64 << std::endl;
+
+#define FPU_RETIRE(x) x(a, b, c); \
+ if(trace) { \
+ convertible_t bc; \
+ bc.f64 = b; \
+ std::cout << std::hex << a.u64 << " " << bc.u64 << " " << c.u64 << std::endl; \
+ } \
+ if(fpuCheck) { \
+ convertible_t bc; \
+ if(c.f64 != c.f64) { \
+ std::stringstream ss; \
+ bc.f64 = b; \
+ ss << "NaN result of " << #x << "(" << std::hex << a.u64 << ", " << bc.u64 << ") = " << c.u64; \
+ throw std::runtime_error(ss.str()); \
+ } else if (std::fpclassify(c.f64) == FP_SUBNORMAL) {\
+ std::stringstream ss; \
+ bc.f64 = b; \
+ ss << "Denormal result of " << #x << "(" << std::hex << a.u64 << ", " << bc.u64 << ") = " << c.u64; \
+ throw std::runtime_error(ss.str()); \
+ } \
+ }
+
+#define FPU_RETIRE_NB(x) x(a, b, c); \
+ if(trace) std::cout << std::hex << a.u64 << " " << c.u64 << std::endl;
+
+#define ALU_INST(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
+ convertible_t a = loada(inst); \
+ convertible_t b = loadbr1(inst); \
+ convertible_t& c = getcr(inst); \
+ ALU_RETIRE(x) \
+ }
+
+#define ALU_INST_SR(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
+ convertible_t a = loada(inst); \
+ convertible_t b = loadbr0(inst); \
+ convertible_t& c = getcr(inst); \
+ ALU_RETIRE(x) \
+ }
+
+#define FPU_INST(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
+ convertible_t a = loada(inst); \
+ double b = loadbf(inst); \
+ convertible_t& c = getcf(inst); \
+ FPU_RETIRE(x) \
+ }
+
+#define FPU_INST_NB(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
+ convertible_t a = loada(inst); \
+ convertible_t b; \
+ convertible_t& c = getcf(inst); \
+ FPU_RETIRE_NB(x) \
+ }
+
+ ALU_INST(ADD_64)
+ ALU_INST(ADD_32)
+ ALU_INST(SUB_64)
+ ALU_INST(SUB_32)
+ ALU_INST(MUL_64)
+ ALU_INST(MULH_64)
+ ALU_INST(MUL_32)
+ ALU_INST(IMUL_32)
+ ALU_INST(IMULH_64)
+ ALU_INST(DIV_64)
+ ALU_INST(IDIV_64)
+ ALU_INST(AND_64)
+ ALU_INST(AND_32)
+ ALU_INST(OR_64)
+ ALU_INST(OR_32)
+ ALU_INST(XOR_64)
+ ALU_INST(XOR_32)
+
+ ALU_INST_SR(SHL_64)
+ ALU_INST_SR(SHR_64)
+ ALU_INST_SR(SAR_64)
+ ALU_INST_SR(ROL_64)
+ ALU_INST_SR(ROR_64)
+
+ FPU_INST(FPADD)
+ FPU_INST(FPSUB)
+ FPU_INST(FPMUL)
+ FPU_INST(FPDIV)
+
+ FPU_INST_NB(FPSQRT)
+ FPU_INST_NB(FPROUND)
+
+ void InterpretedVirtualMachine::h_CALL(Instruction& inst) {
+ convertible_t a = loada(inst);
+ convertible_t b = loadbr1(inst);
+ convertible_t& c = getcr(inst);
+ if (b.u32 <= (uint32_t)inst.imm1) {
+ stackPush(a);
+ stackPush(pc);
+ pc += (inst.imm0 & 127) + 1;
+ pc = pc % ProgramLength;
+ }
+ else {
+ c.u64 = a.u64;
+ }
+ }
+
+ void InterpretedVirtualMachine::h_RET(Instruction& inst) {
+ convertible_t a = loada(inst);
+ convertible_t b = loadbr1(inst);
+ convertible_t& c = getcr(inst);
+ if (stack.size() > 0 && b.u32 <= (uint32_t)inst.imm1) {
+ auto raddr = stackPopAddress();
+ auto retval = stackPopValue();
+ c.u64 = a.u64 ^ retval.u64;
+ pc = raddr;
+ }
+ else {
+ c.u64 = a.u64;
+ }
+ }
+
+#include "instructionWeights.hpp"
+#define INST_HANDLE(x) REPN(&InterpretedVirtualMachine::h_##x, WT(x))
+
+ InstructionHandler InterpretedVirtualMachine::engine[256] = {
+ INST_HANDLE(ADD_64)
+ INST_HANDLE(ADD_32)
+ INST_HANDLE(SUB_64)
+ INST_HANDLE(SUB_32)
+ INST_HANDLE(MUL_64)
+ INST_HANDLE(MULH_64)
+ INST_HANDLE(MUL_32)
+ INST_HANDLE(IMUL_32)
+ INST_HANDLE(IMULH_64)
+ INST_HANDLE(DIV_64)
+ INST_HANDLE(IDIV_64)
+ INST_HANDLE(AND_64)
+ INST_HANDLE(AND_32)
+ INST_HANDLE(OR_64)
+ INST_HANDLE(OR_32)
+ INST_HANDLE(XOR_64)
+ INST_HANDLE(XOR_32)
+ INST_HANDLE(SHL_64)
+ INST_HANDLE(SHR_64)
+ INST_HANDLE(SAR_64)
+ INST_HANDLE(ROL_64)
+ INST_HANDLE(ROR_64)
+ INST_HANDLE(FPADD)
+ INST_HANDLE(FPSUB)
+ INST_HANDLE(FPMUL)
+ INST_HANDLE(FPDIV)
+ INST_HANDLE(FPSQRT)
+ INST_HANDLE(FPROUND)
+ INST_HANDLE(CALL)
+ INST_HANDLE(RET)
+ };
+}
\ No newline at end of file
diff --git a/src/InterpretedVirtualMachine.hpp b/src/InterpretedVirtualMachine.hpp
new file mode 100644
index 0000000..416c0e7
--- /dev/null
+++ b/src/InterpretedVirtualMachine.hpp
@@ -0,0 +1,106 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+#pragma once
+
+#include "VirtualMachine.hpp"
+#include "Program.hpp"
+#include
+
+namespace RandomX {
+
+ class InterpretedVirtualMachine;
+
+ typedef void(InterpretedVirtualMachine::*InstructionHandler)(Instruction&);
+
+ class InterpretedVirtualMachine : public VirtualMachine {
+ public:
+ InterpretedVirtualMachine(bool softAes) : VirtualMachine(softAes) {}
+ virtual void initializeProgram(const void* seed) override;
+ virtual void execute() override;
+ const Program& getProgam() {
+ return p;
+ }
+ private:
+ static InstructionHandler engine[256];
+ Program p;
+ std::vector stack;
+ uint64_t pc, ic;
+
+ convertible_t loada(Instruction&);
+ convertible_t loadbr0(Instruction&);
+ convertible_t loadbr1(Instruction&);
+ double loadbf(Instruction&);
+ convertible_t& getcr(Instruction&);
+ convertible_t& getcf(Instruction&);
+
+ void stackPush(convertible_t& c) {
+ stack.push_back(c);
+ }
+
+ void stackPush(uint64_t x) {
+ convertible_t c;
+ c.u64 = x;
+ stack.push_back(c);
+ }
+
+ convertible_t stackPopValue() {
+ convertible_t top = stack.back();
+ stack.pop_back();
+ return top;
+ }
+
+ uint64_t stackPopAddress() {
+ convertible_t top = stack.back();
+ stack.pop_back();
+ return top.u64;
+ }
+
+ void h_ADD_64(Instruction&);
+ void h_ADD_32(Instruction&);
+ void h_SUB_64(Instruction&);
+ void h_SUB_32(Instruction&);
+ void h_MUL_64(Instruction&);
+ void h_MULH_64(Instruction&);
+ void h_MUL_32(Instruction&);
+ void h_IMUL_32(Instruction&);
+ void h_IMULH_64(Instruction&);
+ void h_DIV_64(Instruction&);
+ void h_IDIV_64(Instruction&);
+ void h_AND_64(Instruction&);
+ void h_AND_32(Instruction&);
+ void h_OR_64(Instruction&);
+ void h_OR_32(Instruction&);
+ void h_XOR_64(Instruction&);
+ void h_XOR_32(Instruction&);
+ void h_SHL_64(Instruction&);
+ void h_SHR_64(Instruction&);
+ void h_SAR_64(Instruction&);
+ void h_ROL_64(Instruction&);
+ void h_ROR_64(Instruction&);
+ void h_FPADD(Instruction&);
+ void h_FPSUB(Instruction&);
+ void h_FPMUL(Instruction&);
+ void h_FPDIV(Instruction&);
+ void h_FPSQRT(Instruction&);
+ void h_FPROUND(Instruction&);
+ void h_CALL(Instruction&);
+ void h_RET(Instruction&);
+ };
+}
\ No newline at end of file
diff --git a/src/Pcg32.hpp b/src/Pcg32.hpp
new file mode 100644
index 0000000..906800f
--- /dev/null
+++ b/src/Pcg32.hpp
@@ -0,0 +1,72 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+// Based on:
+// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
+// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
+
+#pragma once
+#include
+
+#if defined(_MSC_VER)
+#pragma warning (disable : 4146)
+#endif
+
+class Pcg32 {
+public:
+ typedef uint32_t result_type;
+ static constexpr result_type min() { return 0U; }
+ static constexpr result_type max() { return UINT32_MAX; }
+ Pcg32(const void* seed) {
+ auto* u64seed = (const uint64_t*)seed;
+ state = *(u64seed + 0);
+ inc = *(u64seed + 1) | 1ull;
+ }
+ Pcg32(uint64_t state, uint64_t inc) : state(state), inc(inc | 1ull) {
+ }
+ result_type operator()() {
+ return next();
+ }
+ result_type getUniform(result_type min, result_type max) {
+ const result_type range = max - min;
+ const result_type erange = range + 1;
+ result_type ret;
+
+ for (;;) {
+ ret = next();
+ if (ret / erange < UINT32_MAX / erange || UINT32_MAX % erange == range) {
+ ret %= erange;
+ break;
+ }
+ }
+ return ret + min;
+ }
+private:
+ uint64_t state;
+ uint64_t inc;
+ result_type next() {
+ uint64_t oldstate = state;
+ // Advance internal state
+ state = oldstate * 6364136223846793005ULL + inc;
+ // Calculate output function (XSH RR), uses old state for max ILP
+ uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
+ uint32_t rot = oldstate >> 59u;
+ return (xorshifted >> rot) | (xorshifted << (-rot & 31));
+ }
+};
diff --git a/src/Program.cpp b/src/Program.cpp
new file mode 100644
index 0000000..ae46de7
--- /dev/null
+++ b/src/Program.cpp
@@ -0,0 +1,77 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+#include "Program.hpp"
+#include "Pcg32.hpp"
+
+namespace RandomX {
+ void Program::initialize(Pcg32& gen) {
+ for (unsigned i = 0; i < sizeof(programBuffer) / sizeof(Pcg32::result_type); ++i) {
+ *(((uint32_t*)&programBuffer) + i) = gen();
+ }
+ }
+
+ void Program::print(std::ostream& os) const {
+ for (int i = 0; i < RandomX::ProgramLength; ++i) {
+ auto instr = programBuffer[i];
+ os << std::dec << instrNames[instr.opcode] << " (" << i << "):" << std::endl;
+ os << " A: loc = " << (instr.loca & 7) << ", reg: " << (instr.rega & 7) << std::endl;
+ os << " B: loc = " << (instr.locb & 7) << ", reg: " << (instr.regb & 7) << std::endl;
+ os << " C: loc = " << (instr.locc & 7) << ", reg: " << (instr.regc & 7) << std::endl;
+ os << " imm0 = " << (int)instr.imm0 << std::endl;
+ os << " imm1 = " << std::hex << instr.imm1 << std::endl;
+ }
+ }
+
+#include "instructionWeights.hpp"
+#define INST_NAME(x) REPN(#x, WT(x))
+
+ const char* Program::instrNames[256] = {
+ INST_NAME(ADD_64)
+ INST_NAME(ADD_32)
+ INST_NAME(SUB_64)
+ INST_NAME(SUB_32)
+ INST_NAME(MUL_64)
+ INST_NAME(MULH_64)
+ INST_NAME(MUL_32)
+ INST_NAME(IMUL_32)
+ INST_NAME(IMULH_64)
+ INST_NAME(DIV_64)
+ INST_NAME(IDIV_64)
+ INST_NAME(AND_64)
+ INST_NAME(AND_32)
+ INST_NAME(OR_64)
+ INST_NAME(OR_32)
+ INST_NAME(XOR_64)
+ INST_NAME(XOR_32)
+ INST_NAME(SHL_64)
+ INST_NAME(SHR_64)
+ INST_NAME(SAR_64)
+ INST_NAME(ROL_64)
+ INST_NAME(ROR_64)
+ INST_NAME(FPADD)
+ INST_NAME(FPSUB)
+ INST_NAME(FPMUL)
+ INST_NAME(FPDIV)
+ INST_NAME(FPSQRT)
+ INST_NAME(FPROUND)
+ INST_NAME(CALL)
+ INST_NAME(RET)
+ };
+}
diff --git a/src/Program.hpp b/src/Program.hpp
new file mode 100644
index 0000000..09641ee
--- /dev/null
+++ b/src/Program.hpp
@@ -0,0 +1,66 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+#pragma once
+
+#include
+#include
+#include "common.hpp"
+
+class Pcg32;
+
+namespace RandomX {
+
+ struct Instruction {
+ uint8_t opcode;
+ uint8_t loca;
+ uint8_t rega;
+ uint8_t locb;
+ uint8_t regb;
+ uint8_t locc;
+ uint8_t regc;
+ uint8_t imm0;
+ uint32_t addr0;
+ union {
+ uint32_t addr1;
+ int32_t imm1;
+ };
+ };
+
+ static_assert(sizeof(Instruction) == 16, "Invalid alignment of struct Instruction");
+
+ class Program {
+ public:
+ Instruction& operator()(uint64_t pc) {
+ return programBuffer[pc];
+ }
+ const char* getName(Instruction& instr) {
+ return instrNames[instr.opcode];
+ }
+ void initialize(Pcg32& gen);
+ friend std::ostream& operator<<(std::ostream& os, const Program& p) {
+ p.print(os);
+ return os;
+ }
+ private:
+ void print(std::ostream&) const;
+ static const char* instrNames[256];
+ Instruction programBuffer[ProgramLength];
+ };
+}
diff --git a/src/Stopwatch.hpp b/src/Stopwatch.hpp
new file mode 100644
index 0000000..4f3a5a1
--- /dev/null
+++ b/src/Stopwatch.hpp
@@ -0,0 +1,75 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+#pragma once
+
+#include
+#include
+
+class Stopwatch {
+public:
+ Stopwatch(bool startNow = false) {
+ reset();
+ if (startNow) {
+ start();
+ }
+ }
+ void reset() {
+ isRunning = false;
+ elapsed = 0;
+ }
+ void start() {
+ if (!isRunning) {
+ startMark = std::chrono::high_resolution_clock::now();
+ isRunning = true;
+ }
+ }
+ void restart() {
+ startMark = std::chrono::high_resolution_clock::now();
+ isRunning = true;
+ elapsed = 0;
+ }
+ void stop() {
+ if (isRunning) {
+ chrono_t endMark = std::chrono::high_resolution_clock::now();
+ uint64_t ns = std::chrono::duration_cast(endMark - startMark).count();
+ elapsed += ns;
+ isRunning = false;
+ }
+ }
+ double getElapsed() {
+ return getElapsedNanosec() / 1e+9;
+ }
+private:
+ using chrono_t = std::chrono::high_resolution_clock::time_point;
+ using sw_unit = std::chrono::nanoseconds;
+ chrono_t startMark;
+ uint64_t elapsed;
+ bool isRunning;
+
+ uint64_t getElapsedNanosec() {
+ uint64_t elns = elapsed;
+ if (isRunning) {
+ chrono_t endMark = std::chrono::high_resolution_clock::now();
+ uint64_t ns = std::chrono::duration_cast(endMark - startMark).count();
+ elns += ns;
+ }
+ return elns;
+ }
+};
\ No newline at end of file
diff --git a/src/TestAluFpu.cpp b/src/TestAluFpu.cpp
new file mode 100644
index 0000000..f2fe387
--- /dev/null
+++ b/src/TestAluFpu.cpp
@@ -0,0 +1,390 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+#include
+#include
+#include
+#include "instructions.hpp"
+#include "Pcg32.hpp"
+//#define DEBUG
+
+using namespace RandomX;
+
+typedef void(*VmOperation)(convertible_t&, convertible_t&, convertible_t&);
+
+uint64_t rxRound(uint32_t mode, int64_t x, int64_t y, VmOperation op) {
+ convertible_t a, b, c;
+ a.u64 = mode;
+ FPROUND(a, b, c);
+#ifdef DEBUG
+ a.f64 = convertToDouble(x);
+ b.f64 = convertToDouble(y);
+ std::cout << std::hex << (uint64_t)x << " -> " << a.u64 << std::endl;
+ std::cout << std::hex << (uint64_t)y << " -> " << b.u64 << std::endl;
+ std::cout << std::dec;
+#endif
+ a.i64 = x;
+ b.i64 = y;
+ op(a, b, c);
+ return c.u64;
+}
+
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+#define RX_EXECUTE_U64(va, vb, INST) do { \
+ a.u64 = va; \
+ b.u64 = vb; \
+ INST(a, b, c); \
+ } while(false)
+
+#define RX_EXECUTE_I64(va, vb, INST) do { \
+ a.i64 = va; \
+ b.i64 = vb; \
+ INST(a, b, c); \
+ } while(false)
+
+TEST_CASE("Integer addition (64-bit)", "[ADD_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xFFFFFFFF, 0x1, ADD_64);
+ CHECK(c.u64 == 0x100000000);
+
+ RX_EXECUTE_U64(0x8000000000000000, 0x8000000000000000, ADD_64);
+ CHECK(c.u64 == 0x0);
+}
+
+TEST_CASE("Integer addition (32-bit)", "[ADD_32]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xFFFFFFFF, 0x1, ADD_32);
+ CHECK(c.u64 == 0);
+
+ RX_EXECUTE_U64(0xFF00000000000001, 0x0000000100000001, ADD_32);
+ CHECK(c.u64 == 2);
+}
+
+TEST_CASE("Integer subtraction (64-bit)", "[SUB_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(1, 0xFFFFFFFF, SUB_64);
+ CHECK(c.u64 == 0xFFFFFFFF00000002);
+}
+
+TEST_CASE("Integer subtraction (32-bit)", "[SUB_32]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(1, 0xFFFFFFFF, SUB_32);
+ CHECK(c.u64 == 2);
+}
+
+TEST_CASE("Unsigned multiplication (64-bit, low half)", "[MUL_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, MUL_64);
+ CHECK(c.u64 == 0x28723424A9108E51);
+}
+
+TEST_CASE("Unsigned multiplication (64-bit, high half)", "[MULH_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, MULH_64);
+ CHECK(c.u64 == 0xB4676D31D2B34883);
+}
+
+TEST_CASE("Unsigned multiplication (32-bit x 32-bit -> 64-bit)", "[MUL_32]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, MUL_32);
+ CHECK(c.u64 == 0xB001AA5FA9108E51);
+}
+
+TEST_CASE("Signed multiplication (32-bit x 32-bit -> 64-bit)", "[IMUL_32]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, IMUL_32);
+ CHECK(c.u64 == 0x03EBA0C1A9108E51);
+}
+
+TEST_CASE("Signed multiplication (64-bit, high half)", "[IMULH_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, IMULH_64);
+ CHECK(c.u64 == 0x02D93EF1269D3EE5);
+}
+
+TEST_CASE("Unsigned division (64-bit / 32-bit -> 32-bit)", "[DIV_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(8774217225983458895, 3014068202, DIV_64);
+ CHECK(c.u64 == 2911087818);
+
+ RX_EXECUTE_U64(8774217225983458895, 0, DIV_64);
+ CHECK(c.u64 == 8774217225983458895);
+
+ RX_EXECUTE_U64(3014068202, 8774217225983458895, DIV_64);
+ CHECK(c.u64 == 2);
+}
+
+TEST_CASE("Signed division (64-bit / 32-bit -> 32-bit)", "[IDIV_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(8774217225983458895, 3014068202, IDIV_64);
+ CHECK(c.u64 == 0xFFFFFFFE67B4994E);
+
+ RX_EXECUTE_U64(5, 0xFFFFFFFFFFFFFFFF, IDIV_64);
+ CHECK(c.u64 == 0xFFFFFFFFFFFFFFFB);
+
+ RX_EXECUTE_U64(8774217225983458895, 0, IDIV_64);
+ CHECK(c.u64 == 8774217225983458895);
+
+ RX_EXECUTE_U64(0x8000000000000000, 0xFFFFFFFFFFFFFFFF, IDIV_64);
+ CHECK(c.u64 == 0x8000000000000000);
+
+ RX_EXECUTE_U64(0x8000000000000000, 0x93D1FFFFFFFFFFFF, IDIV_64);
+ CHECK(c.u64 == 0x8000000000000000);
+
+ RX_EXECUTE_U64(0xFFFFFFFFB3A707EA, 8774217225983458895, IDIV_64);
+ CHECK(c.u64 == 0xFFFFFFFFFFFFFFFF);
+}
+
+TEST_CASE("Bitwise AND (64-bit)", "[AND_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xCCCCCCCCCCCCCCCC, 0xAAAAAAAAAAAAAAAA, AND_64);
+ CHECK(c.u64 == 0x8888888888888888);
+}
+
+TEST_CASE("Bitwise AND (32-bit)", "[AND_32]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0xCCCCCCCCCCCCCCCC, 0xAAAAAAAAAAAAAAAA, AND_32);
+ CHECK(c.u64 == 0x88888888);
+}
+
+TEST_CASE("Bitwise OR (64-bit)", "[OR_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0x4444444444444444, 0xAAAAAAAAAAAAAAAA, OR_64);
+ CHECK(c.u64 == 0xEEEEEEEEEEEEEEEE);
+}
+
+TEST_CASE("Bitwise OR (32-bit)", "[OR_32]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0x4444444444444444, 0xAAAAAAAAAAAAAAAA, OR_32);
+ CHECK(c.u64 == 0xEEEEEEEE);
+}
+
+TEST_CASE("Bitwise XOR (64-bit)", "[XOR_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0x8888888888888888, 0xAAAAAAAAAAAAAAAA, XOR_64);
+ CHECK(c.u64 == 0x2222222222222222);
+}
+
+TEST_CASE("Bitwise XOR (32-bit)", "[XOR_32]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0x8888888888888888, 0xAAAAAAAAAAAAAAAA, XOR_32);
+ CHECK(c.u64 == 0x22222222);
+}
+
+TEST_CASE("Logical left shift (64-bit)", "[SHL_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0x3, 52, SHL_64);
+ CHECK(c.u64 == 0x30000000000000);
+
+ RX_EXECUTE_U64(953360005391419562, 4569451684712230561, SHL_64);
+ CHECK(c.u64 == 6978065200108797952);
+
+ RX_EXECUTE_U64(0x8000000000000000, 1, SHL_64);
+ CHECK(c.u64 == 0);
+}
+
+TEST_CASE("Logical right shift (64-bit)", "[SHR_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0x3, 52, SHR_64);
+ CHECK(c.u64 == 0);
+
+ RX_EXECUTE_U64(953360005391419562, 4569451684712230561, SHR_64);
+ CHECK(c.u64 == 110985711);
+
+ RX_EXECUTE_U64(0x8000000000000000, 1, SHR_64);
+ CHECK(c.u64 == 0x4000000000000000);
+}
+
+TEST_CASE("Arithmetic right shift (64-bit)", "[SAR_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_I64(-9, 2, SAR_64);
+ CHECK(c.i64 == -3);
+
+ RX_EXECUTE_I64(INT64_MIN, 63, SAR_64);
+ CHECK(c.i64 == -1);
+
+ RX_EXECUTE_I64(INT64_MAX, 163768499474606398, SAR_64);
+ CHECK(c.i64 == 1);
+}
+
+TEST_CASE("Circular left shift (64-bit)", "[ROL_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0x3, 52, ROL_64);
+ CHECK(c.u64 == 0x30000000000000);
+
+ RX_EXECUTE_U64(953360005391419562, 4569451684712230561, ROL_64);
+ CHECK(c.u64 == 6978065200552740799);
+
+ RX_EXECUTE_U64(0x8000000000000000, 1, ROL_64);
+ CHECK(c.u64 == 1);
+}
+
+TEST_CASE("Circular right shift (64-bit)", "[ROR_64]") {
+ convertible_t a, b, c;
+
+ RX_EXECUTE_U64(0x3, 52, ROR_64);
+ CHECK(c.u64 == 12288);
+
+ RX_EXECUTE_U64(953360005391419562, 4569451684712230561, ROR_64);
+ CHECK(c.u64 == 0xD835C455069D81EF);
+
+ RX_EXECUTE_U64(0x8000000000000000, 1, ROR_64);
+ CHECK(c.u64 == 0x4000000000000000);
+}
+
+TEST_CASE("Denormal results are not produced", "[FTZ]") {
+ FPINIT();
+ convertible_t a, b, c;
+ a.i64 = 2048;
+ FPDIV(a, DBL_MAX, c);
+#ifdef DEBUG
+ std::cout << a.i64 << " / " << DBL_MAX << " = " << std::hex << c.u64 << std::endl;
+#endif
+ REQUIRE(std::fpclassify(c.f64) != FP_SUBNORMAL);
+ b.f64 = c.f64;
+ a.i64 = 0;
+ FPSUB_64(a, b, c);
+#ifdef DEBUG
+ std::cout << a.i64 << " - " << b.f64 << " = " << std::hex << c.u64 << std::endl;
+#endif
+ CHECK(std::fpclassify(c.f64) != FP_SUBNORMAL);
+}
+
+TEST_CASE("NaN results are not produced", "[NAN]") {
+ FPINIT();
+ convertible_t a, c;
+ a.i64 = 0;
+ FPDIV(a, 0, c);
+ CHECK(std::fpclassify(c.f64) != FP_NAN);
+ FPMUL(a, std::numeric_limits::infinity(), c);
+ CHECK(std::fpclassify(c.f64) != FP_NAN);
+}
+
+volatile int64_t fpAdda = 7379480244170225589;
+volatile int64_t fpAddb = -438072579179686797;
+volatile int64_t fpSuba = 2939258788088626026;
+volatile int64_t fpSubb = 4786131045320678734;
+volatile int64_t fpMula1 = 8399833736388895639;
+volatile int64_t fpMulb1 = 5671608020317594922;
+volatile int64_t fpMula2 = -7094299423744805450;
+volatile int64_t fpMulb2 = 4982086006202596504;
+volatile int64_t fpDiva1 = 8399833736388895639;
+volatile int64_t fpDivb1 = 5671608020317594922;
+volatile int64_t fpDiva2 = -7434878587645025912;
+volatile int64_t fpDivb2 = 5266243837734830806;
+volatile int64_t fpSqrta = -7594301562963134542;
+
+TEST_CASE("IEEE-754 compliance", "[FPU]") {
+ FPINIT();
+ convertible_t a, b, c;
+
+ a.i64 = 2048;
+ FPDIV(a, 0, c);
+ CHECK(c.f64 == std::numeric_limits::infinity());
+
+ a.i64 = -2048;
+ FPDIV(a, 0, c);
+ CHECK(c.f64 == -std::numeric_limits::infinity());
+
+#ifdef DEBUG
+ std::cout << "FPROUND" << std::endl;
+#endif
+ CHECK(rxRound(RoundToNearest, fpAdda, 0, &FPROUND) == 0x43d99a4b8bc531dcU);
+ CHECK(rxRound(RoundDown, fpAdda, 0, &FPROUND) == 0x43d99a4b8bc531dcU);
+ CHECK(rxRound(RoundUp, fpAdda, 0, &FPROUND) == 0x43d99a4b8bc531dcU);
+ CHECK(rxRound(RoundToZero, fpAdda, 0, &FPROUND) == 0x43d99a4b8bc531dcU);
+
+ CHECK(rxRound(RoundToNearest, fpSuba, 0, &FPROUND) == 0x43c4652c25bf7bdcU);
+ CHECK(rxRound(RoundDown, fpSuba, 0, &FPROUND) == 0x43c4652c25bf7bdcU);
+ CHECK(rxRound(RoundUp, fpSuba, 0, &FPROUND) == 0x43c4652c25bf7bdcU);
+ CHECK(rxRound(RoundToZero, fpSuba, 0, &FPROUND) == 0x43c4652c25bf7bdcU);
+
+#ifdef DEBUG
+ std::cout << "FPADD" << std::endl;
+#endif
+ CHECK(rxRound(RoundToNearest, fpAdda, fpAddb, &FPADD_64) == 0xf9eba74f6c27d473U);
+ CHECK(rxRound(RoundDown, fpAdda, fpAddb, &FPADD_64) == 0xf9eba74f6c27d473U);
+ CHECK(rxRound(RoundUp, fpAdda, fpAddb, &FPADD_64) == 0xf9eba74f6c27d472U);
+ CHECK(rxRound(RoundToZero, fpAdda, fpAddb, &FPADD_64) == 0xf9eba74f6c27d472U);
+
+#ifdef DEBUG
+ std::cout << "FPSUB" << std::endl;
+#endif
+ CHECK(rxRound(RoundToNearest, fpSuba, fpSubb, &FPSUB_64) == 0x43c4652bb6bc2c49U);
+ CHECK(rxRound(RoundDown, fpSuba, fpSubb, &FPSUB_64) == 0x43c4652bb6bc2c48U);
+ CHECK(rxRound(RoundUp, fpSuba, fpSubb, &FPSUB_64) == 0x43c4652bb6bc2c49U);
+ CHECK(rxRound(RoundToZero, fpSuba, fpSubb, &FPSUB_64) == 0x43c4652bb6bc2c48U);
+
+#ifdef DEBUG
+ std::cout << "FPMUL" << std::endl;
+#endif
+ CHECK(rxRound(RoundToNearest, fpMula1, fpMulb1, &FPMUL_64) == 0x52a3abbb1677f3e9U);
+ CHECK(rxRound(RoundDown, fpMula1, fpMulb1, &FPMUL_64) == 0x52a3abbb1677f3e8U);
+ CHECK(rxRound(RoundUp, fpMula1, fpMulb1, &FPMUL_64) == 0x52a3abbb1677f3e9U);
+ CHECK(rxRound(RoundToZero, fpMula1, fpMulb1, &FPMUL_64) == 0x52a3abbb1677f3e8U);
+
+ CHECK(rxRound(RoundToNearest, fpMula2, fpMulb2, &FPMUL_64) == 0xc90ea6c25e29c583U);
+ CHECK(rxRound(RoundDown, fpMula2, fpMulb2, &FPMUL_64) == 0xc90ea6c25e29c583U);
+ CHECK(rxRound(RoundUp, fpMula2, fpMulb2, &FPMUL_64) == 0xc90ea6c25e29c582U);
+ CHECK(rxRound(RoundToZero, fpMula2, fpMulb2, &FPMUL_64) == 0xc90ea6c25e29c582U);
+
+#ifdef DEBUG
+ std::cout << "FPDIV" << std::endl;
+#endif
+ CHECK(rxRound(RoundToNearest, fpDiva1, fpDivb1, &FPDIV_64) == 0x3515967d3015e81cU);
+ CHECK(rxRound(RoundDown, fpDiva1, fpDivb1, &FPDIV_64) == 0x3515967d3015e81bU);
+ CHECK(rxRound(RoundUp, fpDiva1, fpDivb1, &FPDIV_64) == 0x3515967d3015e81cU);
+ CHECK(rxRound(RoundToZero, fpDiva1, fpDivb1, &FPDIV_64) == 0x3515967d3015e81bU);
+
+ CHECK(rxRound(RoundToNearest, fpDiva2, fpDivb2, &FPDIV_64) == 0xbab33c30b92b8fccU);
+ CHECK(rxRound(RoundDown, fpDiva2, fpDivb2, &FPDIV_64) == 0xbab33c30b92b8fccU);
+ CHECK(rxRound(RoundUp, fpDiva2, fpDivb2, &FPDIV_64) == 0xbab33c30b92b8fcbU);
+ CHECK(rxRound(RoundToZero, fpDiva2, fpDivb2, &FPDIV_64) == 0xbab33c30b92b8fcbU);
+
+#ifdef DEBUG
+ std::cout << "FPSQRT" << std::endl;
+#endif
+ CHECK(rxRound(RoundToNearest, fpSqrta, 0, &FPSQRT) == 0x41d304e3fcc31a2dU);
+ CHECK(rxRound(RoundDown, fpSqrta, 0, &FPSQRT) == 0x41d304e3fcc31a2cU);
+ CHECK(rxRound(RoundUp, fpSqrta, 0, &FPSQRT) == 0x41d304e3fcc31a2dU);
+ CHECK(rxRound(RoundToZero, fpSqrta, 0, &FPSQRT) == 0x41d304e3fcc31a2cU);
+}
diff --git a/src/VirtualMachine.cpp b/src/VirtualMachine.cpp
new file mode 100644
index 0000000..05b22ac
--- /dev/null
+++ b/src/VirtualMachine.cpp
@@ -0,0 +1,77 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+#include "VirtualMachine.hpp"
+#include "common.hpp"
+#include "dataset.hpp"
+#include
+
+namespace RandomX {
+ VirtualMachine::VirtualMachine(bool softAes) : softAes(softAes), lightClient(false) {
+ mem.dataset = nullptr;
+ }
+
+ void VirtualMachine::initializeDataset(const void* seed, bool light) {
+ if (lightClient) {
+ _mm_free(mem.lcm->cache);
+ _mm_free(mem.lcm->block);
+ }
+ _mm_free(mem.dataset);
+ lightClient = light;
+ if (light) {
+ if (softAes) {
+ datasetInitLight(seed, mem.lcm);
+ readDataset = &datasetReadLight;
+ }
+ else {
+ datasetInitLight(seed, mem.lcm);
+ readDataset = &datasetReadLight;
+ }
+ }
+ else {
+ readDataset = &datasetRead;
+ if (softAes) {
+ datasetInit(seed, mem.dataset);
+ }
+ else {
+ datasetInit(seed, mem.dataset);
+ }
+ }
+ }
+
+ void VirtualMachine::initializeScratchpad(uint32_t index) {
+ if (lightClient) {
+ if (softAes) {
+ initBlock(mem.lcm->cache, ((uint8_t*)scratchpad) + DatasetBlockSize * 0, 4 * index + 0, mem.lcm->keys);
+ initBlock(mem.lcm->cache, ((uint8_t*)scratchpad) + DatasetBlockSize * 1, 4 * index + 1, mem.lcm->keys);
+ initBlock(mem.lcm->cache, ((uint8_t*)scratchpad) + DatasetBlockSize * 2, 4 * index + 2, mem.lcm->keys);
+ initBlock(mem.lcm->cache, ((uint8_t*)scratchpad) + DatasetBlockSize * 3, 4 * index + 3, mem.lcm->keys);
+ }
+ else {
+ initBlock(mem.lcm->cache, ((uint8_t*)scratchpad) + DatasetBlockSize * 0, 4 * index + 0, mem.lcm->keys);
+ initBlock(mem.lcm->cache, ((uint8_t*)scratchpad) + DatasetBlockSize * 1, 4 * index + 1, mem.lcm->keys);
+ initBlock(mem.lcm->cache, ((uint8_t*)scratchpad) + DatasetBlockSize * 2, 4 * index + 2, mem.lcm->keys);
+ initBlock(mem.lcm->cache, ((uint8_t*)scratchpad) + DatasetBlockSize * 3, 4 * index + 3, mem.lcm->keys);
+ }
+ }
+ else {
+ memcpy(scratchpad, mem.dataset + ScratchpadSize * index, ScratchpadSize);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/VirtualMachine.hpp b/src/VirtualMachine.hpp
new file mode 100644
index 0000000..79e9f58
--- /dev/null
+++ b/src/VirtualMachine.hpp
@@ -0,0 +1,61 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+#pragma once
+#include
+#include "common.hpp"
+
+namespace RandomX {
+
+ typedef convertible_t(*DatasetReadFunc)(addr_t, MemoryRegisters&);
+
+ class VirtualMachine {
+ public:
+ VirtualMachine(bool softAes);
+ virtual ~VirtualMachine() {}
+ void initializeDataset(const void* seed, bool light = false);
+ void initializeScratchpad(uint32_t index);
+ virtual void initializeProgram(const void* seed) = 0;
+ virtual void execute() = 0;
+ const RegisterFile& getRegisterFile() const {
+ return reg;
+ }
+ const convertible_t* getScratchpad() const {
+ return scratchpad;
+ }
+ const void* getCache() {
+ if (lightClient) {
+ return mem.lcm->cache;
+ }
+ return nullptr;
+ }
+ const __m128i* getKeys() {
+ if (lightClient) {
+ return mem.lcm->keys;
+ }
+ return nullptr;
+ }
+ protected:
+ bool softAes, lightClient;
+ RegisterFile reg;
+ MemoryRegisters mem;
+ DatasetReadFunc readDataset;
+ alignas(16) convertible_t scratchpad[ScratchpadLength];
+ };
+}
\ No newline at end of file
diff --git a/src/argon2.h b/src/argon2.h
new file mode 100644
index 0000000..e416cec
--- /dev/null
+++ b/src/argon2.h
@@ -0,0 +1,220 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+/* Original code from Argon2 reference source code package used under CC0 Licence
+ * https://github.com/P-H-C/phc-winner-argon2
+ * Copyright 2015
+ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
+*/
+
+#pragma once
+
+#include
+#include
+#include
+
+/*
+ * Argon2 input parameter restrictions
+ */
+
+ /* Minimum and maximum number of lanes (degree of parallelism) */
+#define ARGON2_MIN_LANES UINT32_C(1)
+#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF)
+
+/* Minimum and maximum number of threads */
+#define ARGON2_MIN_THREADS UINT32_C(1)
+#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF)
+
+/* Number of synchronization points between lanes per pass */
+#define ARGON2_SYNC_POINTS UINT32_C(4)
+
+/* Minimum and maximum digest size in bytes */
+#define ARGON2_MIN_OUTLEN UINT32_C(4)
+#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */
+#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */
+
+#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
+/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */
+#define ARGON2_MAX_MEMORY_BITS \
+ ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1))
+#define ARGON2_MAX_MEMORY \
+ ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS)
+
+/* Minimum and maximum number of passes */
+#define ARGON2_MIN_TIME UINT32_C(1)
+#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum password length in bytes */
+#define ARGON2_MIN_PWD_LENGTH UINT32_C(0)
+#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum associated data length in bytes */
+#define ARGON2_MIN_AD_LENGTH UINT32_C(0)
+#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum salt length in bytes */
+#define ARGON2_MIN_SALT_LENGTH UINT32_C(8)
+#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF)
+
+/* Minimum and maximum key length in bytes */
+#define ARGON2_MIN_SECRET UINT32_C(0)
+#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF)
+
+/* Flags to determine which fields are securely wiped (default = no wipe). */
+#define ARGON2_DEFAULT_FLAGS UINT32_C(0)
+#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0)
+#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1)
+
+
+/* Error codes */
+typedef enum Argon2_ErrorCodes {
+ ARGON2_OK = 0,
+
+ ARGON2_OUTPUT_PTR_NULL = -1,
+
+ ARGON2_OUTPUT_TOO_SHORT = -2,
+ ARGON2_OUTPUT_TOO_LONG = -3,
+
+ ARGON2_PWD_TOO_SHORT = -4,
+ ARGON2_PWD_TOO_LONG = -5,
+
+ ARGON2_SALT_TOO_SHORT = -6,
+ ARGON2_SALT_TOO_LONG = -7,
+
+ ARGON2_AD_TOO_SHORT = -8,
+ ARGON2_AD_TOO_LONG = -9,
+
+ ARGON2_SECRET_TOO_SHORT = -10,
+ ARGON2_SECRET_TOO_LONG = -11,
+
+ ARGON2_TIME_TOO_SMALL = -12,
+ ARGON2_TIME_TOO_LARGE = -13,
+
+ ARGON2_MEMORY_TOO_LITTLE = -14,
+ ARGON2_MEMORY_TOO_MUCH = -15,
+
+ ARGON2_LANES_TOO_FEW = -16,
+ ARGON2_LANES_TOO_MANY = -17,
+
+ ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */
+ ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */
+ ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */
+ ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */
+
+ ARGON2_MEMORY_ALLOCATION_ERROR = -22,
+
+ ARGON2_FREE_MEMORY_CBK_NULL = -23,
+ ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24,
+
+ ARGON2_INCORRECT_PARAMETER = -25,
+ ARGON2_INCORRECT_TYPE = -26,
+
+ ARGON2_OUT_PTR_MISMATCH = -27,
+
+ ARGON2_THREADS_TOO_FEW = -28,
+ ARGON2_THREADS_TOO_MANY = -29,
+
+ ARGON2_MISSING_ARGS = -30,
+
+ ARGON2_ENCODING_FAIL = -31,
+
+ ARGON2_DECODING_FAIL = -32,
+
+ ARGON2_THREAD_FAIL = -33,
+
+ ARGON2_DECODING_LENGTH_FAIL = -34,
+
+ ARGON2_VERIFY_MISMATCH = -35
+} argon2_error_codes;
+
+/* Memory allocator types --- for external allocation */
+typedef int(*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate);
+typedef void(*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate);
+
+/* Argon2 external data structures */
+
+/*
+ *****
+ * Context: structure to hold Argon2 inputs:
+ * output array and its length,
+ * password and its length,
+ * salt and its length,
+ * secret and its length,
+ * associated data and its length,
+ * number of passes, amount of used memory (in KBytes, can be rounded up a bit)
+ * number of parallel threads that will be run.
+ * All the parameters above affect the output hash value.
+ * Additionally, two function pointers can be provided to allocate and
+ * deallocate the memory (if NULL, memory will be allocated internally).
+ * Also, three flags indicate whether to erase password, secret as soon as they
+ * are pre-hashed (and thus not needed anymore), and the entire memory
+ *****
+ * Simplest situation: you have output array out[8], password is stored in
+ * pwd[32], salt is stored in salt[16], you do not have keys nor associated
+ * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with
+ * 4 parallel lanes.
+ * You want to erase the password, but you're OK with last pass not being
+ * erased. You want to use the default memory allocator.
+ * Then you initialize:
+ Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false)
+ */
+typedef struct Argon2_Context {
+ uint8_t *out; /* output array */
+ uint32_t outlen; /* digest length */
+
+ uint8_t *pwd; /* password array */
+ uint32_t pwdlen; /* password length */
+
+ uint8_t *salt; /* salt array */
+ uint32_t saltlen; /* salt length */
+
+ uint8_t *secret; /* key array */
+ uint32_t secretlen; /* key length */
+
+ uint8_t *ad; /* associated data array */
+ uint32_t adlen; /* associated data length */
+
+ uint32_t t_cost; /* number of passes */
+ uint32_t m_cost; /* amount of memory requested (KB) */
+ uint32_t lanes; /* number of lanes */
+ uint32_t threads; /* maximum number of threads */
+
+ uint32_t version; /* version number */
+
+ allocate_fptr allocate_cbk; /* pointer to memory allocator */
+ deallocate_fptr free_cbk; /* pointer to memory deallocator */
+
+ uint32_t flags; /* array of bool options */
+} argon2_context;
+
+/* Argon2 primitive type */
+typedef enum Argon2_type {
+ Argon2_d = 0,
+ Argon2_i = 1,
+ Argon2_id = 2
+} argon2_type;
+
+/* Version of the algorithm */
+typedef enum Argon2_version {
+ ARGON2_VERSION_10 = 0x10,
+ ARGON2_VERSION_13 = 0x13,
+ ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
+} argon2_version;
diff --git a/src/argon2_core.c b/src/argon2_core.c
new file mode 100644
index 0000000..f90a0d7
--- /dev/null
+++ b/src/argon2_core.c
@@ -0,0 +1,507 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+/* Original code from Argon2 reference source code package used under CC0 Licence
+ * https://github.com/P-H-C/phc-winner-argon2
+ * Copyright 2015
+ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
+*/
+
+ /*For memory wiping*/
+#ifdef _MSC_VER
+#include
+#include /* For SecureZeroMemory */
+#endif
+#if defined __STDC_LIB_EXT1__
+#define __STDC_WANT_LIB_EXT1__ 1
+#endif
+#define VC_GE_2005(version) (version >= 1400)
+
+#include
+#include
+#include
+
+#include "argon2_core.h"
+#include "blake2/blake2.h"
+#include "blake2/blake2-impl.h"
+
+#ifdef GENKAT
+#include "genkat.h"
+#endif
+
+#if defined(__clang__)
+#if __has_attribute(optnone)
+#define NOT_OPTIMIZED __attribute__((optnone))
+#endif
+#elif defined(__GNUC__)
+#define GCC_VERSION \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION >= 40400
+#define NOT_OPTIMIZED __attribute__((optimize("O0")))
+#endif
+#endif
+#ifndef NOT_OPTIMIZED
+#define NOT_OPTIMIZED
+#endif
+
+/***************Instance and Position constructors**********/
+void init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); }
+
+void copy_block(block *dst, const block *src) {
+ memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK);
+}
+
+void xor_block(block *dst, const block *src) {
+ int i;
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
+ dst->v[i] ^= src->v[i];
+ }
+}
+
+static void load_block(block *dst, const void *input) {
+ unsigned i;
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
+ dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i]));
+ }
+}
+
+static void store_block(void *output, const block *src) {
+ unsigned i;
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
+ store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
+ }
+}
+
+/***************Memory functions*****************/
+
+int allocate_memory(const argon2_context *context, uint8_t **memory,
+ size_t num, size_t size) {
+ size_t memory_size = num * size;
+ if (memory == NULL) {
+ return ARGON2_MEMORY_ALLOCATION_ERROR;
+ }
+
+ /* 1. Check for multiplication overflow */
+ if (size != 0 && memory_size / size != num) {
+ return ARGON2_MEMORY_ALLOCATION_ERROR;
+ }
+
+ /* 2. Try to allocate with appropriate allocator */
+ if (context->allocate_cbk) {
+ (context->allocate_cbk)(memory, memory_size);
+ }
+ else {
+ *memory = (uint8_t*)malloc(memory_size);
+ }
+
+ if (*memory == NULL) {
+ return ARGON2_MEMORY_ALLOCATION_ERROR;
+ }
+
+ return ARGON2_OK;
+}
+
+void free_memory(const argon2_context *context, uint8_t *memory,
+ size_t num, size_t size) {
+ size_t memory_size = num * size;
+ clear_internal_memory(memory, memory_size);
+ if (context->free_cbk) {
+ (context->free_cbk)(memory, memory_size);
+ }
+ else {
+ free(memory);
+ }
+}
+
+void NOT_OPTIMIZED secure_wipe_memory(void *v, size_t n) {
+#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER)
+ SecureZeroMemory(v, n);
+#elif defined memset_s
+ memset_s(v, n, 0, n);
+#elif defined(__OpenBSD__)
+ explicit_bzero(v, n);
+#else
+ static void *(*const volatile memset_sec)(void *, int, size_t) = &memset;
+ memset_sec(v, 0, n);
+#endif
+}
+
+/* Memory clear flag defaults to true. */
+#define FLAG_clear_internal_memory 0
+void clear_internal_memory(void *v, size_t n) {
+ if (FLAG_clear_internal_memory && v) {
+ secure_wipe_memory(v, n);
+ }
+}
+
+uint32_t index_alpha(const argon2_instance_t *instance,
+ const argon2_position_t *position, uint32_t pseudo_rand,
+ int same_lane) {
+ /*
+ * Pass 0:
+ * This lane : all already finished segments plus already constructed
+ * blocks in this segment
+ * Other lanes : all already finished segments
+ * Pass 1+:
+ * This lane : (SYNC_POINTS - 1) last segments plus already constructed
+ * blocks in this segment
+ * Other lanes : (SYNC_POINTS - 1) last segments
+ */
+ uint32_t reference_area_size;
+ uint64_t relative_position;
+ uint32_t start_position, absolute_position;
+
+ if (0 == position->pass) {
+ /* First pass */
+ if (0 == position->slice) {
+ /* First slice */
+ reference_area_size =
+ position->index - 1; /* all but the previous */
+ }
+ else {
+ if (same_lane) {
+ /* The same lane => add current segment */
+ reference_area_size =
+ position->slice * instance->segment_length +
+ position->index - 1;
+ }
+ else {
+ reference_area_size =
+ position->slice * instance->segment_length +
+ ((position->index == 0) ? (-1) : 0);
+ }
+ }
+ }
+ else {
+ /* Second pass */
+ if (same_lane) {
+ reference_area_size = instance->lane_length -
+ instance->segment_length + position->index -
+ 1;
+ }
+ else {
+ reference_area_size = instance->lane_length -
+ instance->segment_length +
+ ((position->index == 0) ? (-1) : 0);
+ }
+ }
+
+ /* 1.2.4. Mapping pseudo_rand to 0.. and produce
+ * relative position */
+ relative_position = pseudo_rand;
+ relative_position = relative_position * relative_position >> 32;
+ relative_position = reference_area_size - 1 -
+ (reference_area_size * relative_position >> 32);
+
+ /* 1.2.5 Computing starting position */
+ start_position = 0;
+
+ if (0 != position->pass) {
+ start_position = (position->slice == ARGON2_SYNC_POINTS - 1)
+ ? 0
+ : (position->slice + 1) * instance->segment_length;
+ }
+
+ /* 1.2.6. Computing absolute position */
+ absolute_position = (start_position + relative_position) %
+ instance->lane_length; /* absolute position */
+ return absolute_position;
+}
+
+/* Single-threaded version for p=1 case */
+static int fill_memory_blocks_st(argon2_instance_t *instance) {
+ uint32_t r, s, l;
+
+ for (r = 0; r < instance->passes; ++r) {
+ for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
+ for (l = 0; l < instance->lanes; ++l) {
+ argon2_position_t position = { r, l, (uint8_t)s, 0 };
+ fill_segment(instance, position);
+ }
+ }
+#ifdef GENKAT
+ internal_kat(instance, r); /* Print all memory blocks */
+#endif
+ }
+ return ARGON2_OK;
+}
+
+int fill_memory_blocks(argon2_instance_t *instance) {
+ if (instance == NULL || instance->lanes == 0) {
+ return ARGON2_INCORRECT_PARAMETER;
+ }
+ return fill_memory_blocks_st(instance);
+}
+
+int validate_inputs(const argon2_context *context) {
+ if (NULL == context) {
+ return ARGON2_INCORRECT_PARAMETER;
+ }
+
+ if (NULL == context->out) {
+ return ARGON2_OUTPUT_PTR_NULL;
+ }
+
+ /* Validate output length */
+ if (ARGON2_MIN_OUTLEN > context->outlen) {
+ return ARGON2_OUTPUT_TOO_SHORT;
+ }
+
+ if (ARGON2_MAX_OUTLEN < context->outlen) {
+ return ARGON2_OUTPUT_TOO_LONG;
+ }
+
+ /* Validate password (required param) */
+ if (NULL == context->pwd) {
+ if (0 != context->pwdlen) {
+ return ARGON2_PWD_PTR_MISMATCH;
+ }
+ }
+
+ if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) {
+ return ARGON2_PWD_TOO_SHORT;
+ }
+
+ if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) {
+ return ARGON2_PWD_TOO_LONG;
+ }
+
+ /* Validate salt (required param) */
+ if (NULL == context->salt) {
+ if (0 != context->saltlen) {
+ return ARGON2_SALT_PTR_MISMATCH;
+ }
+ }
+
+ if (ARGON2_MIN_SALT_LENGTH > context->saltlen) {
+ return ARGON2_SALT_TOO_SHORT;
+ }
+
+ if (ARGON2_MAX_SALT_LENGTH < context->saltlen) {
+ return ARGON2_SALT_TOO_LONG;
+ }
+
+ /* Validate secret (optional param) */
+ if (NULL == context->secret) {
+ if (0 != context->secretlen) {
+ return ARGON2_SECRET_PTR_MISMATCH;
+ }
+ }
+ else {
+ if (ARGON2_MIN_SECRET > context->secretlen) {
+ return ARGON2_SECRET_TOO_SHORT;
+ }
+ if (ARGON2_MAX_SECRET < context->secretlen) {
+ return ARGON2_SECRET_TOO_LONG;
+ }
+ }
+
+ /* Validate associated data (optional param) */
+ if (NULL == context->ad) {
+ if (0 != context->adlen) {
+ return ARGON2_AD_PTR_MISMATCH;
+ }
+ }
+ else {
+ if (ARGON2_MIN_AD_LENGTH > context->adlen) {
+ return ARGON2_AD_TOO_SHORT;
+ }
+ if (ARGON2_MAX_AD_LENGTH < context->adlen) {
+ return ARGON2_AD_TOO_LONG;
+ }
+ }
+
+ /* Validate memory cost */
+ if (ARGON2_MIN_MEMORY > context->m_cost) {
+ return ARGON2_MEMORY_TOO_LITTLE;
+ }
+
+ if (ARGON2_MAX_MEMORY < context->m_cost) {
+ return ARGON2_MEMORY_TOO_MUCH;
+ }
+
+ if (context->m_cost < 8 * context->lanes) {
+ return ARGON2_MEMORY_TOO_LITTLE;
+ }
+
+ /* Validate time cost */
+ if (ARGON2_MIN_TIME > context->t_cost) {
+ return ARGON2_TIME_TOO_SMALL;
+ }
+
+ if (ARGON2_MAX_TIME < context->t_cost) {
+ return ARGON2_TIME_TOO_LARGE;
+ }
+
+ /* Validate lanes */
+ if (ARGON2_MIN_LANES > context->lanes) {
+ return ARGON2_LANES_TOO_FEW;
+ }
+
+ if (ARGON2_MAX_LANES < context->lanes) {
+ return ARGON2_LANES_TOO_MANY;
+ }
+
+ /* Validate threads */
+ if (ARGON2_MIN_THREADS > context->threads) {
+ return ARGON2_THREADS_TOO_FEW;
+ }
+
+ if (ARGON2_MAX_THREADS < context->threads) {
+ return ARGON2_THREADS_TOO_MANY;
+ }
+
+ if (NULL != context->allocate_cbk && NULL == context->free_cbk) {
+ return ARGON2_FREE_MEMORY_CBK_NULL;
+ }
+
+ if (NULL == context->allocate_cbk && NULL != context->free_cbk) {
+ return ARGON2_ALLOCATE_MEMORY_CBK_NULL;
+ }
+
+ return ARGON2_OK;
+}
+
+void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) {
+ uint32_t l;
+ /* Make the first and second block in each lane as G(H0||0||i) or
+ G(H0||1||i) */
+ uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
+ for (l = 0; l < instance->lanes; ++l) {
+
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l);
+ blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
+ ARGON2_PREHASH_SEED_LENGTH);
+ load_block(&instance->memory[l * instance->lane_length + 0],
+ blockhash_bytes);
+
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
+ blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
+ ARGON2_PREHASH_SEED_LENGTH);
+ load_block(&instance->memory[l * instance->lane_length + 1],
+ blockhash_bytes);
+ }
+ clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
+}
+
+void initial_hash(uint8_t *blockhash, argon2_context *context, argon2_type type) {
+ blake2b_state BlakeHash;
+ uint8_t value[sizeof(uint32_t)];
+
+ if (NULL == context || NULL == blockhash) {
+ return;
+ }
+
+ blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH);
+
+ store32(&value, context->lanes);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->outlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->m_cost);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->t_cost);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->version);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, (uint32_t)type);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ store32(&value, context->pwdlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ if (context->pwd != NULL) {
+ blake2b_update(&BlakeHash, (const uint8_t *)context->pwd,
+ context->pwdlen);
+
+ if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) {
+ secure_wipe_memory(context->pwd, context->pwdlen);
+ context->pwdlen = 0;
+ }
+ }
+
+ store32(&value, context->saltlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ if (context->salt != NULL) {
+ blake2b_update(&BlakeHash, (const uint8_t *)context->salt, context->saltlen);
+ }
+
+ store32(&value, context->secretlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ if (context->secret != NULL) {
+ blake2b_update(&BlakeHash, (const uint8_t *)context->secret,
+ context->secretlen);
+
+ if (context->flags & ARGON2_FLAG_CLEAR_SECRET) {
+ secure_wipe_memory(context->secret, context->secretlen);
+ context->secretlen = 0;
+ }
+ }
+
+ store32(&value, context->adlen);
+ blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+
+ if (context->ad != NULL) {
+ blake2b_update(&BlakeHash, (const uint8_t *)context->ad,
+ context->adlen);
+ }
+
+ blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH);
+}
+
+int initialize(argon2_instance_t *instance, argon2_context *context) {
+ uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
+ int result = ARGON2_OK;
+
+ if (instance == NULL || context == NULL)
+ return ARGON2_INCORRECT_PARAMETER;
+ instance->context_ptr = context;
+
+ /* 1. Memory allocation */
+ /*result = allocate_memory(context, (uint8_t **)&(instance->memory), instance->memory_blocks, sizeof(block));
+ if (result != ARGON2_OK) {
+ return result;
+ }*/
+
+ /* 2. Initial hashing */
+ /* H_0 + 8 extra bytes to produce the first blocks */
+ /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */
+ /* Hashing all inputs */
+ initial_hash(blockhash, context, instance->type);
+ /* Zeroing 8 extra bytes */
+ clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH,
+ ARGON2_PREHASH_SEED_LENGTH -
+ ARGON2_PREHASH_DIGEST_LENGTH);
+
+ /* 3. Creating first blocks, we always have at least two blocks in a slice
+ */
+ fill_first_blocks(blockhash, instance);
+ /* Clearing the hash */
+ clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH);
+
+ return ARGON2_OK;
+}
diff --git a/src/argon2_core.h b/src/argon2_core.h
new file mode 100644
index 0000000..6886fac
--- /dev/null
+++ b/src/argon2_core.h
@@ -0,0 +1,245 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+/* Original code from Argon2 reference source code package used under CC0 Licence
+ * https://github.com/P-H-C/phc-winner-argon2
+ * Copyright 2015
+ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
+*/
+
+#ifndef ARGON2_CORE_H
+#define ARGON2_CORE_H
+
+#include
+#include "argon2.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define CONST_CAST(x) (x)(uintptr_t)
+
+ /**********************Argon2 internal constants*******************************/
+
+enum argon2_core_constants {
+ /* Memory block size in bytes */
+ ARGON2_BLOCK_SIZE = 1024,
+ ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8,
+ ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16,
+ ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32,
+ ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64,
+
+ /* Number of pseudo-random values generated by one call to Blake in Argon2i
+ to
+ generate reference block positions */
+ ARGON2_ADDRESSES_IN_BLOCK = 128,
+
+ /* Pre-hashing digest length and its extension*/
+ ARGON2_PREHASH_DIGEST_LENGTH = 64,
+ ARGON2_PREHASH_SEED_LENGTH = 72
+};
+
+/*************************Argon2 internal data types***********************/
+
+/*
+ * Structure for the (1KB) memory block implemented as 128 64-bit words.
+ * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no
+ * bounds checking).
+ */
+typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block;
+
+/*****************Functions that work with the block******************/
+
+/* Initialize each byte of the block with @in */
+void init_block_value(block *b, uint8_t in);
+
+/* Copy block @src to block @dst */
+void copy_block(block *dst, const block *src);
+
+/* XOR @src onto @dst bytewise */
+void xor_block(block *dst, const block *src);
+
+/*
+ * Argon2 instance: memory pointer, number of passes, amount of memory, type,
+ * and derived values.
+ * Used to evaluate the number and location of blocks to construct in each
+ * thread
+ */
+typedef struct Argon2_instance_t {
+ block *memory; /* Memory pointer */
+ uint32_t version;
+ uint32_t passes; /* Number of passes */
+ uint32_t memory_blocks; /* Number of blocks in memory */
+ uint32_t segment_length;
+ uint32_t lane_length;
+ uint32_t lanes;
+ uint32_t threads;
+ argon2_type type;
+ int print_internals; /* whether to print the memory blocks */
+ argon2_context *context_ptr; /* points back to original context */
+} argon2_instance_t;
+
+/*
+ * Argon2 position: where we construct the block right now. Used to distribute
+ * work between threads.
+ */
+typedef struct Argon2_position_t {
+ uint32_t pass;
+ uint32_t lane;
+ uint8_t slice;
+ uint32_t index;
+} argon2_position_t;
+
+/*Struct that holds the inputs for thread handling FillSegment*/
+typedef struct Argon2_thread_data {
+ argon2_instance_t *instance_ptr;
+ argon2_position_t pos;
+} argon2_thread_data;
+
+/*************************Argon2 core functions********************************/
+
+/* Allocates memory to the given pointer, uses the appropriate allocator as
+ * specified in the context. Total allocated memory is num*size.
+ * @param context argon2_context which specifies the allocator
+ * @param memory pointer to the pointer to the memory
+ * @param size the size in bytes for each element to be allocated
+ * @param num the number of elements to be allocated
+ * @return ARGON2_OK if @memory is a valid pointer and memory is allocated
+ */
+int allocate_memory(const argon2_context *context, uint8_t **memory,
+ size_t num, size_t size);
+
+/*
+ * Frees memory at the given pointer, uses the appropriate deallocator as
+ * specified in the context. Also cleans the memory using clear_internal_memory.
+ * @param context argon2_context which specifies the deallocator
+ * @param memory pointer to buffer to be freed
+ * @param size the size in bytes for each element to be deallocated
+ * @param num the number of elements to be deallocated
+ */
+void free_memory(const argon2_context *context, uint8_t *memory,
+ size_t num, size_t size);
+
+/* Function that securely cleans the memory. This ignores any flags set
+ * regarding clearing memory. Usually one just calls clear_internal_memory.
+ * @param mem Pointer to the memory
+ * @param s Memory size in bytes
+ */
+void secure_wipe_memory(void *v, size_t n);
+
+/* Function that securely clears the memory if FLAG_clear_internal_memory is
+ * set. If the flag isn't set, this function does nothing.
+ * @param mem Pointer to the memory
+ * @param s Memory size in bytes
+ */
+void clear_internal_memory(void *v, size_t n);
+
+/*
+ * Computes absolute position of reference block in the lane following a skewed
+ * distribution and using a pseudo-random value as input
+ * @param instance Pointer to the current instance
+ * @param position Pointer to the current position
+ * @param pseudo_rand 32-bit pseudo-random value used to determine the position
+ * @param same_lane Indicates if the block will be taken from the current lane.
+ * If so we can reference the current segment
+ * @pre All pointers must be valid
+ */
+uint32_t index_alpha(const argon2_instance_t *instance,
+ const argon2_position_t *position, uint32_t pseudo_rand,
+ int same_lane);
+
+/*
+ * Function that validates all inputs against predefined restrictions and return
+ * an error code
+ * @param context Pointer to current Argon2 context
+ * @return ARGON2_OK if everything is all right, otherwise one of error codes
+ * (all defined in
+ */
+int validate_inputs(const argon2_context *context);
+
+/*
+ * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears
+ * password and secret if needed
+ * @param context Pointer to the Argon2 internal structure containing memory
+ * pointer, and parameters for time and space requirements.
+ * @param blockhash Buffer for pre-hashing digest
+ * @param type Argon2 type
+ * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes
+ * allocated
+ */
+void initial_hash(uint8_t *blockhash, argon2_context *context,
+ argon2_type type);
+
+/*
+ * Function creates first 2 blocks per lane
+ * @param instance Pointer to the current instance
+ * @param blockhash Pointer to the pre-hashing digest
+ * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values
+ */
+void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance);
+
+/*
+ * Function allocates memory, hashes the inputs with Blake, and creates first
+ * two blocks. Returns the pointer to the main memory with 2 blocks per lane
+ * initialized
+ * @param context Pointer to the Argon2 internal structure containing memory
+ * pointer, and parameters for time and space requirements.
+ * @param instance Current Argon2 instance
+ * @return Zero if successful, -1 if memory failed to allocate. @context->state
+ * will be modified if successful.
+ */
+int initialize(argon2_instance_t *instance, argon2_context *context);
+
+/*
+ * XORing the last block of each lane, hashing it, making the tag. Deallocates
+ * the memory.
+ * @param context Pointer to current Argon2 context (use only the out parameters
+ * from it)
+ * @param instance Pointer to current instance of Argon2
+ * @pre instance->state must point to necessary amount of memory
+ * @pre context->out must point to outlen bytes of memory
+ * @pre if context->free_cbk is not NULL, it should point to a function that
+ * deallocates memory
+ */
+void finalize(const argon2_context *context, argon2_instance_t *instance);
+
+/*
+ * Function that fills the segment using previous segments also from other
+ * threads
+ * @param context current context
+ * @param instance Pointer to the current instance
+ * @param position Current position
+ * @pre all block pointers must be valid
+ */
+void fill_segment(const argon2_instance_t *instance,
+ argon2_position_t position);
+
+/*
+ * Function that fills the entire memory t_cost times based on the first two
+ * blocks in each lane
+ * @param instance Pointer to the current instance
+ * @return ARGON2_OK if successful, @context->state
+ */
+int fill_memory_blocks(argon2_instance_t *instance);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/src/argon2_ref.c b/src/argon2_ref.c
new file mode 100644
index 0000000..f35f8e3
--- /dev/null
+++ b/src/argon2_ref.c
@@ -0,0 +1,205 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+/* Original code from Argon2 reference source code package used under CC0 Licence
+ * https://github.com/P-H-C/phc-winner-argon2
+ * Copyright 2015
+ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
+*/
+
+#include
+#include
+#include
+
+#include "argon2.h"
+#include "argon2_core.h"
+
+#include "blake2/blamka-round-ref.h"
+#include "blake2/blake2-impl.h"
+#include "blake2/blake2.h"
+
+ /*
+ * Function fills a new memory block and optionally XORs the old block over the new one.
+ * @next_block must be initialized.
+ * @param prev_block Pointer to the previous block
+ * @param ref_block Pointer to the reference block
+ * @param next_block Pointer to the block to be constructed
+ * @param with_xor Whether to XOR into the new block (1) or just overwrite (0)
+ * @pre all block pointers must be valid
+ */
+static void fill_block(const block *prev_block, const block *ref_block,
+ block *next_block, int with_xor) {
+ block blockR, block_tmp;
+ unsigned i;
+
+ copy_block(&blockR, ref_block);
+ xor_block(&blockR, prev_block);
+ copy_block(&block_tmp, &blockR);
+ /* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */
+ if (with_xor) {
+ /* Saving the next block contents for XOR over: */
+ xor_block(&block_tmp, next_block);
+ /* Now blockR = ref_block + prev_block and
+ block_tmp = ref_block + prev_block + next_block */
+ }
+
+ /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
+ (16,17,..31)... finally (112,113,...127) */
+ for (i = 0; i < 8; ++i) {
+ BLAKE2_ROUND_NOMSG(
+ blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2],
+ blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5],
+ blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8],
+ blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11],
+ blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14],
+ blockR.v[16 * i + 15]);
+ }
+
+ /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
+ (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
+ for (i = 0; i < 8; i++) {
+ BLAKE2_ROUND_NOMSG(
+ blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16],
+ blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33],
+ blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64],
+ blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81],
+ blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112],
+ blockR.v[2 * i + 113]);
+ }
+
+ copy_block(next_block, &block_tmp);
+ xor_block(next_block, &blockR);
+}
+
+static void next_addresses(block *address_block, block *input_block,
+ const block *zero_block) {
+ input_block->v[6]++;
+ fill_block(zero_block, input_block, address_block, 0);
+ fill_block(zero_block, address_block, address_block, 0);
+}
+
+void fill_segment(const argon2_instance_t *instance,
+ argon2_position_t position) {
+ block *ref_block = NULL, *curr_block = NULL;
+ block address_block, input_block, zero_block;
+ uint64_t pseudo_rand, ref_index, ref_lane;
+ uint32_t prev_offset, curr_offset;
+ uint32_t starting_index;
+ uint32_t i;
+ int data_independent_addressing;
+
+ if (instance == NULL) {
+ return;
+ }
+
+ data_independent_addressing =
+ (instance->type == Argon2_i) ||
+ (instance->type == Argon2_id && (position.pass == 0) &&
+ (position.slice < ARGON2_SYNC_POINTS / 2));
+
+ if (data_independent_addressing) {
+ init_block_value(&zero_block, 0);
+ init_block_value(&input_block, 0);
+
+ input_block.v[0] = position.pass;
+ input_block.v[1] = position.lane;
+ input_block.v[2] = position.slice;
+ input_block.v[3] = instance->memory_blocks;
+ input_block.v[4] = instance->passes;
+ input_block.v[5] = instance->type;
+ }
+
+ starting_index = 0;
+
+ if ((0 == position.pass) && (0 == position.slice)) {
+ starting_index = 2; /* we have already generated the first two blocks */
+
+ /* Don't forget to generate the first block of addresses: */
+ if (data_independent_addressing) {
+ next_addresses(&address_block, &input_block, &zero_block);
+ }
+ }
+
+ /* Offset of the current block */
+ curr_offset = position.lane * instance->lane_length +
+ position.slice * instance->segment_length + starting_index;
+
+ if (0 == curr_offset % instance->lane_length) {
+ /* Last block in this lane */
+ prev_offset = curr_offset + instance->lane_length - 1;
+ }
+ else {
+ /* Previous block */
+ prev_offset = curr_offset - 1;
+ }
+
+ for (i = starting_index; i < instance->segment_length;
+ ++i, ++curr_offset, ++prev_offset) {
+ /*1.1 Rotating prev_offset if needed */
+ if (curr_offset % instance->lane_length == 1) {
+ prev_offset = curr_offset - 1;
+ }
+
+ /* 1.2 Computing the index of the reference block */
+ /* 1.2.1 Taking pseudo-random value from the previous block */
+ if (data_independent_addressing) {
+ if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) {
+ next_addresses(&address_block, &input_block, &zero_block);
+ }
+ pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK];
+ }
+ else {
+ pseudo_rand = instance->memory[prev_offset].v[0];
+ }
+
+ /* 1.2.2 Computing the lane of the reference block */
+ ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
+
+ if ((position.pass == 0) && (position.slice == 0)) {
+ /* Can not reference other lanes yet */
+ ref_lane = position.lane;
+ }
+
+ /* 1.2.3 Computing the number of possible reference block within the
+ * lane.
+ */
+ position.index = i;
+ ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
+ ref_lane == position.lane);
+
+ /* 2 Creating a new block */
+ ref_block =
+ instance->memory + instance->lane_length * ref_lane + ref_index;
+ curr_block = instance->memory + curr_offset;
+ if (ARGON2_VERSION_10 == instance->version) {
+ /* version 1.2.1 and earlier: overwrite, not XOR */
+ fill_block(instance->memory + prev_offset, ref_block, curr_block, 0);
+ }
+ else {
+ if (0 == position.pass) {
+ fill_block(instance->memory + prev_offset, ref_block,
+ curr_block, 0);
+ }
+ else {
+ fill_block(instance->memory + prev_offset, ref_block,
+ curr_block, 1);
+ }
+ }
+ }
+}
diff --git a/src/blake2/blake2-impl.h b/src/blake2/blake2-impl.h
new file mode 100644
index 0000000..60b26fe
--- /dev/null
+++ b/src/blake2/blake2-impl.h
@@ -0,0 +1,162 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+/* Original code from Argon2 reference source code package used under CC0 Licence
+ * https://github.com/P-H-C/phc-winner-argon2
+ * Copyright 2015
+ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
+*/
+
+#ifndef PORTABLE_BLAKE2_IMPL_H
+#define PORTABLE_BLAKE2_IMPL_H
+
+#include
+#include
+
+#if defined(_MSC_VER)
+#define BLAKE2_INLINE __inline
+#elif defined(__GNUC__) || defined(__clang__)
+#define BLAKE2_INLINE __inline__
+#else
+#define BLAKE2_INLINE
+#endif
+
+ /* Argon2 Team - Begin Code */
+ /*
+ Not an exhaustive list, but should cover the majority of modern platforms
+ Additionally, the code will always be correct---this is only a performance
+ tweak.
+ */
+#if (defined(__BYTE_ORDER__) && \
+ (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \
+ defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
+ defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \
+ defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \
+ defined(_M_ARM)
+#define NATIVE_LITTLE_ENDIAN
+#endif
+ /* Argon2 Team - End Code */
+
+static BLAKE2_INLINE uint32_t load32(const void *src) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint32_t w;
+ memcpy(&w, src, sizeof w);
+ return w;
+#else
+ const uint8_t *p = (const uint8_t *)src;
+ uint32_t w = *p++;
+ w |= (uint32_t)(*p++) << 8;
+ w |= (uint32_t)(*p++) << 16;
+ w |= (uint32_t)(*p++) << 24;
+ return w;
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load64(const void *src) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint64_t w;
+ memcpy(&w, src, sizeof w);
+ return w;
+#else
+ const uint8_t *p = (const uint8_t *)src;
+ uint64_t w = *p++;
+ w |= (uint64_t)(*p++) << 8;
+ w |= (uint64_t)(*p++) << 16;
+ w |= (uint64_t)(*p++) << 24;
+ w |= (uint64_t)(*p++) << 32;
+ w |= (uint64_t)(*p++) << 40;
+ w |= (uint64_t)(*p++) << 48;
+ w |= (uint64_t)(*p++) << 56;
+ return w;
+#endif
+}
+
+static BLAKE2_INLINE void store32(void *dst, uint32_t w) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy(dst, &w, sizeof w);
+#else
+ uint8_t *p = (uint8_t *)dst;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+#endif
+}
+
+static BLAKE2_INLINE void store64(void *dst, uint64_t w) {
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy(dst, &w, sizeof w);
+#else
+ uint8_t *p = (uint8_t *)dst;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load48(const void *src) {
+ const uint8_t *p = (const uint8_t *)src;
+ uint64_t w = *p++;
+ w |= (uint64_t)(*p++) << 8;
+ w |= (uint64_t)(*p++) << 16;
+ w |= (uint64_t)(*p++) << 24;
+ w |= (uint64_t)(*p++) << 32;
+ w |= (uint64_t)(*p++) << 40;
+ return w;
+}
+
+static BLAKE2_INLINE void store48(void *dst, uint64_t w) {
+ uint8_t *p = (uint8_t *)dst;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+ w >>= 8;
+ *p++ = (uint8_t)w;
+}
+
+static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) {
+ return (w >> c) | (w << (32 - c));
+}
+
+static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) {
+ return (w >> c) | (w << (64 - c));
+}
+
+#endif
diff --git a/src/blake2/blake2.h b/src/blake2/blake2.h
new file mode 100644
index 0000000..e099731
--- /dev/null
+++ b/src/blake2/blake2.h
@@ -0,0 +1,98 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+/* Original code from Argon2 reference source code package used under CC0 Licence
+ * https://github.com/P-H-C/phc-winner-argon2
+ * Copyright 2015
+ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
+*/
+
+#ifndef PORTABLE_BLAKE2_H
+#define PORTABLE_BLAKE2_H
+
+#include
+#include
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+ enum blake2b_constant {
+ BLAKE2B_BLOCKBYTES = 128,
+ BLAKE2B_OUTBYTES = 64,
+ BLAKE2B_KEYBYTES = 64,
+ BLAKE2B_SALTBYTES = 16,
+ BLAKE2B_PERSONALBYTES = 16
+ };
+
+#pragma pack(push, 1)
+ typedef struct __blake2b_param {
+ uint8_t digest_length; /* 1 */
+ uint8_t key_length; /* 2 */
+ uint8_t fanout; /* 3 */
+ uint8_t depth; /* 4 */
+ uint32_t leaf_length; /* 8 */
+ uint64_t node_offset; /* 16 */
+ uint8_t node_depth; /* 17 */
+ uint8_t inner_length; /* 18 */
+ uint8_t reserved[14]; /* 32 */
+ uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
+ uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
+ } blake2b_param;
+#pragma pack(pop)
+
+ typedef struct __blake2b_state {
+ uint64_t h[8];
+ uint64_t t[2];
+ uint64_t f[2];
+ uint8_t buf[BLAKE2B_BLOCKBYTES];
+ unsigned buflen;
+ unsigned outlen;
+ uint8_t last_node;
+ } blake2b_state;
+
+ /* Ensure param structs have not been wrongly padded */
+ /* Poor man's static_assert */
+ enum {
+ blake2_size_check_0 = 1 / !!(CHAR_BIT == 8),
+ blake2_size_check_2 =
+ 1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT)
+ };
+
+ /* Streaming API */
+ int blake2b_init(blake2b_state *S, size_t outlen);
+ int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key,
+ size_t keylen);
+ int blake2b_init_param(blake2b_state *S, const blake2b_param *P);
+ int blake2b_update(blake2b_state *S, const void *in, size_t inlen);
+ int blake2b_final(blake2b_state *S, void *out, size_t outlen);
+
+ /* Simple API */
+ int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
+ const void *key, size_t keylen);
+
+ /* Argon2 Team - Begin Code */
+ int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen);
+ /* Argon2 Team - End Code */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/src/blake2/blake2b.c b/src/blake2/blake2b.c
new file mode 100644
index 0000000..e7569b4
--- /dev/null
+++ b/src/blake2/blake2b.c
@@ -0,0 +1,400 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+/* Original code from Argon2 reference source code package used under CC0 Licence
+ * https://github.com/P-H-C/phc-winner-argon2
+ * Copyright 2015
+ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
+*/
+
+#include
+#include
+#include
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+static const uint64_t blake2b_IV[8] = {
+ UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b),
+ UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1),
+ UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f),
+ UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179) };
+
+static const unsigned int blake2b_sigma[12][16] = {
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
+ {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
+ {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
+ {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
+ {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
+ {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
+ {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
+ {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
+ {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
+};
+
+static BLAKE2_INLINE void blake2b_set_lastnode(blake2b_state *S) {
+ S->f[1] = (uint64_t)-1;
+}
+
+static BLAKE2_INLINE void blake2b_set_lastblock(blake2b_state *S) {
+ if (S->last_node) {
+ blake2b_set_lastnode(S);
+ }
+ S->f[0] = (uint64_t)-1;
+}
+
+static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S,
+ uint64_t inc) {
+ S->t[0] += inc;
+ S->t[1] += (S->t[0] < inc);
+}
+
+static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) {
+ //clear_internal_memory(S, sizeof(*S)); /* wipe */
+ blake2b_set_lastblock(S); /* invalidate for further use */
+}
+
+static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) {
+ memset(S, 0, sizeof(*S));
+ memcpy(S->h, blake2b_IV, sizeof(S->h));
+}
+
+int blake2b_init_param(blake2b_state *S, const blake2b_param *P) {
+ const unsigned char *p = (const unsigned char *)P;
+ unsigned int i;
+
+ if (NULL == P || NULL == S) {
+ return -1;
+ }
+
+ blake2b_init0(S);
+ /* IV XOR Parameter Block */
+ for (i = 0; i < 8; ++i) {
+ S->h[i] ^= load64(&p[i * sizeof(S->h[i])]);
+ }
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+/* Sequential blake2b initialization */
+int blake2b_init(blake2b_state *S, size_t outlen) {
+ blake2b_param P;
+
+ if (S == NULL) {
+ return -1;
+ }
+
+ if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
+ blake2b_invalidate_state(S);
+ return -1;
+ }
+
+ /* Setup Parameter Block for unkeyed BLAKE2 */
+ P.digest_length = (uint8_t)outlen;
+ P.key_length = 0;
+ P.fanout = 1;
+ P.depth = 1;
+ P.leaf_length = 0;
+ P.node_offset = 0;
+ P.node_depth = 0;
+ P.inner_length = 0;
+ memset(P.reserved, 0, sizeof(P.reserved));
+ memset(P.salt, 0, sizeof(P.salt));
+ memset(P.personal, 0, sizeof(P.personal));
+
+ return blake2b_init_param(S, &P);
+}
+
+int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, size_t keylen) {
+ blake2b_param P;
+
+ if (S == NULL) {
+ return -1;
+ }
+
+ if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
+ blake2b_invalidate_state(S);
+ return -1;
+ }
+
+ if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) {
+ blake2b_invalidate_state(S);
+ return -1;
+ }
+
+ /* Setup Parameter Block for keyed BLAKE2 */
+ P.digest_length = (uint8_t)outlen;
+ P.key_length = (uint8_t)keylen;
+ P.fanout = 1;
+ P.depth = 1;
+ P.leaf_length = 0;
+ P.node_offset = 0;
+ P.node_depth = 0;
+ P.inner_length = 0;
+ memset(P.reserved, 0, sizeof(P.reserved));
+ memset(P.salt, 0, sizeof(P.salt));
+ memset(P.personal, 0, sizeof(P.personal));
+
+ if (blake2b_init_param(S, &P) < 0) {
+ blake2b_invalidate_state(S);
+ return -1;
+ }
+
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset(block, 0, BLAKE2B_BLOCKBYTES);
+ memcpy(block, key, keylen);
+ blake2b_update(S, block, BLAKE2B_BLOCKBYTES);
+ /* Burn the key from stack */
+ //clear_internal_memory(block, BLAKE2B_BLOCKBYTES);
+ }
+ return 0;
+}
+
+static void blake2b_compress(blake2b_state *S, const uint8_t *block) {
+ uint64_t m[16];
+ uint64_t v[16];
+ unsigned int i, r;
+
+ for (i = 0; i < 16; ++i) {
+ m[i] = load64(block + i * sizeof(m[i]));
+ }
+
+ for (i = 0; i < 8; ++i) {
+ v[i] = S->h[i];
+ }
+
+ v[8] = blake2b_IV[0];
+ v[9] = blake2b_IV[1];
+ v[10] = blake2b_IV[2];
+ v[11] = blake2b_IV[3];
+ v[12] = blake2b_IV[4] ^ S->t[0];
+ v[13] = blake2b_IV[5] ^ S->t[1];
+ v[14] = blake2b_IV[6] ^ S->f[0];
+ v[15] = blake2b_IV[7] ^ S->f[1];
+
+#define G(r, i, a, b, c, d) \
+ do { \
+ a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
+ d = rotr64(d ^ a, 32); \
+ c = c + d; \
+ b = rotr64(b ^ c, 24); \
+ a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
+ d = rotr64(d ^ a, 16); \
+ c = c + d; \
+ b = rotr64(b ^ c, 63); \
+ } while ((void)0, 0)
+
+#define ROUND(r) \
+ do { \
+ G(r, 0, v[0], v[4], v[8], v[12]); \
+ G(r, 1, v[1], v[5], v[9], v[13]); \
+ G(r, 2, v[2], v[6], v[10], v[14]); \
+ G(r, 3, v[3], v[7], v[11], v[15]); \
+ G(r, 4, v[0], v[5], v[10], v[15]); \
+ G(r, 5, v[1], v[6], v[11], v[12]); \
+ G(r, 6, v[2], v[7], v[8], v[13]); \
+ G(r, 7, v[3], v[4], v[9], v[14]); \
+ } while ((void)0, 0)
+
+ for (r = 0; r < 12; ++r) {
+ ROUND(r);
+ }
+
+ for (i = 0; i < 8; ++i) {
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+ }
+
+#undef G
+#undef ROUND
+}
+
+int blake2b_update(blake2b_state *S, const void *in, size_t inlen) {
+ const uint8_t *pin = (const uint8_t *)in;
+
+ if (inlen == 0) {
+ return 0;
+ }
+
+ /* Sanity check */
+ if (S == NULL || in == NULL) {
+ return -1;
+ }
+
+ /* Is this a reused state? */
+ if (S->f[0] != 0) {
+ return -1;
+ }
+
+ if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) {
+ /* Complete current block */
+ size_t left = S->buflen;
+ size_t fill = BLAKE2B_BLOCKBYTES - left;
+ memcpy(&S->buf[left], pin, fill);
+ blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
+ blake2b_compress(S, S->buf);
+ S->buflen = 0;
+ inlen -= fill;
+ pin += fill;
+ /* Avoid buffer copies when possible */
+ while (inlen > BLAKE2B_BLOCKBYTES) {
+ blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
+ blake2b_compress(S, pin);
+ inlen -= BLAKE2B_BLOCKBYTES;
+ pin += BLAKE2B_BLOCKBYTES;
+ }
+ }
+ memcpy(&S->buf[S->buflen], pin, inlen);
+ S->buflen += (unsigned int)inlen;
+ return 0;
+}
+
+int blake2b_final(blake2b_state *S, void *out, size_t outlen) {
+ uint8_t buffer[BLAKE2B_OUTBYTES] = { 0 };
+ unsigned int i;
+
+ /* Sanity checks */
+ if (S == NULL || out == NULL || outlen < S->outlen) {
+ return -1;
+ }
+
+ /* Is this a reused state? */
+ if (S->f[0] != 0) {
+ return -1;
+ }
+
+ blake2b_increment_counter(S, S->buflen);
+ blake2b_set_lastblock(S);
+ memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */
+ blake2b_compress(S, S->buf);
+
+ for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */
+ store64(buffer + sizeof(S->h[i]) * i, S->h[i]);
+ }
+
+ memcpy(out, buffer, S->outlen);
+ //clear_internal_memory(buffer, sizeof(buffer));
+ //clear_internal_memory(S->buf, sizeof(S->buf));
+ //clear_internal_memory(S->h, sizeof(S->h));
+ return 0;
+}
+
+int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
+ const void *key, size_t keylen) {
+ blake2b_state S;
+ int ret = -1;
+
+ /* Verify parameters */
+ if (NULL == in && inlen > 0) {
+ goto fail;
+ }
+
+ if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) {
+ goto fail;
+ }
+
+ if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) {
+ goto fail;
+ }
+
+ if (keylen > 0) {
+ if (blake2b_init_key(&S, outlen, key, keylen) < 0) {
+ goto fail;
+ }
+ }
+ else {
+ if (blake2b_init(&S, outlen) < 0) {
+ goto fail;
+ }
+ }
+
+ if (blake2b_update(&S, in, inlen) < 0) {
+ goto fail;
+ }
+ ret = blake2b_final(&S, out, outlen);
+
+fail:
+ //clear_internal_memory(&S, sizeof(S));
+ return ret;
+}
+
+/* Argon2 Team - Begin Code */
+int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) {
+ uint8_t *out = (uint8_t *)pout;
+ blake2b_state blake_state;
+ uint8_t outlen_bytes[sizeof(uint32_t)] = { 0 };
+ int ret = -1;
+
+ if (outlen > UINT32_MAX) {
+ goto fail;
+ }
+
+ /* Ensure little-endian byte order! */
+ store32(outlen_bytes, (uint32_t)outlen);
+
+#define TRY(statement) \
+ do { \
+ ret = statement; \
+ if (ret < 0) { \
+ goto fail; \
+ } \
+ } while ((void)0, 0)
+
+ if (outlen <= BLAKE2B_OUTBYTES) {
+ TRY(blake2b_init(&blake_state, outlen));
+ TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
+ TRY(blake2b_update(&blake_state, in, inlen));
+ TRY(blake2b_final(&blake_state, out, outlen));
+ }
+ else {
+ uint32_t toproduce;
+ uint8_t out_buffer[BLAKE2B_OUTBYTES];
+ uint8_t in_buffer[BLAKE2B_OUTBYTES];
+ TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES));
+ TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
+ TRY(blake2b_update(&blake_state, in, inlen));
+ TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES));
+ memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
+ out += BLAKE2B_OUTBYTES / 2;
+ toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2;
+
+ while (toproduce > BLAKE2B_OUTBYTES) {
+ memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
+ TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer,
+ BLAKE2B_OUTBYTES, NULL, 0));
+ memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
+ out += BLAKE2B_OUTBYTES / 2;
+ toproduce -= BLAKE2B_OUTBYTES / 2;
+ }
+
+ memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
+ TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL,
+ 0));
+ memcpy(out, out_buffer, toproduce);
+ }
+fail:
+ //clear_internal_memory(&blake_state, sizeof(blake_state));
+ return ret;
+#undef TRY
+}
+/* Argon2 Team - End Code */
+
diff --git a/src/blake2/blamka-round-ref.h b/src/blake2/blamka-round-ref.h
new file mode 100644
index 0000000..d7acd68
--- /dev/null
+++ b/src/blake2/blamka-round-ref.h
@@ -0,0 +1,64 @@
+/*
+Copyright (c) 2018 tevador
+
+This file is part of RandomX.
+
+RandomX 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 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. If not, see.
+*/
+
+/* Original code from Argon2 reference source code package used under CC0 Licence
+ * https://github.com/P-H-C/phc-winner-argon2
+ * Copyright 2015
+ * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
+*/
+
+#ifndef BLAKE_ROUND_MKA_H
+#define BLAKE_ROUND_MKA_H
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+ /* designed by the Lyra PHC team */
+static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) {
+ const uint64_t m = UINT64_C(0xFFFFFFFF);
+ const uint64_t xy = (x & m) * (y & m);
+ return x + y + 2 * xy;
+}
+
+#define G(a, b, c, d) \
+ do { \
+ a = fBlaMka(a, b); \
+ d = rotr64(d ^ a, 32); \
+ c = fBlaMka(c, d); \
+ b = rotr64(b ^ c, 24); \
+ a = fBlaMka(a, b); \
+ d = rotr64(d ^ a, 16); \
+ c = fBlaMka(c, d); \
+ b = rotr64(b ^ c, 63); \
+ } while ((void)0, 0)
+
+#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \
+ v12, v13, v14, v15) \
+ do { \
+ G(v0, v4, v8, v12); \
+ G(v1, v5, v9, v13); \
+ G(v2, v6, v10, v14); \
+ G(v3, v7, v11, v15); \
+ G(v0, v5, v10, v15); \
+ G(v1, v6, v11, v12); \
+ G(v2, v7, v8, v13); \
+ G(v3, v4, v9, v14); \
+ } while ((void)0, 0)
+
+#endif
diff --git a/src/catch.hpp b/src/catch.hpp
new file mode 100644
index 0000000..b324e56
--- /dev/null
+++ b/src/catch.hpp
@@ -0,0 +1,14075 @@
+/*
+ * Catch v2.4.2
+ * Generated: 2018-10-26 21:12:29.223927
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+// start catch.hpp
+
+
+#define CATCH_VERSION_MAJOR 2
+#define CATCH_VERSION_MINOR 4
+#define CATCH_VERSION_PATCH 2
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// start catch_suppress_warnings.h
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# pragma clang diagnostic ignored "-Wcovered-switch-default"
+# endif
+#elif defined __GNUC__
+ // GCC likes to warn on REQUIREs, and we cannot suppress them
+ // locally because g++'s support for _Pragma is lacking in older,
+ // still supported, versions
+# pragma GCC diagnostic ignored "-Wparentheses"
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+// end catch_suppress_warnings.h
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+# define CATCH_CONFIG_ALL_PARTS
+#endif
+
+// In the impl file, we want to have access to all parts of the headers
+// Can also be used to sanely support PCHs
+#if defined(CATCH_CONFIG_ALL_PARTS)
+# define CATCH_CONFIG_EXTERNAL_INTERFACES
+# if defined(CATCH_CONFIG_DISABLE_MATCHERS)
+# undef CATCH_CONFIG_DISABLE_MATCHERS
+# endif
+# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+# endif
+#endif
+
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+// start catch_platform.h
+
+#ifdef __APPLE__
+# include
+# if TARGET_OS_OSX == 1
+# define CATCH_PLATFORM_MAC
+# elif TARGET_OS_IPHONE == 1
+# define CATCH_PLATFORM_IPHONE
+# endif
+
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
+
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
+# define CATCH_PLATFORM_WINDOWS
+#endif
+
+// end catch_platform.h
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// start catch_user_interfaces.h
+
+namespace Catch {
+ unsigned int rngSeed();
+}
+
+// end catch_user_interfaces.h
+// start catch_tag_alias_autoregistrar.h
+
+// start catch_common.h
+
+// start catch_compiler_capabilities.h
+
+// Detect a number of compiler features - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_ form
+// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+#ifdef __cplusplus
+
+# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
+# define CATCH_CPP14_OR_GREATER
+# endif
+
+# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define CATCH_CPP17_OR_GREATER
+# endif
+
+#endif
+
+#if defined(CATCH_CPP17_OR_GREATER)
+# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif
+
+#ifdef __clang__
+
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Assume that non-Windows platforms support posix signals by default
+#if !defined(CATCH_PLATFORM_WINDOWS)
+ #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
+ #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#endif
+
+#ifdef __OS400__
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Android somehow still does not support std::to_string
+#if defined(__ANDROID__)
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Not all Windows environments support SEH properly
+#if defined(__MINGW32__)
+# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// PS4
+#if defined(__ORBIS__)
+# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# define _BSD_SOURCE
+// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+
+# endif
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+# endif
+
+// Universal Windows platform does not support SEH
+// Or console colours (or console at all...)
+# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+# define CATCH_CONFIG_COLOUR_NONE
+# else
+# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+# endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if we are compiled with -fno-exceptions or equivalent
+#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
+# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// DJGPP
+#ifdef __DJGPP__
+# define CATCH_INTERNAL_CONFIG_NO_WCHAR
+#endif // __DJGPP__
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use of __COUNTER__ is suppressed during code analysis in
+// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
+// handled by it.
+// Otherwise all supported compilers support COUNTER macro,
+// but user still might want to turn it off
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
+ #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if string_view is available and usable
+// The check is split apart to work around v140 (VS2015) preprocessor issue...
+#if defined(__has_include)
+#if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+#endif
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if variant is available and usable
+#if defined(__has_include)
+# if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+# if defined(__clang__) && (__clang_major__ < 8)
+ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+ // fix should be in clang 8, workaround in libstdc++ 8.2
+# include
+# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+# define CATCH_CONFIG_NO_CPP17_VARIANT
+# else
+# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+# endif // defined(__clang__) && (__clang_major__ < 8)
+# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+#endif // __has_include
+
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+# define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
+# define CATCH_CONFIG_WCHAR
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
+# define CATCH_CONFIG_CPP11_TO_STRING
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+# define CATCH_CONFIG_CPP17_STRING_VIEW
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
+# define CATCH_CONFIG_CPP17_VARIANT
+#endif
+
+#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
+# define CATCH_CONFIG_NEW_CAPTURE
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+# define CATCH_CONFIG_DISABLE_EXCEPTIONS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
+#endif
+
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#define CATCH_TRY if ((true))
+#define CATCH_CATCH_ALL if ((false))
+#define CATCH_CATCH_ANON(type) if ((false))
+#else
+#define CATCH_TRY try
+#define CATCH_CATCH_ALL catch (...)
+#define CATCH_CATCH_ANON(type) catch (type)
+#endif
+
+// end catch_compiler_capabilities.h
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#include
+#include
+#include
+
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy {};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
+namespace Catch {
+
+ struct CaseSensitive { enum Choice {
+ Yes,
+ No
+ }; };
+
+ class NonCopyable {
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+
+ protected:
+ NonCopyable();
+ virtual ~NonCopyable();
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo() = delete;
+ SourceLineInfo( char const* _file, std::size_t _line ) noexcept
+ : file( _file ),
+ line( _line )
+ {}
+
+ SourceLineInfo( SourceLineInfo const& other ) = default;
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+
+ bool empty() const noexcept;
+ bool operator == ( SourceLineInfo const& other ) const noexcept;
+ bool operator < ( SourceLineInfo const& other ) const noexcept;
+
+ char const* file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // Bring in operator<< from global namespace into Catch namespace
+ // This is necessary because the overload of operator<< above makes
+ // lookup stop at namespace Catch
+ using ::operator<<;
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() const;
+ };
+ template
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO \
+ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) )
+
+// end catch_common.h
+namespace Catch {
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+// end catch_tag_alias_autoregistrar.h
+// start catch_test_registry.h
+
+// start catch_interfaces_testcase.h
+
+#include
+#include
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestInvoker {
+ virtual void invoke () const = 0;
+ virtual ~ITestInvoker();
+ };
+
+ using ITestCasePtr = std::shared_ptr;
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector const& getAllTests() const = 0;
+ virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+// end catch_interfaces_testcase.h
+// start catch_stringref.h
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ class StringData;
+
+ /// A non-owning string class (similar to the forthcoming std::string_view)
+ /// Note that, because a StringRef may be a substring of another string,
+ /// it may not be null terminated. c_str() must return a null terminated
+ /// string, however, and so the StringRef will internally take ownership
+ /// (taking a copy), if necessary. In theory this ownership is not externally
+ /// visible - but it does mean (substring) StringRefs should not be shared between
+ /// threads.
+ class StringRef {
+ public:
+ using size_type = std::size_t;
+
+ private:
+ friend struct StringRefTestAccess;
+
+ char const* m_start;
+ size_type m_size;
+
+ char* m_data = nullptr;
+
+ void takeOwnership();
+
+ static constexpr char const* const s_empty = "";
+
+ public: // construction/ assignment
+ StringRef() noexcept
+ : StringRef( s_empty, 0 )
+ {}
+
+ StringRef( StringRef const& other ) noexcept
+ : m_start( other.m_start ),
+ m_size( other.m_size )
+ {}
+
+ StringRef( StringRef&& other ) noexcept
+ : m_start( other.m_start ),
+ m_size( other.m_size ),
+ m_data( other.m_data )
+ {
+ other.m_data = nullptr;
+ }
+
+ StringRef( char const* rawChars ) noexcept;
+
+ StringRef( char const* rawChars, size_type size ) noexcept
+ : m_start( rawChars ),
+ m_size( size )
+ {}
+
+ StringRef( std::string const& stdString ) noexcept
+ : m_start( stdString.c_str() ),
+ m_size( stdString.size() )
+ {}
+
+ ~StringRef() noexcept {
+ delete[] m_data;
+ }
+
+ auto operator = ( StringRef const &other ) noexcept -> StringRef& {
+ delete[] m_data;
+ m_data = nullptr;
+ m_start = other.m_start;
+ m_size = other.m_size;
+ return *this;
+ }
+
+ operator std::string() const;
+
+ void swap( StringRef& other ) noexcept;
+
+ public: // operators
+ auto operator == ( StringRef const& other ) const noexcept -> bool;
+ auto operator != ( StringRef const& other ) const noexcept -> bool;
+
+ auto operator[] ( size_type index ) const noexcept -> char;
+
+ public: // named queries
+ auto empty() const noexcept -> bool {
+ return m_size == 0;
+ }
+ auto size() const noexcept -> size_type {
+ return m_size;
+ }
+
+ auto numberOfCharacters() const noexcept -> size_type;
+ auto c_str() const -> char const*;
+
+ public: // substrings and searches
+ auto substr( size_type start, size_type size ) const noexcept -> StringRef;
+
+ // Returns the current start pointer.
+ // Note that the pointer can change when if the StringRef is a substring
+ auto currentData() const noexcept -> char const*;
+
+ private: // ownership queries - may not be consistent between calls
+ auto isOwned() const noexcept -> bool;
+ auto isSubstring() const noexcept -> bool;
+ };
+
+ auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
+ auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
+ auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
+
+ auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
+ auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
+
+ inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ return StringRef( rawChars, size );
+ }
+
+} // namespace Catch
+
+inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+ return Catch::StringRef( rawChars, size );
+}
+
+// end catch_stringref.h
+namespace Catch {
+
+template
+class TestInvokerAsMethod : public ITestInvoker {
+ void (C::*m_testAsMethod)();
+public:
+ TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
+
+ void invoke() const override {
+ C obj;
+ (obj.*m_testAsMethod)();
+ }
+};
+
+auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
+
+template
+auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
+ return new(std::nothrow) TestInvokerAsMethod( testAsMethod );
+}
+
+struct NameAndTags {
+ NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
+ StringRef name;
+ StringRef tags;
+};
+
+struct AutoReg : NonCopyable {
+ AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+ ~AutoReg();
+};
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
+#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
+#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
+#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+
+#if defined(CATCH_CONFIG_DISABLE)
+ #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \
+ void test(); \
+ }; \
+ } \
+ void TestName::test()
+
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+// end catch_test_registry.h
+// start catch_capture.hpp
+
+// start catch_assertionhandler.h
+
+// start catch_assertioninfo.h
+
+// start catch_result_type.h
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ bool isOk( ResultWas::OfType resultType );
+ bool isJustInfo( int flags );
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x01,
+
+ ContinueOnFailure = 0x02, // Failures fail test, but execution continues
+ FalseTest = 0x04, // Prefix expression with !
+ SuppressFail = 0x08 // Failures are reported but do not fail the test
+ }; };
+
+ ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
+
+ bool shouldContinueOnFailure( int flags );
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ bool shouldSuppressFailure( int flags );
+
+} // end namespace Catch
+
+// end catch_result_type.h
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ StringRef macroName;
+ SourceLineInfo lineInfo;
+ StringRef capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+
+ // We want to delete this constructor but a compiler bug in 4.8 means
+ // the struct is then treated as non-aggregate
+ //AssertionInfo() = delete;
+ };
+
+} // end namespace Catch
+
+// end catch_assertioninfo.h
+// start catch_decomposer.h
+
+// start catch_tostring.h
+
+#include
+#include
+#include
+#include
+// start catch_stream.h
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+ std::ostream& clog();
+
+ class StringRef;
+
+ struct IStream {
+ virtual ~IStream();
+ virtual std::ostream& stream() const = 0;
+ };
+
+ auto makeStream( StringRef const &filename ) -> IStream const*;
+
+ class ReusableStringStream {
+ std::size_t m_index;
+ std::ostream* m_oss;
+ public:
+ ReusableStringStream();
+ ~ReusableStringStream();
+
+ auto str() const -> std::string;
+
+ template
+ auto operator << ( T const& value ) -> ReusableStringStream& {
+ *m_oss << value;
+ return *this;
+ }
+ auto get() -> std::ostream& { return *m_oss; }
+ };
+}
+
+// end catch_stream.h
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+#include
+#endif
+
+#ifdef __OBJC__
+// start catch_objc_arc.hpp
+
+#import
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+// end catch_objc_arc.hpp
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
+#endif
+
+namespace Catch {
+ namespace Detail {
+
+ extern const std::string unprintableString;
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template
+ std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+ template
+ class IsStreamInsertable {
+ template
+ static auto test(int)
+ -> decltype(std::declval() << std::declval(), std::true_type());
+
+ template
+ static auto test(...)->std::false_type;
+
+ public:
+ static const bool value = decltype(test(0))::value;
+ };
+
+ template
+ std::string convertUnknownEnumToString( E e );
+
+ template
+ typename std::enable_if<
+ !std::is_enum::value && !std::is_base_of::value,
+ std::string>::type convertUnstreamable( T const& ) {
+ return Detail::unprintableString;
+ }
+ template
+ typename std::enable_if<
+ !std::is_enum::value && std::is_base_of::value,
+ std::string>::type convertUnstreamable(T const& ex) {
+ return ex.what();
+ }
+
+ template
+ typename std::enable_if<
+ std::is_enum::value
+ , std::string>::type convertUnstreamable( T const& value ) {
+ return convertUnknownEnumToString( value );
+ }
+
+#if defined(_MANAGED)
+ //! Convert a CLR string to a utf8 std::string
+ template
+ std::string clrReferenceToString( T^ ref ) {
+ if (ref == nullptr)
+ return std::string("null");
+ auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
+ cli::pin_ptr p = &bytes[0];
+ return std::string(reinterpret_cast(p), bytes->Length);
+ }
+#endif
+
+ } // namespace Detail
+
+ // If we decide for C++14, change these to enable_if_ts
+ template
+ struct StringMaker {
+ template
+ static
+ typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type
+ convert(const Fake& value) {
+ ReusableStringStream rss;
+ // NB: call using the function-like syntax to avoid ambiguity with
+ // user-defined templated operator<< under clang.
+ rss.operator<<(value);
+ return rss.str();
+ }
+
+ template
+ static
+ typename std::enable_if::value, std::string>::type
+ convert( const Fake& value ) {
+#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
+ return Detail::convertUnstreamable(value);
+#else
+ return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
+#endif
+ }
+ };
+
+ namespace Detail {
+
+ // This function dispatches all stringification requests inside of Catch.
+ // Should be preferably called fully qualified, like ::Catch::Detail::stringify
+ template
+ std::string stringify(const T& e) {
+ return ::Catch::StringMaker::type>::type>::convert(e);
+ }
+
+ template
+ std::string convertUnknownEnumToString( E e ) {
+ return ::Catch::Detail::stringify(static_cast::type>(e));
+ }
+
+#if defined(_MANAGED)
+ template
+ std::string stringify( T^ e ) {
+ return ::Catch::StringMaker::convert(e);
+ }
+#endif
+
+ } // namespace Detail
+
+ // Some predefined specializations
+
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::string& str);
+ };
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ template<>
+ struct StringMaker {
+ static std::string convert(std::string_view str);
+ };
+#endif
+
+ template<>
+ struct StringMaker {
+ static std::string convert(char const * str);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(char * str);
+ };
+
+#ifdef CATCH_CONFIG_WCHAR
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::wstring& wstr);
+ };
+
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ template<>
+ struct StringMaker {
+ static std::string convert(std::wstring_view str);
+ };
+# endif
+
+ template<>
+ struct StringMaker {
+ static std::string convert(wchar_t const * str);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(wchar_t * str);
+ };
+#endif
+
+ // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
+ // while keeping string semantics?
+ template
+ struct StringMaker {
+ static std::string convert(char const* str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+ }
+ };
+ template
+ struct StringMaker {
+ static std::string convert(signed char const* str) {
+ return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) });
+ }
+ };
+ template
+ struct StringMaker {
+ static std::string convert(unsigned char const* str) {
+ return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) });
+ }
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(int value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(long long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned int value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned long long value);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(bool b);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(char c);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(signed char c);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned char c);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(std::nullptr_t);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(float value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(double value);
+ };
+
+ template
+ struct StringMaker {
+ template
+ static std::string convert(U* p) {
+ if (p) {
+ return ::Catch::Detail::rawMemoryToString(p);
+ } else {
+ return "nullptr";
+ }
+ }
+ };
+
+ template
+ struct StringMaker {
+ static std::string convert(R C::* p) {
+ if (p) {
+ return ::Catch::Detail::rawMemoryToString(p);
+ } else {
+ return "nullptr";
+ }
+ }
+ };
+
+#if defined(_MANAGED)
+ template
+ struct StringMaker {
+ static std::string convert( T^ ref ) {
+ return ::Catch::Detail::clrReferenceToString(ref);
+ }
+ };
+#endif
+
+ namespace Detail {
+ template
+ std::string rangeToString(InputIterator first, InputIterator last) {
+ ReusableStringStream rss;
+ rss << "{ ";
+ if (first != last) {
+ rss << ::Catch::Detail::stringify(*first);
+ for (++first; first != last; ++first)
+ rss << ", " << ::Catch::Detail::stringify(*first);
+ }
+ rss << " }";
+ return rss.str();
+ }
+ }
+
+#ifdef __OBJC__
+ template<>
+ struct StringMaker {
+ static std::string convert(NSString * nsstring) {
+ if (!nsstring)
+ return "nil";
+ return std::string("@") + [nsstring UTF8String];
+ }
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(NSObject* nsObject) {
+ return ::Catch::Detail::stringify([nsObject description]);
+ }
+
+ };
+ namespace Detail {
+ inline std::string stringify( NSString* nsstring ) {
+ return StringMaker::convert( nsstring );
+ }
+
+ } // namespace Detail
+#endif // __OBJC__
+
+} // namespace Catch
+
+//////////////////////////////////////////////////////
+// Separate std-lib types stringification, so it can be selectively enabled
+// This means that we do not bring in
+
+#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
+# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#endif
+
+// Separate std::pair specialization
+#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
+#include
+namespace Catch {
+ template
+ struct StringMaker > {
+ static std::string convert(const std::pair& pair) {
+ ReusableStringStream rss;
+ rss << "{ "
+ << ::Catch::Detail::stringify(pair.first)
+ << ", "
+ << ::Catch::Detail::stringify(pair.second)
+ << " }";
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+
+// Separate std::tuple specialization
+#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
+#include
+namespace Catch {
+ namespace Detail {
+ template<
+ typename Tuple,
+ std::size_t N = 0,
+ bool = (N < std::tuple_size::value)
+ >
+ struct TupleElementPrinter {
+ static void print(const Tuple& tuple, std::ostream& os) {
+ os << (N ? ", " : " ")
+ << ::Catch::Detail::stringify(std::get(tuple));
+ TupleElementPrinter::print(tuple, os);
+ }
+ };
+
+ template<
+ typename Tuple,
+ std::size_t N
+ >
+ struct TupleElementPrinter {
+ static void print(const Tuple&, std::ostream&) {}
+ };
+
+ }
+
+ template
+ struct StringMaker> {
+ static std::string convert(const std::tuple& tuple) {
+ ReusableStringStream rss;
+ rss << '{';
+ Detail::TupleElementPrinter>::print(tuple, rss.get());
+ rss << " }";
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+
+#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
+#include
+namespace Catch {
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::monostate&) {
+ return "{ }";
+ }
+ };
+
+ template
+ struct StringMaker> {
+ static std::string convert(const std::variant& variant) {
+ if (variant.valueless_by_exception()) {
+ return "{valueless variant}";
+ } else {
+ return std::visit(
+ [](const auto& value) {
+ return ::Catch::Detail::stringify(value);
+ },
+ variant
+ );
+ }
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+
+namespace Catch {
+ struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
+
+ // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
+ using std::begin;
+ using std::end;
+
+ not_this_one begin( ... );
+ not_this_one end( ... );
+
+ template
+ struct is_range {
+ static const bool value =
+ !std::is_same())), not_this_one>::value &&
+ !std::is_same())), not_this_one>::value;
+ };
+
+#if defined(_MANAGED) // Managed types are never ranges
+ template
+ struct is_range {
+ static const bool value = false;
+ };
+#endif
+
+ template
+ std::string rangeToString( Range const& range ) {
+ return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
+ }
+
+ // Handle vector specially
+ template
+ std::string rangeToString( std::vector const& v ) {
+ ReusableStringStream rss;
+ rss << "{ ";
+ bool first = true;
+ for( bool b : v ) {
+ if( first )
+ first = false;
+ else
+ rss << ", ";
+ rss << ::Catch::Detail::stringify( b );
+ }
+ rss << " }";
+ return rss.str();
+ }
+
+ template
+ struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> {
+ static std::string convert( R const& range ) {
+ return rangeToString( range );
+ }
+ };
+
+ template
+ struct StringMaker {
+ static std::string convert(T const(&arr)[SZ]) {
+ return rangeToString(arr);
+ }
+ };
+
+} // namespace Catch
+
+// Separate std::chrono::duration specialization
+#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#include
+#include
+#include
+
+namespace Catch {
+
+template
+struct ratio_string {
+ static std::string symbol();
+};
+
+template
+std::string ratio_string::symbol() {
+ Catch::ReusableStringStream rss;
+ rss << '[' << Ratio::num << '/'
+ << Ratio::den << ']';
+ return rss.str();
+}
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+
+ ////////////
+ // std::chrono::duration specializations
+ template
+ struct StringMaker> {
+ static std::string convert(std::chrono::duration const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << ' ' << ratio_string::symbol() << 's';
+ return rss.str();
+ }
+ };
+ template
+ struct StringMaker>> {
+ static std::string convert(std::chrono::duration> const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << " s";
+ return rss.str();
+ }
+ };
+ template
+ struct StringMaker>> {
+ static std::string convert(std::chrono::duration> const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << " m";
+ return rss.str();
+ }
+ };
+ template
+ struct StringMaker>> {
+ static std::string convert(std::chrono::duration> const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << " h";
+ return rss.str();
+ }
+ };
+
+ ////////////
+ // std::chrono::time_point specialization
+ // Generic time_point cannot be specialized, only std::chrono::time_point
+ template
+ struct StringMaker> {
+ static std::string convert(std::chrono::time_point const& time_point) {
+ return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
+ }
+ };
+ // std::chrono::time_point specialization
+ template
+ struct StringMaker> {
+ static std::string convert(std::chrono::time_point const& time_point) {
+ auto converted = std::chrono::system_clock::to_time_t(time_point);
+
+#ifdef _MSC_VER
+ std::tm timeInfo = {};
+ gmtime_s(&timeInfo, &converted);
+#else
+ std::tm* timeInfo = std::gmtime(&converted);
+#endif
+
+ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+ char timeStamp[timeStampSize];
+ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+ std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+ return std::string(timeStamp);
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// end catch_tostring.h
+#include
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4018) // more "signed/unsigned mismatch"
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#pragma warning(disable:4180) // qualifier applied to function type has no meaning
+#endif
+
+namespace Catch {
+
+ struct ITransientExpression {
+ auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
+ auto getResult() const -> bool { return m_result; }
+ virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
+
+ ITransientExpression( bool isBinaryExpression, bool result )
+ : m_isBinaryExpression( isBinaryExpression ),
+ m_result( result )
+ {}
+
+ // We don't actually need a virtual destructor, but many static analysers
+ // complain if it's not here :-(
+ virtual ~ITransientExpression();
+
+ bool m_isBinaryExpression;
+ bool m_result;
+
+ };
+
+ void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
+
+ template
+ class BinaryExpr : public ITransientExpression {
+ LhsT m_lhs;
+ StringRef m_op;
+ RhsT m_rhs;
+
+ void streamReconstructedExpression( std::ostream &os ) const override {
+ formatReconstructedExpression
+ ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
+ }
+
+ public:
+ BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
+ : ITransientExpression{ true, comparisonResult },
+ m_lhs( lhs ),
+ m_op( op ),
+ m_rhs( rhs )
+ {}
+ };
+
+ template
+ class UnaryExpr : public ITransientExpression {
+ LhsT m_lhs;
+
+ void streamReconstructedExpression( std::ostream &os ) const override {
+ os << Catch::Detail::stringify( m_lhs );
+ }
+
+ public:
+ explicit UnaryExpr( LhsT lhs )
+ : ITransientExpression{ false, lhs ? true : false },
+ m_lhs( lhs )
+ {}
+ };
+
+ // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)
+ template
+ auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); }
+ template
+ auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); }
+ template
+ auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); }
+ template
+ auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; }
+ template
+ auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; }
+
+ template
+ auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); }
+ template
+ auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); }
+ template
+ auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); }
+ template
+ auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; }
+ template
+ auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; }
+
+ template
+ class ExprLhs {
+ LhsT m_lhs;
+ public:
+ explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
+
+ template
+ auto operator == ( RhsT const& rhs ) -> BinaryExpr const {
+ return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };
+ }
+ auto operator == ( bool rhs ) -> BinaryExpr const {
+ return { m_lhs == rhs, m_lhs, "==", rhs };
+ }
+
+ template
+ auto operator != ( RhsT const& rhs ) -> BinaryExpr