Update to v106r65 release.

byuu says:

This synchronizes bsnes/higan with many recent internal nall changes.

This will be the last WIP until I am situated in Japan. Apologies for the
bugfixes that didn't get applied yet, I ran out of time.
This commit is contained in:
Tim Allen 2018-10-04 20:11:23 +10:00
parent 336d20123f
commit 03b06257d3
75 changed files with 2242 additions and 1371 deletions

View File

@ -55,3 +55,5 @@ else ifneq ($(filter $(platform),linux bsd),)
rm -f $(prefix)/share/applications/$(name).desktop
rm -f $(prefix)/share/icons/$(name).png
endif
-include obj/*.d

View File

@ -148,7 +148,7 @@ auto ListWindow::loadDatabase(string location) -> void {
}
auto ListWindow::saveDatabase(string location) -> void {
file fp{location, file::mode::write};
auto fp = file::open(location, file::mode::write);
if(!fp) return MessageDialog().setParent(*this).setText({
"Error: failed to write file.\n\n",
"Name: ", location
@ -618,24 +618,12 @@ auto hiro::initialize() -> void {
}
#include <nall/main.hpp>
auto nall::main(vector<string> arguments) -> void {
auto nall::main(Arguments) -> void {
new ListWindow;
new GameWindow;
new MemoryWindow;
new OscillatorWindow;
//internal command used to synchronize all genius databases from an old format to a new format
//if enabled, use with extreme caution and make backups first
/*if(arguments.size() == 3 && arguments[1] == "--sync") {
for(auto& filename : directory::contents(arguments[2], "*.bml")) {
if(filename.beginsWith("Boards")) continue;
print(filename, "\n");
listWindow->loadDatabase({arguments[2], filename});
listWindow->saveDatabase({arguments[2], filename});
}
return print("[Done]\n");
}*/
listWindow->setVisible();
Application::run();
}

View File

@ -28,7 +28,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.64";
static const string Version = "106.65";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -67,7 +67,7 @@ auto APU::power() -> void {
phase = 0;
cycle = 0;
PRNG prng;
PRNG::PCG prng;
for(auto& n : wave.pattern) n = prng.random();
}

View File

@ -54,7 +54,7 @@ auto BSMemory::load() -> bool {
for(auto& byte : page.buffer[1]) byte = random();
for(auto& block : blocks) {
block.erased = 0;
block.erased = 1;
block.locked = 1;
}

View File

@ -23,10 +23,9 @@ auto hiro::initialize() -> void {
}
#include <nall/main.hpp>
auto nall::main(vector<string> arguments) -> void {
auto nall::main(Arguments arguments) -> void {
settings.location = locate("settings.bml");
arguments.takeLeft(); //ignore program location in argument parsing
for(auto argument : arguments) {
if(argument == "--fullscreen") {
presentation.startFullScreen = true;

View File

@ -123,7 +123,7 @@ auto Program::openRomSuperFamicom(string name, vfs::file::mode mode) -> vfs::sha
return vfs::fs::file::open({Location::notsuffix(superFamicom.location), ".msu"}, mode);
}
if(name.match("msu1/track-*.pcm")) {
if(name.match("msu1/track*.pcm")) {
name.trimLeft("msu1/track", 1L);
return vfs::fs::file::open({Location::notsuffix(superFamicom.location), name}, mode);
}

View File

@ -100,7 +100,7 @@ auto Program::saveState(string filename) -> bool {
if(gamePath().endsWith("/")) {
string location = {statePath(), filename, ".bst"};
directory::create(Location::path(location));
if(!file::write(location, saveState.data(), saveState.size())) {
if(!file::write(location, saveState)) {
return showMessage({"Unable to write [", prefix, "] to disk"}), false;
}
} else {

View File

@ -18,7 +18,7 @@ auto hiro::initialize() -> void {
}
#include <nall/main.hpp>
auto nall::main(vector<string> arguments) -> void {
auto nall::main(Arguments arguments) -> void {
new Program(arguments);
Application::run();
}

View File

@ -1,3 +1,6 @@
#include <nall/nall.hpp>
using namespace nall;
#include <ruby/ruby.hpp>
using namespace ruby;
extern unique_pointer<Video> video;

View File

@ -13,7 +13,7 @@
#include "utility.cpp"
unique_pointer<Program> program;
Program::Program(vector<string> arguments) {
Program::Program(Arguments arguments) {
program = this;
Emulator::platform = this;
@ -89,8 +89,7 @@ Program::Program(vector<string> arguments) {
updateAudioDriver();
updateAudioEffects();
arguments.takeFirst(); //ignore program location in argument parsing
for(auto& argument : arguments) {
for(auto argument : arguments) {
if(argument == "--fullscreen") {
presentation->toggleFullScreen();
} else if(directory::exists(argument.split("|", 1L).right())) {

View File

@ -1,6 +1,6 @@
struct Program : Emulator::Platform {
//program.cpp
Program(vector<string> arguments);
Program(Arguments arguments);
auto main() -> void;
auto quit() -> void;

View File

@ -11,9 +11,9 @@ auto Program::loadState(uint slot, bool managed) -> bool {
string type = managed ? "managed" : "quick";
auto location = stateName(slot, managed);
auto memory = file::read(location);
if(memory.size() == 0) return showMessage({"Slot ", slot, " ", type, " state does not exist"}), false;
if(!memory) return showMessage({"Slot ", slot, " ", type, " state does not exist"}), false;
serializer s(memory.data(), memory.size());
if(emulator->unserialize(s) == false) return showMessage({"Slot ", slot, " ", type, " state incompatible"}), false;
if(!emulator->unserialize(s)) return showMessage({"Slot ", slot, " ", type, " state incompatible"}), false;
return showMessage({"Loaded ", type, " state from slot ", slot}), true;
}
@ -22,10 +22,8 @@ auto Program::saveState(uint slot, bool managed) -> bool {
string type = managed ? "managed" : "quick";
auto location = stateName(slot, managed);
serializer s = emulator->serialize();
if(s.size() == 0) return showMessage({"Failed to save ", type, " state to slot ", slot}), false;
if(!s) return showMessage({"Failed to save ", type, " state to slot ", slot}), false;
directory::create(Location::path(location));
if(file::write(location, s.data(), s.size()) == false) {
return showMessage({"Unable to write ", type, " state to slot ", slot}), false;
}
if(!file::write(location, {s.data(), s.size()})) return showMessage({"Unable to write ", type, " state to slot ", slot}), false;
return showMessage({"Saved ", type, " state to slot ", slot}), true;
}

View File

@ -1,5 +1,5 @@
database
revision: 2018-05-17
revision: 2018-09-20
//BS Memory (JPN)

View File

@ -1,5 +1,5 @@
database
revision: 2018-05-17
revision: 2018-09-20
//Sufami Turbo (JPN)

View File

@ -1,5 +1,5 @@
database
revision: 2018-05-17
revision: 2018-09-20
//Prototypes (JPN)
@ -125,7 +125,7 @@ game
//Super Famicom (JPN)
database
revision: 2018-05-17
revision: 2018-09-20
game
sha256: 5c4e283efc338958b8dd45ebd6daf133a9eb280420a98e2e1df358ae0242c366
@ -1002,6 +1002,18 @@ game
content: Program
note: Custom wiring on PCB
game
sha256: 76f80cdf704a0e1daf1af5bbf564e427b425a5ee42329417de6f29219fe63e5f
label: ロックマンエックス
name: Rockman X
region: SHVC-RX
revision: SHVC-RX-1
board: SHVC-2A0N-11
memory
type: ROM
size: 0x180000
content: Program
game
sha256: 6dfc016c571a16e5d42045060b1a88b6f3da5831e05b33c22035e1d990deccf3
label: ロマンシング サ・ガ3 体験版サンプルROM
@ -1321,6 +1333,22 @@ game
size: 0x2000
content: Save
game
sha256: b3204162def67b0dc40097344074e9b660ed296e5b5e22e778f373f0b985645b
label: スーパー麻雀大会
name: Super Mahjong Taikai
region: SHVC-IQ
revision: SHVC-IQ-2
board: SHVC-1A3B-13
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
game
sha256: 15d1187d17fa10c77152f691197d33674e64c33a1f8ceb37e8570588be507b89
label: スーパー麻雀大会
@ -5733,7 +5761,7 @@ game
//Super Nintendo (USA)
database
revision: 2018-05-06
revision: 2018-09-20
game
sha256: 2ffe8828480f943056fb1ab5c3c84d48a0bf8cbe3ed7c9960b349b59adb07f3b
@ -7538,6 +7566,22 @@ game
oscillator
frequency: 21440000
game
sha256: fa8cacf5bbfc39ee6bbaa557adf89133d60d42f6cf9e1db30d5a36a469f74d15
label: Donkey Kong Country
name: Donkey Kong Country
region: SNS-8X-USA
revision: SNS-8X-0
board: SHVC-1J1M-11
memory
type: ROM
size: 0x400000
content: Program
memory
type: RAM
size: 0x800
content: Save
game
sha256: df2644d435330192a13768cc1f79c5aa3084a64217a5250c6dd4ffdbe2175be4
label: Donkey Kong Country
@ -10270,6 +10314,18 @@ game
size: 0x180000
content: Program
game
sha256: b8f70a6e7fb93819f79693578887e2c11e196bdf1ac6ddc7cb924b1ad0be2d32
label: Mega Man X
name: Mega Man X
region: SNS-RX-USA
revision: SNS-RX-1
board: MAXI-1A0N-30
memory
type: ROM
size: 0x180000
content: Program
game
sha256: da484f2a636b8d692840f40e5468e992c5f2af26d365c69fbc12ef2923588d23
label: Mega Man X2
@ -12980,6 +13036,23 @@ game
content: Save
volatile
game
sha256: 82e39dfbb3e4fe5c28044e80878392070c618b298dd5a267e5ea53c8f72cc548
label: Star Fox
name: Star Fox
region: SNS-FO-USA
revision: SNS-FO-2
board: SHVC-1C0N
memory
type: ROM
size: 0x100000
content: Program
memory
type: RAM
size: 0x8000
content: Save
volatile
game
sha256: 2c0bac12a7866fad1cb306da768a201c12f2520332df1ef51cba1576db21ff06
label: Star Fox: Super Weekend
@ -13690,6 +13763,46 @@ game
oscillator
frequency: 7600000
game
sha256: 76d293e5a772eb2f326e173eac62ca323873b1f329f9b935a97ba86974e1fcd5
label: Super Mario Kart
name: Super Mario Kart
region: SNS-MK-USA
revision: SNS-MK-0
board: SHVC-1K1X-10
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x800
content: Save
memory
type: ROM
size: 0x1800
content: Program
manufacturer: NEC
architecture: uPD7725
identifier: DSP1B
memory
type: ROM
size: 0x800
content: Data
manufacturer: NEC
architecture: uPD7725
identifier: DSP1B
memory
type: RAM
size: 0x200
content: Data
manufacturer: NEC
architecture: uPD7725
identifier: DSP1B
volatile
oscillator
frequency: 7600000
game
sha256: 740646f3535bfb365ca44e70d46ab433467b142bd84010393070bd0b141af853
label: Super Mario RPG: Legend of the Seven Stars
@ -13727,6 +13840,24 @@ game
size: 0x800
content: Save
game
sha256: 9b4957466798bbdb5b43a450bbb60b2591ae81d95b891430f62d53ca62e8bc7b
label: Super Mario World 2: Yoshi's Island
name: Super Mario World 2 - Yoshi's Island
region: SNS-YI-USA
revision: SNS-YI-0
board: SHVC-1CB5B-01
memory
type: ROM
size: 0x200000
content: Program
memory
type: RAM
size: 0x8000
content: Save
oscillator
frequency: 21440000
game
sha256: bd763c1a56365c244be92e6cffefd318780a2a19eda7d5baf1c6d5bd6c1b3e06
label: Super Mario World 2: Yoshi's Island

View File

@ -42,13 +42,13 @@ else ifneq ($(filter $(platform),linux bsd),)
mkdir -p $(prefix)/share/applications/
mkdir -p $(prefix)/share/icons/
mkdir -p $(prefix)/share/$(name)/
mkdir -p $(prefix)/share/$(name)/database/
mkdir -p $(prefix)/share/$(name)/firmware/
mkdir -p $(prefix)/share/$(name)/Database/
mkdir -p $(prefix)/share/$(name)/Firmware/
cp out/$(name) $(prefix)/bin/$(name)
cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop
cp data/$(name).png $(prefix)/share/icons/$(name).png
cp -R database/* $(prefix)/share/$(name)/database/
cp -R firmware/* $(prefix)/share/$(name)/firmware/
cp -R Database/* $(prefix)/share/$(name)/Database/
cp -R Firmware/* $(prefix)/share/$(name)/Firmware/
endif
uninstall:

View File

@ -1,19 +1,19 @@
Icarus::Icarus() {
Database::Famicom = BML::unserialize(string::read(locate("database/Famicom.bml")));
Database::SuperFamicom = BML::unserialize(string::read(locate("database/Super Famicom.bml")));
Database::MasterSystem = BML::unserialize(string::read(locate("database/Master System.bml")));
Database::MegaDrive = BML::unserialize(string::read(locate("database/Mega Drive.bml")));
Database::PCEngine = BML::unserialize(string::read(locate("database/PC Engine.bml")));
Database::SuperGrafx = BML::unserialize(string::read(locate("database/SuperGrafx.bml")));
Database::GameBoy = BML::unserialize(string::read(locate("database/Game Boy.bml")));
Database::GameBoyColor = BML::unserialize(string::read(locate("database/Game Boy Color.bml")));
Database::GameBoyAdvance = BML::unserialize(string::read(locate("database/Game Boy Advance.bml")));
Database::GameGear = BML::unserialize(string::read(locate("database/Game Gear.bml")));
Database::WonderSwan = BML::unserialize(string::read(locate("database/WonderSwan.bml")));
Database::WonderSwanColor = BML::unserialize(string::read(locate("database/WonderSwan Color.bml")));
Database::PocketChallengeV2 = BML::unserialize(string::read(locate("database/Pocket Challenge V2.bml")));
Database::BSMemory = BML::unserialize(string::read(locate("database/BS Memory.bml")));
Database::SufamiTurbo = BML::unserialize(string::read(locate("database/Sufami Turbo.bml")));
Database::Famicom = BML::unserialize(string::read(locate("Database/Famicom.bml")));
Database::SuperFamicom = BML::unserialize(string::read(locate("Database/Super Famicom.bml")));
Database::MasterSystem = BML::unserialize(string::read(locate("Database/Master System.bml")));
Database::MegaDrive = BML::unserialize(string::read(locate("Database/Mega Drive.bml")));
Database::PCEngine = BML::unserialize(string::read(locate("Database/PC Engine.bml")));
Database::SuperGrafx = BML::unserialize(string::read(locate("Database/SuperGrafx.bml")));
Database::GameBoy = BML::unserialize(string::read(locate("Database/Game Boy.bml")));
Database::GameBoyColor = BML::unserialize(string::read(locate("Database/Game Boy Color.bml")));
Database::GameBoyAdvance = BML::unserialize(string::read(locate("Database/Game Boy Advance.bml")));
Database::GameGear = BML::unserialize(string::read(locate("Database/Game Gear.bml")));
Database::WonderSwan = BML::unserialize(string::read(locate("Database/WonderSwan.bml")));
Database::WonderSwanColor = BML::unserialize(string::read(locate("Database/WonderSwan Color.bml")));
Database::PocketChallengeV2 = BML::unserialize(string::read(locate("Database/Pocket Challenge V2.bml")));
Database::BSMemory = BML::unserialize(string::read(locate("Database/BS Memory.bml")));
Database::SufamiTurbo = BML::unserialize(string::read(locate("Database/Sufami Turbo.bml")));
}
auto Icarus::error() const -> string {

View File

@ -12,7 +12,7 @@ struct Icarus {
}
virtual auto write(const string& filename, const uint8_t* data, uint size) -> bool {
return file::write(filename, data, size);
return file::write(filename, {data, size});
}
auto write(const string& filename, const vector<uint8_t>& buffer) -> bool {

View File

@ -46,7 +46,7 @@ auto Icarus::superFamicomImport(vector<uint8_t>& buffer, string location) -> str
auto size = rom["size"].natural();
if(size > buffer.size() - offset) {
auto firmware = string{rom["identifier"].text(), ".", rom["content"].text(), ".rom"}.trimLeft(".", 1L).downcase();
auto location = locate({"firmware/", firmware});
auto location = locate({"Firmware/", firmware});
if(location && file::size(location) == size) {
write({target, name}, file::read(location));
} else {

View File

@ -62,23 +62,23 @@ auto hiro::initialize() -> void {
}
#include <nall/main.hpp>
auto nall::main(vector<string> arguments) -> void {
if(arguments.size() == 2 && arguments[1] == "--name") {
auto nall::main(Arguments arguments) -> void {
if(arguments.size() == 1 && arguments[0] == "--name") {
return print("icarus");
}
if(arguments.size() == 3 && arguments[1] == "--manifest" && directory::exists(arguments[2])) {
return print(icarus.manifest(arguments[2]));
if(arguments.size() == 2 && arguments[0] == "--manifest" && directory::exists(arguments[1])) {
return print(icarus.manifest(arguments[1]));
}
if(arguments.size() == 3 && arguments[1] == "--import" && file::exists(arguments[2])) {
if(string target = icarus.import(arguments[2])) {
if(arguments.size() == 2 && arguments[0] == "--import" && file::exists(arguments[1])) {
if(string target = icarus.import(arguments[1])) {
return print(target, "\n");
}
return;
}
if(arguments.size() == 2 && arguments[1] == "--import") {
if(arguments.size() == 1 && arguments[0] == "--import") {
if(string source = BrowserDialog()
.setTitle("Load ROM File")
.setPath(settings["icarus/Path"].text())

View File

@ -39,10 +39,13 @@ ifeq ($(platform),)
endif
endif
compiler.c = $(compiler) -x c -std=c11
compiler.cpp = $(compiler) -x c++ -std=c++14
compiler.objc = $(compiler) -x objective-c -std=c11
compiler.objcpp = $(compiler) -x objective-c++ -std=c++14
flags.c = -x c -std=c11
flags.h = -x c-header -std=c11
flags.cpp = -x c++ -std=c++14
flags.hpp = -x c++-header -std=c++14
flags.objc = -x objective-c -std=c11
flags.objcpp = -x objective-c++ -std=c++14
flags.deps = -MMD -MP -MF $(@:.o=.d)
@ -51,8 +54,8 @@ flags.deps = -MMD -MP -MF $(@:.o=.d)
ifeq ($(compiler),)
ifeq ($(platform),windows)
compiler := g++
flags.cpp := -x c++ -std=gnu++14
flags.hpp := -x c++-header -std=gnu++14
compiler.cpp = $(compiler) -x c++ -std=gnu++14
flags.cpp = -x c++ -std=gnu++14
else ifeq ($(platform),macos)
compiler := clang++
else ifeq ($(platform),linux)
@ -158,14 +161,10 @@ nall.verbose:
compile = \
$(strip \
$(if $(filter %.c,$<), \
$(compiler) $(flags.c) $(flags.deps) $(flags) $1 -c $< -o $@ \
$(compiler.c) $(flags.deps) $(flags) $1 -c $< -o $@ \
,$(if $(filter %.cpp,$<), \
$(compiler) $(flags.cpp) $(flags.deps) $(flags) $1 -c $< -o $@ \
,$(if $(filter %.h,$<), \
$(compiler) $(flags.h) $(flags) $1 -c $< -o $@ \
,$(if $(filter %.hpp,$<), \
$(compiler) $(flags.hpp) $(flags) $1 -c $< -o $@ \
)))) \
$(compiler.cpp) $(flags.deps) $(flags) $1 -c $< -o $@ \
)) \
)
# function rwildcard(directory, pattern)

161
nall/arguments.hpp Normal file
View File

@ -0,0 +1,161 @@
#pragma once
#include <nall/location.hpp>
#include <nall/path.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
struct Arguments {
Arguments(int argc, char** argv);
Arguments(vector<string> arguments);
explicit operator bool() const { return (bool)arguments; }
auto size() const -> uint { return arguments.size(); }
auto operator[](uint index) -> string& { return arguments[index]; }
auto operator[](uint index) const -> const string& { return arguments[index]; }
auto programPath() const -> string;
auto programName() const -> string;
auto programLocation() const -> string;
auto find(string_view name) const -> bool;
auto find(string_view name, bool& argument) const -> bool;
auto find(string_view name, string& argument) const -> bool;
auto begin() const { return arguments.begin(); }
auto end() const { return arguments.end(); }
auto rbegin() const { return arguments.rbegin(); }
auto rend() const { return arguments.rend(); }
auto take() -> string;
auto take(string_view name) -> bool;
auto take(string_view name, bool& argument) -> bool;
auto take(string_view name, string& argument) -> bool;
auto begin() { return arguments.begin(); }
auto end() { return arguments.end(); }
auto rbegin() { return arguments.rbegin(); }
auto rend() { return arguments.rend(); }
private:
auto construct() -> void;
string programArgument;
vector<string> arguments;
};
inline auto Arguments::construct() -> void {
if(!arguments) return;
//extract and pre-process program argument
programArgument = arguments.takeFirst();
programArgument = {Path::real(programArgument), Location::file(programArgument)};
//normalize path and file arguments
for(auto& argument : arguments) {
if(directory::exists(argument)) argument.transform("\\", "/").trimRight("/").append("/");
else if(file::exists(argument)) argument.transform("\\", "/").trimRight("/");
}
}
inline Arguments::Arguments(int argc, char** argv) {
#if defined(PLATFORM_WINDOWS)
utf8_arguments(argc, argv);
#endif
for(uint index : range(argc)) arguments.append(argv[index]);
construct();
}
inline Arguments::Arguments(vector<string> arguments) {
this->arguments = arguments;
construct();
}
inline auto Arguments::programPath() const -> string {
return Location::path(programArgument);
}
inline auto Arguments::programName() const -> string {
return Location::file(programArgument);
}
inline auto Arguments::programLocation() const -> string {
return programArgument;
}
inline auto Arguments::find(string_view name) const -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name)) {
return true;
}
}
return false;
}
inline auto Arguments::find(string_view name, bool& argument) const -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name) && arguments.size() >= index
&& (arguments[index + 1] == "true" || arguments[index + 1] == "false")) {
argument = arguments[index + 1] == "true";
return true;
}
}
return false;
}
inline auto Arguments::find(string_view name, string& argument) const -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name) && arguments.size() >= index) {
argument = arguments[index + 1];
return true;
}
}
return false;
}
//
inline auto Arguments::take() -> string {
if(!arguments) return {};
return arguments.takeFirst();
}
inline auto Arguments::take(string_view name) -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name)) {
arguments.remove(index);
return true;
}
}
return false;
}
inline auto Arguments::take(string_view name, bool& argument) -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name) && arguments.size() >= index
&& (arguments[index + 1] == "true" || arguments[index + 1] == "false")) {
arguments.remove(index);
argument = arguments.take(index) == "true";
return true;
}
}
return false;
}
inline auto Arguments::take(string_view name, string& argument) -> bool {
for(uint index : range(arguments.size())) {
if(arguments[index].match(name) && arguments.size() >= index) {
arguments.remove(index);
argument = arguments.take(index);
return true;
}
}
return false;
}
}

View File

@ -10,7 +10,18 @@
#include <nall/arithmetic/unsigned.hpp>
#if !defined(__SIZEOF_INT128__)
namespace nall {
template<uint Bits> struct ArithmeticNatural;
template<> struct ArithmeticNatural< 8> { using type = uint8_t; };
template<> struct ArithmeticNatural< 16> { using type = uint16_t; };
template<> struct ArithmeticNatural< 32> { using type = uint32_t; };
template<> struct ArithmeticNatural< 64> { using type = uint64_t; };
#if INTMAX_BITS >= 128
template<> struct ArithmeticNatural<128> { using type = uint128_t; };
#endif
}
#if INTMAX_BITS < 128
#define PairBits 128
#define TypeBits 64
#define HalfBits 32

View File

@ -2,20 +2,27 @@
namespace nall {
struct BarrettReduction {
BarrettReduction(uint256_t modulo) : modulo(modulo), factor((1_u1024 << 512) / modulo) {}
template<uint Bits> struct BarrettReduction {
using type = typename ArithmeticNatural<1 * Bits>::type;
using pair = typename ArithmeticNatural<2 * Bits>::type;
//return = value % modulo
inline auto operator()(uint512_t value) const -> uint256_t {
uint512_t hi, lo;
nall::mul(value, factor, hi, lo);
uint512_t remainder = value - hi * modulo;
explicit BarrettReduction(type modulo) : modulo(modulo), factor(pair(1) + -pair(modulo) / modulo) {}
//return => value % modulo
inline auto operator()(pair value) const -> type {
pair hi, lo;
mul(value, factor, hi, lo);
pair remainder = value - hi * modulo;
return remainder < modulo ? remainder : remainder - modulo;
}
private:
const uint512_t modulo;
const uint512_t factor;
const pair modulo;
const pair factor;
};
template<typename T, uint Bits> auto operator%(T value, const BarrettReduction<Bits>& modulo) {
return modulo(value);
}
}

View File

@ -20,6 +20,8 @@ struct Pair {
explicit operator bool() const { return hi | lo; }
template<typename T> operator T() const { T value; _get(*this, value); return value; }
auto operator+() const -> Pair { return *this; }
auto operator-() const -> Pair { return Pair(0) - *this; }
auto operator~() const -> Pair { return {~hi, ~lo}; }
auto operator!() const -> bool { return !(hi || lo); }
@ -75,13 +77,6 @@ struct Pair {
template<typename T> auto operator> (const T& rhs) const -> bool { return Cast(*this) > Cast(rhs); }
template<typename T> auto operator< (const T& rhs) const -> bool { return Cast(*this) < Cast(rhs); }
explicit Pair(const vector<uint8_t>& value) : hi(0), lo(0) {
for(auto n : reverse(value)) {
operator<<=(8);
operator|=(n);
}
}
private:
Type lo;
Type hi;
@ -98,6 +93,10 @@ private:
template<typename T> friend auto shr(const Pair&, const T&) -> Pair;
};
template<> struct ArithmeticNatural<PairBits> {
using type = Pair;
};
#define ConcatenateUDL(Size) _u##Size
#define DeclareUDL(Size) ConcatenateUDL(Size)
@ -333,29 +332,6 @@ template<> struct stringify<Pair> {
uint _size;
};
inline auto to_vector(Pair value) -> vector<uint8_t> {
vector<uint8_t> result;
result.resize(PairBits / 8);
for(auto& byte : result) {
byte = value;
value >>= 8;
}
return result;
}
#if 0
inline auto hex(const Pair& value, long precision = 0, char padchar = '0') -> string {
string text;
if(!upper(value)) {
text.append(hex(lower(value)));
} else {
text.append(hex(upper(value)));
text.append(hex(lower(value), TypeBits / 4, '0'));
}
return pad(text, precision, padchar);
}
#endif
}
#undef ConcatenateType

View File

@ -2,25 +2,35 @@
namespace nall {
template<typename T, enable_if_t<is_unsigned<T>::value>> alwaysinline auto upper(T value) -> T {
template<typename T, enable_if_t<is_unsigned<T>::value>>
inline auto upper(T value) -> T {
return value >> sizeof(T) * 4;
}
template<typename T, enable_if_t<is_unsigned<T>::value>> alwaysinline auto lower(T value) -> T {
static const T Mask = T(0) - 1 >> sizeof(T) * 4;
template<typename T, enable_if_t<is_unsigned<T>::value>>
inline auto lower(T value) -> T {
static const T Mask = ~T(0) >> sizeof(T) * 4;
return value & Mask;
}
alwaysinline auto square(uintmax value) -> uintmax {
template<typename T, typename U, enable_if_t<is_unsigned<T>::value>, enable_if_t<is_unsigned<U>::value>>
inline auto mul(T lhs, U rhs) -> uintmax {
return lhs * rhs;
}
template<typename T, enable_if_t<is_unsigned<T>::value>>
inline auto square(T value) -> uintmax {
return value * value;
}
template<typename T, typename U> alwaysinline auto rol(const T& lhs, const U& rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
return lhs << rhs | lhs >> (sizeof(T) * 8 - rhs);
template<typename T, typename U>
inline auto rol(T lhs, U rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
return lhs << rhs | lhs >> sizeof(T) * 8 - rhs;
}
template<typename T, typename U> alwaysinline auto ror(const T& lhs, const U& rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
return lhs >> rhs | lhs << (sizeof(T) * 8 - rhs);
template<typename T, typename U>
inline auto ror(T lhs, U rhs, enable_if_t<is_unsigned<T>::value>* = 0) -> T {
return lhs >> rhs | lhs << sizeof(T) * 8 - rhs;
}
#if INTMAX_BITS >= 128

View File

@ -41,6 +41,14 @@ template<typename T> struct array_span : array_view<T> {
super::_size--;
}
auto span(uint offset, uint length) const -> type {
#ifdef DEBUG
struct out_of_bounds {};
if(offset + length >= super::_size) throw out_of_bounds{};
#endif
return {super::_data + offset, length};
}
//array_span<uint8_t> specializations
template<typename U> auto writel(U value, uint size) -> void;
template<typename U> auto writem(U value, uint size) -> void;

View File

@ -29,7 +29,7 @@ template<typename T> struct array_view {
inline operator const T*() const {
#ifdef DEBUG
struct out_of_bounds {};
if(_size <= 0) throw out_of_bounds{};
if(_size < 0) throw out_of_bounds{};
#endif
return _data;
}
@ -72,17 +72,29 @@ template<typename T> struct array_view {
return value;
}
auto view(uint offset, uint length) const -> type {
#ifdef DEBUG
struct out_of_bounds {};
if(offset + length >= _size) throw out_of_bounds{};
#endif
return {_data + offset, length};
}
//array_view<uint8_t> specializations
template<typename U> auto readl(U& value, uint size) -> U;
template<typename U> auto readm(U& value, uint size) -> U;
template<typename U> auto readvn(U& value, uint size) -> U;
template<typename U> auto readvi(U& value, uint size) -> U;
template<typename U> auto readl(U& value, uint offset, uint size) -> U { return view(offset, size).readl(value, size); }
template<typename U = uint64_t> auto readl(uint size) -> U { U value; return readl(value, size); }
template<typename U = uint64_t> auto readm(uint size) -> U { U value; return readm(value, size); }
template<typename U = uint64_t> auto readvn(uint size) -> U { U value; return readvn(value, size); }
template<typename U = int64_t> auto readvi(uint size) -> U { U value; return readvi(value, size); }
template<typename U = uint64_t> auto readl(uint offset, uint size) -> U { U value; return readl(value, offset, size); }
protected:
const T* _data;
int _size;
@ -92,13 +104,13 @@ protected:
template<> template<typename U> inline auto array_view<uint8_t>::readl(U& value, uint size) -> U {
value = 0;
for(uint byte : range(size)) value |= read() << byte * 8;
for(uint byte : range(size)) value |= (U)read() << byte * 8;
return value;
}
template<> template<typename U> inline auto array_view<uint8_t>::readm(U& value, uint size) -> U {
value = 0;
for(uint byte : reverse(range(size))) value |= read() << byte * 8;
for(uint byte : reverse(range(size))) value |= (U)read() << byte * 8;
return value;
}

View File

@ -1,272 +0,0 @@
#pragma once
#include <nall/random.hpp>
#include <nall/cipher/chacha20.hpp>
#include <nall/elliptic-curve/ed25519.hpp>
#include <nall/encode/base.hpp>
#include <nall/decode/base.hpp>
#include <nall/encode/lzsa.hpp>
#include <nall/decode/lzsa.hpp>
namespace nall { namespace Beat {
struct Archive {
struct Encryption {
string type;
uint256_t key = 0;
uint192_t nonce = 0;
};
struct Signature {
string type;
uint256_t privateKey = 0;
uint256_t publicKey = 0;
uint512_t signature = 0;
};
struct Compression {
string type;
uint size = 0;
};
//timestamps are human-readable strings in ISO 8601 format; save for T=>space
//times are stored in UTC, rather than local times
struct Timestamps {
string created;
string modified;
string accessed;
};
struct Permissions {
string name;
bool readable = false;
bool writable = false;
bool executable = false;
};
struct Node {
string name;
//paths and files
Timestamps timestamps;
struct {
Permissions owner;
Permissions group;
Permissions other;
} permissions;
//files only
uint offset = 0;
uint size = 0;
Compression compression;
string filename;
vector<uint8_t> filedata;
};
auto append(const Node& node) -> bool;
auto encryptionManifest() -> string;
auto manifest() -> string;
auto create() -> vector<uint8_t>;
//internal functions
auto encode() -> vector<uint8_t>;
auto encode(Node& node, uint64_t offset) -> vector<uint8_t>;
Encryption encryption;
Signature signature;
Compression compression; //solid archiving
vector<Node> nodes;
};
auto Archive::append(const Node& node) -> bool {
//prevent multiple nodes with the same name
if(nodes.find([&](auto& item) { return item.name == node.name; })) return false;
nodes.append(node);
return true;
}
auto Archive::encryptionManifest() -> string {
string manifest;
manifest.append("encryption\n");
manifest.append(" type: ", encryption.type, "\n");
manifest.append(" nonce: ", Encode::Base<57>(encryption.nonce), "\n");
return manifest;
}
auto Archive::manifest() -> string {
string manifest;
manifest.append("archive\n");
for(auto& node : nodes) {
if(node.name.endsWith("/")) {
manifest.append(" path: ", string{node.name}.trimRight("/", 1L), "\n");
} else {
manifest.append(" file: ", node.name, "\n");
manifest.append(" offset: ", node.offset, "\n");
manifest.append(" size: ", node.size, "\n");
if(node.compression.type) {
manifest.append(" compression: ", node.compression.type, "\n");
manifest.append(" size: ", node.compression.size, "\n");
}
}
if(node.timestamps.created || node.timestamps.modified || node.timestamps.accessed) {
manifest.append(" timestamp\n");
if(auto timestamp = node.timestamps.created ) manifest.append(" created: ", timestamp, "\n");
if(auto timestamp = node.timestamps.modified) manifest.append(" modified: ", timestamp, "\n");
if(auto timestamp = node.timestamps.accessed) manifest.append(" accessed: ", timestamp, "\n");
}
if(node.permissions.owner.name || node.permissions.group.name || node.permissions.other.name) {
manifest.append(" permission\n");
if(node.permissions.owner.name) {
manifest.append(" owner: ", node.permissions.owner.name, "\n");
if(node.permissions.owner.readable ) manifest.append(" readable\n");
if(node.permissions.owner.writable ) manifest.append(" writable\n");
if(node.permissions.owner.executable) manifest.append(" executable\n");
}
if(node.permissions.group.name) {
manifest.append(" group: ", node.permissions.group.name, "\n");
if(node.permissions.group.readable ) manifest.append(" readable\n");
if(node.permissions.group.writable ) manifest.append(" writable\n");
if(node.permissions.group.executable) manifest.append(" executable\n");
}
if(node.permissions.other.name) {
manifest.append(" other\n");
if(node.permissions.other.readable ) manifest.append(" readable\n");
if(node.permissions.other.writable ) manifest.append(" writable\n");
if(node.permissions.other.executable) manifest.append(" executable\n");
}
}
}
if(compression.type) {
manifest.append(" compression: ", compression.type, "\n");
manifest.append(" size: ", compression.size, "\n");
}
if(signature.type == "ed25519") {
manifest.append(" signature: ", signature.type, "\n");
manifest.append(" publicKey: ", Encode::Base<57>(signature.publicKey), "\n");
manifest.append(" signature: ", Encode::Base<57>(signature.signature), "\n");
}
return manifest;
}
auto Archive::create() -> vector<uint8_t> {
vector<uint8_t> output;
output.append('B');
output.append('P');
output.append('A');
output.append('1');
nodes.sort([&](auto& lhs, auto& rhs) {
return string::compare(lhs.name, rhs.name) < 0;
});
auto content = encode();
if(compression.type == "lzsa") {
content = Encode::LZSA(content);
compression.size = content.size();
}
if(signature.type == "ed25519") {
EllipticCurve::Ed25519 ed25519;
signature.publicKey = ed25519.publicKey(signature.privateKey);
signature.signature = ed25519.sign(content, signature.privateKey);
}
if(encryption.type == "xchacha20") {
//a randomly generated nonce is preferred
if(!encryption.nonce) {
CSPRNG csprng;
encryption.nonce = csprng.random<uint192_t>();
}
Cipher::XChaCha20 xchacha20{encryption.key, encryption.nonce};
content = xchacha20.encrypt(content);
string manifest;
manifest.append("encryption\n");
manifest.append(" type: ", encryption.type, "\n");
manifest.append(" nonce: ", Encode::Base<57>(encryption.nonce), "\n");
output.append(content);
for(uint8_t byte : manifest) output.append(byte);
output.appendl(manifest.size(), 8);
} else {
encryption = {};
output.append(content);
}
auto sha256 = Hash::SHA256(output).value();
output.appendl(sha256, 32);
return output;
}
//
auto Archive::encode() -> vector<uint8_t> {
vector<uint8_t> output;
for(auto& node : nodes) {
if(node.filename) {
node.timestamps.created = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::create));
node.timestamps.accessed = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::access));
node.timestamps.modified = chrono::utc::datetime(inode::timestamp(node.filename, inode::time::modify));
uint mode = inode::mode(node.filename);
node.permissions.owner.name = inode::user(node.filename);
node.permissions.owner.executable = mode & 0100;
node.permissions.owner.writable = mode & 0200;
node.permissions.owner.readable = mode & 0400;
node.permissions.group.name = inode::group(node.filename);
node.permissions.group.executable = mode & 0010;
node.permissions.group.writable = mode & 0020;
node.permissions.group.readable = mode & 0040;
node.permissions.other.name = " ";
node.permissions.other.executable = mode & 0001;
node.permissions.other.writable = mode & 0002;
node.permissions.other.readable = mode & 0004;
}
if(node.name.endsWith("/")) continue;
auto buffer = encode(node, output.size());
output.append(buffer);
}
auto manifest = this->manifest();
for(auto byte : manifest) output.append(byte);
for(auto byte : range(8)) output.append((uint64_t)manifest.size() >> byte * 8);
return output;
}
auto Archive::encode(Node& node, uint64_t offset) -> vector<uint8_t> {
node.offset = offset;
vector<uint8_t> output;
if(node.filename) {
output = file::read(node.filename);
} else {
output = node.filedata;
}
node.size = output.size();
if(node.compression.type == "lzsa") {
output = Encode::LZSA(output);
node.compression.size = output.size();
} else {
node.compression = {};
}
return output;
}
}}

View File

@ -0,0 +1,200 @@
#pragma once
#include <nall/beat/archive/node.hpp>
namespace nall { namespace Beat { namespace Archive {
struct Container {
Container(array_view<uint8_t> = {});
~Container();
auto isCompressed() const -> bool { return (bool)compression.type; }
auto isSigned() const -> bool { return (bool)signature.type; }
auto isEncrypted() const -> bool { return (bool)encryption.type; }
auto compressLZSA() -> void;
auto signEd25519(uint256_t privateKey) -> void;
auto encryptXChaCha20(uint256_t privateKey, uint192_t nonce = 0) -> void;
auto validate() -> bool;
auto decryptXChaCha20(uint256_t privateKey) -> bool;
auto verifyEd25519(uint256_t publicKey) -> bool;
auto decompressLZSA() -> bool;
auto append(string name, string location) -> shared_pointer<Node>;
auto appendPath(string name) -> shared_pointer<Node>;
auto appendFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node>;
auto remove(string name) -> bool;
auto find(string name) -> shared_pointer<Node>;
auto sort() -> void;
auto begin() { return nodes.begin(); }
auto end() { return nodes.end(); }
auto begin() const { return nodes.begin(); }
auto end() const { return nodes.end(); }
auto rbegin() { return nodes.rbegin(); }
auto rend() { return nodes.rend(); }
auto rbegin() const { return nodes.rbegin(); }
auto rend() const { return nodes.rend(); }
vector<shared_pointer<Node>> nodes;
vector<uint8_t> memory;
string metadata;
struct Compression {
string type;
} compression;
struct Signature {
string type;
uint256_t privateKey = 0;
uint256_t publicKey = 0;
uint512_t value = 0;
} signature;
struct Encryption {
string type;
uint256_t privateKey = 0;
uint192_t nonce = 0;
} encryption;
};
Container::Container(array_view<uint8_t> memory) {
this->memory.resize(memory.size());
nall::memory::copy(this->memory.data(), memory.data(), memory.size());
}
Container::~Container() {
metadata = {};
signature = {};
encryption = {};
}
//
auto Container::compressLZSA() -> void {
compression.type = "lzsa";
}
auto Container::signEd25519(uint256_t privateKey) -> void {
signature.type = "ed25519";
signature.privateKey = privateKey;
}
auto Container::encryptXChaCha20(uint256_t privateKey, uint192_t nonce) -> void {
if(!nonce) {
CSPRNG::XChaCha20 csprng;
nonce = csprng.random<uint192_t>();
}
encryption.type = "xchacha20";
encryption.privateKey = privateKey;
encryption.nonce = nonce;
}
//
auto Container::validate() -> bool {
array_view<uint8_t> memory = this->memory;
if(memory.size() < 44) return false; //8 (metadata size) + 32 (SHA256) + 4 (signature)
if(memory[memory.size() - 4] != 'B') return false;
if(memory[memory.size() - 3] != 'P') return false;
if(memory[memory.size() - 2] != 'A') return false;
if(memory[memory.size() - 1] != '1') return false;
auto sha256 = memory.readl<uint256_t>(memory.size() - 36, 32);
if(Hash::SHA256({memory.data(), memory.size() - 36}).value() != sha256) return false;
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
if(size & 1ull << 63) {
size -= 1ull << 63;
metadata = memory.view(memory.size() - 44 - size, size);
uint64_t offset = memory.size() - 44 - size;
for(auto& byte : metadata) byte ^= offset++;
} else {
metadata = memory.view(memory.size() - 44 - size, size);
}
auto document = BML::unserialize(metadata);
if(auto node = document["archive/encryption"]) {
if(node.text() == "xchacha20") {
encryption.type = node.text();
encryption.nonce = Decode::Base<57, uint192_t>(node["nonce"].text());
}
}
if(auto node = document["archive/signature"]) {
if(node.text() == "ed25519") {
signature.type = node.text();
signature.publicKey = Decode::Base<57, uint256_t>(node["publicKey"].text());
signature.value = Decode::Base<57, uint512_t>(node["value"].text());
}
}
if(auto node = document["archive/compression"]) {
compression.type = node.text();
}
return true;
}
auto Container::decryptXChaCha20(uint256_t privateKey) -> bool {
encryption.privateKey = privateKey;
Cipher::XChaCha20 xchacha20{encryption.privateKey, encryption.nonce};
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
memory = xchacha20.decrypt(memory.view(0, memory.size() - 44 - size));
return true;
}
auto Container::verifyEd25519(uint256_t publicKey) -> bool {
EllipticCurve::Ed25519 ed25519;
auto size = memory.readl<uint64_t>(memory.size() - 44, 8);
return ed25519.verify(memory.view(0, memory.size() - 44 - size), signature.value, publicKey);
}
auto Container::decompressLZSA() -> bool {
memory = Decode::LZSA(memory);
return (bool)memory;
}
//
auto Container::append(string name, string location) -> shared_pointer<Node> {
for(auto& node : nodes) if(node->name == name) return {};
if(auto node = Node::create(name, location)) return nodes.append(node), node;
return {};
}
auto Container::appendPath(string name) -> shared_pointer<Node> {
for(auto& node : nodes) if(node->name == name) return {};
if(auto node = Node::createPath(name)) return nodes.append(node), node;
return {};
}
auto Container::appendFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node> {
for(auto& node : nodes) if(node->name == name) return {};
if(auto node = Node::createFile(name, memory)) return nodes.append(node), node;
return {};
}
auto Container::remove(string name) -> bool {
if(auto offset = nodes.find([&](auto& node) { return node->name == name; })) return nodes.remove(*offset), true;
return false;
}
auto Container::find(string name) -> shared_pointer<Node> {
if(auto offset = nodes.find([&](auto& node) { return node->name == name; })) return nodes[*offset];
return {};
}
auto Container::sort() -> void {
nodes.sort([&](auto& lhs, auto& rhs) { return string::icompare(lhs->name, rhs->name) < 0; });
}
}}}

View File

@ -0,0 +1,86 @@
#pragma once
#include <nall/beat/archive/node.hpp>
#include <nall/beat/archive/container.hpp>
namespace nall { namespace Beat { namespace Archive {
auto create(Container& container, string name) -> vector<uint8_t> {
auto& metadata = container.metadata;
metadata = {};
metadata.append("archive: ", Location::file(name), "\n");
vector<uint8_t> memory;
container.sort();
for(auto& node : container.nodes) {
if(node->isFile()) {
node->offset = memory.size();
memory.append(node->memory);
}
metadata.append(node->metadata());
}
metadata.append(" size: ", memory.size(), "\n");
if(container.compression.type == "lzsa") {
memory = Encode::LZSA(memory);
metadata.append(" compression: lzsa\n");
metadata.append(" size: ", memory.size(), "\n");
}
if(container.signature.type == "ed25519") {
EllipticCurve::Ed25519 ed25519;
container.signature.publicKey = ed25519.publicKey(container.signature.privateKey);
container.signature.value = ed25519.sign(memory, container.signature.privateKey);
metadata.append(" signature: ed25519\n");
metadata.append(" publicKey: ", Encode::Base<57>(container.signature.publicKey), "\n");
metadata.append(" value: ", Encode::Base<57>(container.signature.value), "\n");
}
for(auto& byte : metadata) memory.append(byte);
memory.appendl((uint64_t)metadata.size(), 8);
auto sha256 = Hash::SHA256(memory).value();
memory.appendl((uint256_t)sha256, 32);
memory.append('B');
memory.append('P');
memory.append('A');
memory.append('1');
if(container.encryption.type == "xchacha20") {
Cipher::XChaCha20 xchacha20{container.encryption.privateKey, container.encryption.nonce};
memory = xchacha20.encrypt(memory);
metadata = {};
metadata.append("archive\n");
metadata.append(" encryption: xchacha20\n");
metadata.append(" nonce: ", Encode::Base<57>(container.encryption.nonce), "\n");
if(container.signature.type == "ed25519") {
EllipticCurve::Ed25519 ed25519;
container.signature.value = ed25519.sign(memory, container.signature.privateKey);
metadata.append(" signature: ed25519\n");
//metadata.append(" publicKey: ", Encode::Base<57>(container.signature.publicKey), "\n");
metadata.append(" value: ", Encode::Base<57>(container.signature.value), "\n");
}
for(auto& byte : metadata) memory.append(byte ^ memory.size());
memory.appendl((uint64_t)metadata.size() | 1ull << 63, 8);
auto sha256 = Hash::SHA256(memory).value();
memory.appendl((uint256_t)sha256, 32);
memory.append('B');
memory.append('P');
memory.append('A');
memory.append('1');
}
return memory;
}
}}}

View File

@ -0,0 +1,27 @@
#pragma once
#include <nall/beat/archive/node.hpp>
#include <nall/beat/archive/container.hpp>
namespace nall { namespace Beat { namespace Archive {
auto extract(Container& container) -> bool {
function<void (Markup::Node)> extract = [&](auto metadata) {
if(metadata.name() != "path" && metadata.name() != "file") return;
shared_pointer<Node> node = new Node;
if(node->unserialize(container.memory, metadata)) {
container.nodes.append(node);
}
if(metadata.name() != "path") return;
for(auto node : metadata) extract(node);
};
container.nodes.reset();
auto document = BML::unserialize(container.metadata);
for(auto node : document["archive"]) extract(node);
container.sort();
return true;
}
}}}

332
nall/beat/archive/node.hpp Normal file
View File

@ -0,0 +1,332 @@
#pragma once
#include <nall/arithmetic.hpp>
#include <nall/array-view.hpp>
#include <nall/random.hpp>
#include <nall/cipher/chacha20.hpp>
#include <nall/elliptic-curve/ed25519.hpp>
#include <nall/decode/base.hpp>
#include <nall/encode/base.hpp>
#include <nall/decode/lzsa.hpp>
#include <nall/encode/lzsa.hpp>
namespace nall { namespace Beat { namespace Archive {
struct Node {
static auto create(string name, string location) -> shared_pointer<Node>;
static auto createPath(string name) -> shared_pointer<Node>;
static auto createFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node>;
explicit operator bool() const { return (bool)name; }
auto isPath() const -> bool { return name.endsWith("/"); }
auto isFile() const -> bool { return !name.endsWith("/"); }
auto isCompressed() const -> bool { return (bool)compression.type; }
auto metadata(bool indented = true) const -> string;
auto compressLZSA() -> bool;
auto unserialize(array_view<uint8_t> container, Markup::Node metadata) -> bool;
auto decompress() -> bool;
auto getTimestamp(string) const -> uint64_t;
auto getPermissions() const -> uint;
auto getOwner() const -> string;
auto getGroup() const -> string;
//files and paths
string name;
bool timestamps = false;
struct Timestamp {
string created;
string modified;
string accessed;
} timestamp;
bool permissions = false;
struct Permission {
struct Owner {
string name;
bool readable = false;
bool writable = false;
bool executable = false;
} owner;
struct Group {
string name;
bool readable = false;
bool writable = false;
bool executable = false;
} group;
struct Other {
bool readable = false;
bool writable = false;
bool executable = false;
} other;
} permission;
//files only
vector<uint8_t> memory;
uint64_t offset = 0;
struct Compression {
string type;
uint size = 0; //decompressed size; memory.size() == compressed size
} compression;
};
auto Node::create(string name, string location) -> shared_pointer<Node> {
if(!inode::exists(location)) return {};
shared_pointer<Node> node = new Node;
node->name = name;
node->timestamps = true;
node->timestamp.created = chrono::utc::datetime(inode::timestamp(location, inode::time::create));
node->timestamp.modified = chrono::utc::datetime(inode::timestamp(location, inode::time::modify));
node->timestamp.accessed = chrono::utc::datetime(inode::timestamp(location, inode::time::access));
uint mode = inode::mode(location);
node->permissions = true;
node->permission.owner.name = inode::owner(location);
node->permission.group.name = inode::group(location);
node->permission.owner.readable = mode & 0400;
node->permission.owner.writable = mode & 0200;
node->permission.owner.executable = mode & 0100;
node->permission.group.readable = mode & 0040;
node->permission.group.writable = mode & 0020;
node->permission.group.executable = mode & 0010;
node->permission.other.readable = mode & 0004;
node->permission.other.writable = mode & 0002;
node->permission.other.executable = mode & 0001;
if(file::exists(location)) {
node->memory = file::read(location);
}
return node;
}
auto Node::createPath(string name) -> shared_pointer<Node> {
if(!name) return {};
shared_pointer<Node> node = new Node;
node->name = name;
return node;
}
auto Node::createFile(string name, array_view<uint8_t> memory) -> shared_pointer<Node> {
if(!name) return {};
shared_pointer<Node> node = new Node;
node->name = name;
node->memory.resize(memory.size());
memory::copy(node->memory.data(), memory.data(), memory.size());
return node;
}
auto Node::metadata(bool indented) const -> string {
string metadata;
if(!name) return metadata;
string indent;
if(indented) {
indent.append(" ");
auto bytes = string{name}.trimRight("/");
for(auto& byte : bytes) {
if(byte == '/') indent.append(" ");
}
}
if(isPath()) {
metadata.append(indent, "path: ", name, "\n");
}
if(isFile()) {
metadata.append(indent, "file: ", name, "\n");
}
if(timestamps) {
metadata.append(indent, " timestamp\n");
if(timestamp.created != timestamp.modified)
metadata.append(indent, " created: ", timestamp.created, "\n");
metadata.append(indent, " modified: ", timestamp.modified, "\n");
if(timestamp.accessed != timestamp.modified)
metadata.append(indent, " accessed: ", timestamp.accessed, "\n");
}
if(permissions) {
metadata.append(indent, " permission\n");
metadata.append(indent, " owner: ", permission.owner.name, "\n");
if(permission.owner.readable)
metadata.append(indent, " readable\n");
if(permission.owner.writable)
metadata.append(indent, " writable\n");
if(permission.owner.executable)
metadata.append(indent, " executable\n");
metadata.append(indent, " group: ", permission.group.name, "\n");
if(permission.group.readable)
metadata.append(indent, " readable\n");
if(permission.group.writable)
metadata.append(indent, " writable\n");
if(permission.group.executable)
metadata.append(indent, " executable\n");
metadata.append(indent, " other\n");
if(permission.other.readable)
metadata.append(indent, " readable\n");
if(permission.other.writable)
metadata.append(indent, " writable\n");
if(permission.other.executable)
metadata.append(indent, " executable\n");
}
if(isFile()) {
metadata.append(indent, " offset: ", offset, "\n");
if(!isCompressed()) {
metadata.append(indent, " size: ", memory.size(), "\n");
} else {
metadata.append(indent, " size: ", compression.size, "\n");
metadata.append(indent, " compression: ", compression.type, "\n");
metadata.append(indent, " size: ", memory.size(), "\n");
}
}
return metadata;
}
auto Node::unserialize(array_view<uint8_t> container, Markup::Node metadata) -> bool {
*this = {};
if(!metadata.text()) return false;
name = metadata.text();
if(auto node = metadata["timestamp"]) {
timestamps = true;
if(auto created = node["created" ]) timestamp.created = created.text();
if(auto modified = node["modified"]) timestamp.modified = modified.text();
if(auto accessed = node["accessed"]) timestamp.accessed = accessed.text();
}
if(auto node = metadata["permission"]) {
permissions = true;
if(auto owner = node["owner"]) {
permission.owner.name = owner.text();
permission.owner.readable = (bool)owner["readable"];
permission.owner.writable = (bool)owner["writable"];
permission.owner.executable = (bool)owner["executable"];
}
if(auto group = node["group"]) {
permission.group.name = group.text();
permission.group.readable = (bool)group["readable"];
permission.group.writable = (bool)group["writable"];
permission.group.executable = (bool)group["executable"];
}
if(auto other = node["other"]) {
permission.other.readable = (bool)other["readable"];
permission.other.writable = (bool)other["writable"];
permission.other.executable = (bool)other["executable"];
}
}
if(isPath()) return true;
uint offset = metadata["offset"].natural();
uint size = metadata["size"].natural();
if(metadata["compression"]) {
size = metadata["compression/size"].natural();
compression.type = metadata["compression"].text();
}
if(offset + size >= container.size()) return false;
memory.reallocate(size);
nall::memory::copy(memory.data(), container.view(offset, size), size);
return true;
}
auto Node::compressLZSA() -> bool {
if(!memory) return true; //don't compress empty files
if(isCompressed()) return true; //don't recompress files
auto compressedMemory = Encode::LZSA(memory);
if(compressedMemory.size() >= memory.size()) return true; //can't compress smaller than original size
compression.type = "lzsa";
compression.size = memory.size();
memory = move(compressedMemory);
return true;
}
auto Node::decompress() -> bool {
if(!isCompressed()) return true;
if(compression.type == "lzsa") {
compression = {};
memory = Decode::LZSA(memory);
return (bool)memory;
}
return false;
}
auto Node::getTimestamp(string type) const -> uint64_t {
if(!timestamps) return time(nullptr);
string value = chrono::utc::datetime();
if(type == "created" ) value = timestamp.created;
if(type == "modified") value = timestamp.modified;
if(type == "accessed") value = timestamp.accessed;
#if !defined(PLATFORM_WINDOWS)
struct tm timeInfo{};
if(strptime(value, "%Y-%m-%d %H:%M:%S", &timeInfo) != nullptr) {
//todo: not thread safe ...
auto tz = getenv("TZ");
setenv("TZ", "", 1);
timeInfo.tm_isdst = -1;
auto result = mktime(&timeInfo);
if(tz) setenv("TZ", tz, 1);
else unsetenv("TZ");
if(result != -1) return result;
}
#endif
return time(nullptr);
}
auto Node::getPermissions() const -> uint {
if(!permissions) return 0755;
uint mode = 0;
if(permission.owner.readable ) mode |= 0400;
if(permission.owner.writable ) mode |= 0200;
if(permission.owner.executable) mode |= 0100;
if(permission.group.readable ) mode |= 0040;
if(permission.group.writable ) mode |= 0020;
if(permission.group.executable) mode |= 0010;
if(permission.other.readable ) mode |= 0004;
if(permission.other.writable ) mode |= 0002;
if(permission.other.executable) mode |= 0001;
return mode;
}
auto Node::getOwner() const -> string {
if(!permissions || !permission.owner.name) {
#if !defined(PLATFORM_WINDOWS)
struct passwd* pwd = getpwuid(getuid());
assert(pwd);
return pwd->pw_name;
#endif
}
return permission.owner.name;
}
auto Node::getGroup() const -> string {
if(!permissions || !permission.group.name) {
#if !defined(PLATFORM_WINDOWS)
struct group* grp = getgrgid(getgid());
assert(grp);
return grp->gr_name;
#endif
}
return permission.group.name;
}
}}}

View File

@ -4,10 +4,8 @@
namespace nall { namespace Decode {
inline auto LZSA(const void* data) -> vector<uint8_t> {
inline auto LZSA(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> output;
auto input = (const uint8_t*)data;
uint index = 0;
uint size = 0;

View File

@ -1,6 +1,6 @@
#pragma once
#include <nall/filemap.hpp>
#include <nall/file-map.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
#include <nall/decode/inflate.hpp>
@ -24,7 +24,7 @@ struct ZIP {
auto open(const string& filename) -> bool {
close();
if(fm.open(filename, filemap::mode::read) == false) return false;
if(fm.open(filename, file::mode::read) == false) return false;
if(open(fm.data(), fm.size()) == false) {
fm.close();
return false;
@ -115,11 +115,11 @@ struct ZIP {
}
auto close() -> void {
if(fm.open()) fm.close();
if(fm) fm.close();
}
protected:
filemap fm;
file_map fm;
const uint8_t* filedata;
uint filesize;

View File

@ -1,6 +1,7 @@
#pragma once
#include <nall/file.hpp>
#include <nall/function.hpp>
#include <nall/inode.hpp>
#include <nall/intrinsics.hpp>
#include <nall/merge-sort.hpp>
@ -18,6 +19,8 @@
namespace nall {
struct directory : inode {
directory() = delete;
static auto create(const string& pathname, uint permissions = 0755) -> bool; //recursive
static auto remove(const string& pathname) -> bool; //recursive
static auto exists(const string& pathname) -> bool;
@ -64,6 +67,84 @@ struct directory : inode {
return folders;
}
static auto rcontents(const string& pathname, const string& pattern = "*") -> vector<string> {
vector<string> contents;
function<void (const string&, const string&, const string&)>
recurse = [&](const string& basename, const string& pathname, const string& pattern) {
for(auto& folder : directory::ufolders(pathname)) {
contents.append(string{pathname, folder}.trimLeft(basename, 1L));
recurse(basename, {pathname, folder}, pattern);
}
for(auto& file : directory::ufiles(pathname, pattern)) {
contents.append(string{pathname, file}.trimLeft(basename, 1L));
}
};
for(auto& folder : directory::ufolders(pathname)) {
contents.append(folder);
recurse(pathname, {pathname, folder}, pattern);
}
for(auto& file : directory::ufiles(pathname, pattern)) {
contents.append(file);
}
contents.sort();
return contents;
}
static auto ircontents(const string& pathname, const string& pattern = "*") -> vector<string> {
vector<string> contents;
function<void (const string&, const string&, const string&)>
recurse = [&](const string& basename, const string& pathname, const string& pattern) {
for(auto& folder : directory::ufolders(pathname)) {
contents.append(string{pathname, folder}.trimLeft(basename, 1L));
recurse(basename, {pathname, folder}, pattern);
}
for(auto& file : directory::ufiles(pathname, pattern)) {
contents.append(string{pathname, file}.trimLeft(basename, 1L));
}
};
for(auto& folder : directory::ufolders(pathname)) {
contents.append(folder);
recurse(pathname, {pathname, folder}, pattern);
}
for(auto& file : directory::ufiles(pathname, pattern)) {
contents.append(file);
}
contents.isort();
return contents;
}
static auto rfolders(const string& pathname, const string& pattern = "*") -> vector<string> {
vector<string> folders;
for(auto& folder : rcontents(pathname, pattern)) {
if(directory::exists({pathname, folder})) folders.append(folder);
}
return folders;
}
static auto irfolders(const string& pathname, const string& pattern = "*") -> vector<string> {
vector<string> folders;
for(auto& folder : ircontents(pathname, pattern)) {
if(directory::exists({pathname, folder})) folders.append(folder);
}
return folders;
}
static auto rfiles(const string& pathname, const string& pattern = "*") -> vector<string> {
vector<string> files;
for(auto& file : rcontents(pathname, pattern)) {
if(file::exists({pathname, file})) files.append(file);
}
return files;
}
static auto irfiles(const string& pathname, const string& pattern = "*") -> vector<string> {
vector<string> files;
for(auto& file : ircontents(pathname, pattern)) {
if(file::exists({pathname, file})) files.append(file);
}
return files;
}
private:
//internal functions; these return unsorted lists
static auto ufolders(const string& pathname, const string& pattern = "*") -> vector<string>;

View File

@ -3,50 +3,52 @@
#if defined(EC_REFERENCE)
#include <nall/elliptic-curve/modulo25519-reference.hpp>
#else
#include <nall/elliptic-curve/modulo25519.hpp>
#include <nall/elliptic-curve/modulo25519-optimized.hpp>
#endif
namespace nall { namespace EllipticCurve {
struct Curve25519 {
auto sharedKey(uint256_t secretKey, uint256_t basepoint = 9) const -> uint256_t {
secretKey &= ((0_u256 - 1) >> 2) - 7;
secretKey |= 1_u256 << 254;
basepoint &= (0_u256 - 1) >> 1;
secretKey &= (1_u256 << 254) - 8;
secretKey |= (1_u256 << 254);
basepoint &= ~0_u256 >> 1;
point p = scalarMultiply(secretKey, modP(basepoint));
return p.x * p.z.reciprocal();
point p = scalarMultiply(basepoint % P, secretKey);
field k = p.x * reciprocal(p.z);
return k();
}
private:
using field = Modulo25519;
struct point { field x, z; };
inline auto montgomeryAdd(point p, point q, field b) const -> point {
return {
(p.x * q.x - p.z * q.z).square(),
(p.x * q.z - p.z * q.x).square() * b
};
}
const BarrettReduction<256> P = BarrettReduction<256>{EllipticCurve::P};
inline auto montgomeryDouble(point p) const -> point {
field a = (p.x + p.z).square();
field b = (p.x - p.z).square();
field a = square(p.x + p.z);
field b = square(p.x - p.z);
field c = a - b;
field d = a + c * 121665;
return {a * b, c * d};
}
inline auto scalarMultiply(uint256_t e, field b) const -> point {
inline auto montgomeryAdd(point p, point q, field b) const -> point {
return {
square(p.x * q.x - p.z * q.z),
square(p.x * q.z - p.z * q.x) * b
};
}
inline auto scalarMultiply(field b, uint256_t exponent) const -> point {
point p{1, 0}, q{b, 1};
for(uint n : reverse(range(255))) {
bool bit = e >> n & 1;
cswap(bit, p.x, q.x);
cswap(bit, p.z, q.z);
for(uint bit : reverse(range(255))) {
bool condition = exponent >> bit & 1;
cswap(condition, p.x, q.x);
cswap(condition, p.z, q.z);
q = montgomeryAdd(p, q, b);
p = montgomeryDouble(p);
cswap(bit, p.x, q.x);
cswap(bit, p.z, q.z);
cswap(condition, p.x, q.x);
cswap(condition, p.z, q.z);
}
return p;
}

View File

@ -4,64 +4,42 @@
#if defined(EC_REFERENCE)
#include <nall/elliptic-curve/modulo25519-reference.hpp>
#else
#include <nall/elliptic-curve/modulo25519.hpp>
#include <nall/elliptic-curve/modulo25519-optimized.hpp>
#endif
namespace nall { namespace EllipticCurve {
static const uint256_t L = (1_u256 << 252) + 27742317777372353535851937790883648493_u256;
struct Ed25519 {
Ed25519() {
field y = field(4) * field(5).reciprocal();
field x = recoverX(y);
point B{x, y, 1, x * y};
for(uint n : range(253)) {
Bscalar[n] = B;
B = edwardsDouble(B);
}
}
auto publicKey(uint256_t privateKey) const -> uint256_t {
auto H = uint512_t{Hash::SHA512(to_vector(privateKey)).output()};
auto a = clamp(H);
auto A = compress(scalarMultiplyB(modL(a)));
return A;
return compress(scalarMultiply(B, clamp(hash(privateKey)) % L));
}
auto sign(const vector<uint8_t>& message, uint256_t privateKey) const -> uint512_t {
auto H = uint512_t{Hash::SHA512(to_vector(privateKey)).output()};
auto a = clamp(H);
auto A = compress(scalarMultiplyB(modL(a)));
auto sign(array_view<uint8_t> message, uint256_t privateKey) const -> uint512_t {
uint512_t H = hash(privateKey);
uint256_t a = clamp(H) % L;
uint256_t A = compress(scalarMultiply(B, a));
Hash::SHA512 hash1;
hash1.input(to_vector(upper(H)));
hash1.input(message);
auto r = uint512_t{hash1.output()};
auto R = compress(scalarMultiplyB(modL(r)));
uint512_t r = hash(upper(H), message) % L;
uint256_t R = compress(scalarMultiply(B, r));
Hash::SHA512 hash2;
hash2.input(to_vector(R));
hash2.input(to_vector(A));
hash2.input(message);
uint512_t k = modL(uint512_t{hash2.output()});
uint256_t S = modL(k * a + r);
uint512_t k = hash(R, A, message) % L;
uint256_t S = (k * a + r) % L;
return uint512_t(S) << 256 | R;
}
auto verify(const vector<uint8_t>& message, uint512_t signature, uint256_t publicKey) const -> bool {
auto verify(array_view<uint8_t> message, uint512_t signature, uint256_t publicKey) const -> bool {
auto R = decompress(lower(signature));
auto A = decompress(publicKey);
if(!R || !A) return false;
uint256_t S = upper(signature);
Hash::SHA512 hash;
hash.input(to_vector(lower(signature)));
hash.input(to_vector(publicKey));
hash.input(message);
auto r = uint512_t{hash.output()};
uint256_t S = upper(signature) % L;
uint512_t r = hash(lower(signature), publicKey, message) % L;
auto p = scalarMultiplyB(modL(S));
auto q = edwardsAdd(R(), scalarMultiply(modL(r), A()));
auto p = scalarMultiply(B, S);
auto q = edwardsAdd(R(), scalarMultiply(A(), r));
if(!onCurve(p) || !onCurve(q)) return false;
if(p.x * q.z - q.x * p.z) return false;
if(p.y * q.z - q.y * p.z) return false;
@ -71,31 +49,46 @@ struct Ed25519 {
private:
using field = Modulo25519;
struct point { field x, y, z, t; };
point Bscalar[253];
const field D = -field(121665) * field(121666).reciprocal();
const field D = -field(121665) * reciprocal(field(121666));
const point B = *decompress((field(4) * reciprocal(field(5)))());
const BarrettReduction<256> L = BarrettReduction<256>{EllipticCurve::L};
inline auto clamp(uint256_t p) const -> uint256_t {
p &= ((0_u256 - 1) >> 2) - 7;
p |= 1_u256 << 254;
return p;
inline auto input(Hash::SHA512&) const -> void {}
template<typename... P> inline auto input(Hash::SHA512& hash, uint256_t value, P&&... p) const -> void {
for(uint byte : range(32)) hash.input(uint8_t(value >> byte * 8));
input(hash, forward<P>(p)...);
}
inline auto recoverX(field y) const -> field {
field y2 = y.square();
field x = ((y2 - 1) * (D * y2 + 1).reciprocal()).squareRoot();
return x() & 1 ? -x : x;
template<typename... P> inline auto input(Hash::SHA512& hash, array_view<uint8_t> value, P&&... p) const -> void {
hash.input(value);
input(hash, forward<P>(p)...);
}
template<typename... P> inline auto hash(P&&... p) const -> uint512_t {
Hash::SHA512 hash;
input(hash, forward<P>(p)...);
uint512_t result;
for(auto byte : reverse(hash.output())) result = result << 8 | byte;
return result;
}
inline auto clamp(uint256_t p) const -> uint256_t {
p &= (1_u256 << 254) - 8;
p |= (1_u256 << 254);
return p;
}
inline auto onCurve(point p) const -> bool {
if(!p.z) return false;
if(p.x * p.y != p.z * p.t) return false;
if(p.y.square() - p.x.square() - p.z.square() - p.t.square() * D) return false;
if(p.x * p.y - p.z * p.t) return false;
if(square(p.y) - square(p.x) - square(p.z) - square(p.t) * D) return false;
return true;
}
inline auto decompress(uint256_t c) const -> maybe<point> {
field y = c & (1_u256 << 255) - 1;
field x = recoverX(y);
field y = c & ~0_u256 >> 1;
field x = squareRoot((square(y) - 1) * reciprocal(D * square(y) + 1));
if(c >> 255) x = -x;
point p{x, y, 1, x * y};
if(!onCurve(p)) return nothing;
@ -103,18 +96,18 @@ private:
}
inline auto compress(point p) const -> uint256_t {
field r = p.z.reciprocal();
field r = reciprocal(p.z);
field x = p.x * r;
field y = p.y * r;
return (x() & 1) << 255 | (y() & ((0_u256 - 1) >> 1));
return (x & 1) << 255 | (y & ~0_u256 >> 1);
}
inline auto edwardsDouble(point p) const -> point {
field a = p.x.square();
field b = p.y.square();
field c = p.z.square();
field a = square(p.x);
field b = square(p.y);
field c = square(p.z);
field d = -a;
field e = (p.x + p.y).square() - a - b;
field e = square(p.x + p.y) - a - b;
field g = d + b;
field f = g - (c + c);
field h = d - b;
@ -133,29 +126,16 @@ private:
return {e * f, g * h, f * g, e * h};
}
inline auto scalarMultiply(uint512_t e, point q) const -> point {
inline auto scalarMultiply(point q, uint256_t exponent) const -> point {
point p{0, 1, 1, 0}, c;
for(uint n : reverse(range(253))) {
for(uint bit : reverse(range(253))) {
p = edwardsDouble(p);
c = edwardsAdd(p, q);
bool bit = e >> n & 1;
cmove(bit, p.x, c.x);
cmove(bit, p.y, c.y);
cmove(bit, p.z, c.z);
cmove(bit, p.t, c.t);
}
return p;
}
inline auto scalarMultiplyB(uint512_t e) const -> point {
point p{0, 1, 1, 0}, c;
for(uint n : reverse(range(253))) {
bool bit = e >> n & 1;
c = edwardsAdd(p, Bscalar[n]);
cmove(bit, p.x, c.x);
cmove(bit, p.y, c.y);
cmove(bit, p.z, c.z);
cmove(bit, p.t, c.t);
bool condition = exponent >> bit & 1;
cmove(condition, p.x, c.x);
cmove(condition, p.y, c.y);
cmove(condition, p.z, c.z);
cmove(condition, p.t, c.t);
}
return p;
}

View File

@ -0,0 +1,218 @@
#pragma once
#include <nall/arithmetic/barrett.hpp>
namespace nall { namespace EllipticCurve {
static const uint256_t P = (1_u256 << 255) - 19;
#define Mask ((1ull << 51) - 1)
struct Modulo25519 {
inline Modulo25519() = default;
inline Modulo25519(const Modulo25519&) = default;
inline Modulo25519(uint64_t a, uint64_t b = 0, uint64_t c = 0, uint64_t d = 0, uint64_t e = 0) : l{a, b, c, d, e} {}
inline Modulo25519(uint256_t n);
inline explicit operator bool() const { return (bool)operator()(); }
inline auto operator[](uint index) -> uint64_t& { return l[index]; }
inline auto operator[](uint index) const -> uint64_t { return l[index]; }
inline auto operator()() const -> uint256_t;
private:
uint64_t l[5]; //51-bits per limb; 255-bits total
};
inline Modulo25519::Modulo25519(uint256_t n) {
l[0] = n >> 0 & Mask;
l[1] = n >> 51 & Mask;
l[2] = n >> 102 & Mask;
l[3] = n >> 153 & Mask;
l[4] = n >> 204 & Mask;
}
inline auto Modulo25519::operator()() const -> uint256_t {
Modulo25519 o = *this;
o[1] += (o[0] >> 51); o[0] &= Mask;
o[2] += (o[1] >> 51); o[1] &= Mask;
o[3] += (o[2] >> 51); o[2] &= Mask;
o[4] += (o[3] >> 51); o[3] &= Mask;
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
o[1] += (o[0] >> 51); o[0] &= Mask;
o[2] += (o[1] >> 51); o[1] &= Mask;
o[3] += (o[2] >> 51); o[2] &= Mask;
o[4] += (o[3] >> 51); o[3] &= Mask;
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
o[0] += 19;
o[1] += (o[0] >> 51); o[0] &= Mask;
o[2] += (o[1] >> 51); o[1] &= Mask;
o[3] += (o[2] >> 51); o[2] &= Mask;
o[4] += (o[3] >> 51); o[3] &= Mask;
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
o[0] += Mask - 18;
o[1] += Mask;
o[2] += Mask;
o[3] += Mask;
o[4] += Mask;
o[1] += o[0] >> 51; o[0] &= Mask;
o[2] += o[1] >> 51; o[1] &= Mask;
o[3] += o[2] >> 51; o[2] &= Mask;
o[4] += o[3] >> 51; o[3] &= Mask;
o[4] &= Mask;
return (uint256_t)o[0] << 0 | (uint256_t)o[1] << 51 | (uint256_t)o[2] << 102 | (uint256_t)o[3] << 153 | (uint256_t)o[4] << 204;
}
inline auto cmove(bool move, Modulo25519& l, const Modulo25519& r) -> void {
uint64_t mask = -move;
l[0] ^= mask & (l[0] ^ r[0]);
l[1] ^= mask & (l[1] ^ r[1]);
l[2] ^= mask & (l[2] ^ r[2]);
l[3] ^= mask & (l[3] ^ r[3]);
l[4] ^= mask & (l[4] ^ r[4]);
}
inline auto cswap(bool swap, Modulo25519& l, Modulo25519& r) -> void {
uint64_t mask = -swap, x;
x = mask & (l[0] ^ r[0]); l[0] ^= x; r[0] ^= x;
x = mask & (l[1] ^ r[1]); l[1] ^= x; r[1] ^= x;
x = mask & (l[2] ^ r[2]); l[2] ^= x; r[2] ^= x;
x = mask & (l[3] ^ r[3]); l[3] ^= x; r[3] ^= x;
x = mask & (l[4] ^ r[4]); l[4] ^= x; r[4] ^= x;
}
inline auto operator-(const Modulo25519& l) -> Modulo25519 { //P - l
Modulo25519 o;
uint64_t c;
o[0] = 0xfffffffffffda - l[0]; c = o[0] >> 51; o[0] &= Mask;
o[1] = 0xffffffffffffe - l[1] + c; c = o[1] >> 51; o[1] &= Mask;
o[2] = 0xffffffffffffe - l[2] + c; c = o[2] >> 51; o[2] &= Mask;
o[3] = 0xffffffffffffe - l[3] + c; c = o[3] >> 51; o[3] &= Mask;
o[4] = 0xffffffffffffe - l[4] + c; c = o[4] >> 51; o[4] &= Mask;
o[0] += c * 19;
return o;
}
inline auto operator+(const Modulo25519& l, const Modulo25519& r) -> Modulo25519 {
Modulo25519 o;
uint64_t c;
o[0] = l[0] + r[0]; c = o[0] >> 51; o[0] &= Mask;
o[1] = l[1] + r[1] + c; c = o[1] >> 51; o[1] &= Mask;
o[2] = l[2] + r[2] + c; c = o[2] >> 51; o[2] &= Mask;
o[3] = l[3] + r[3] + c; c = o[3] >> 51; o[3] &= Mask;
o[4] = l[4] + r[4] + c; c = o[4] >> 51; o[4] &= Mask;
o[0] += c * 19;
return o;
}
inline auto operator-(const Modulo25519& l, const Modulo25519& r) -> Modulo25519 {
Modulo25519 o;
uint64_t c;
o[0] = l[0] + 0x1fffffffffffb4 - r[0]; c = o[0] >> 51; o[0] &= Mask;
o[1] = l[1] + 0x1ffffffffffffc - r[1] + c; c = o[1] >> 51; o[1] &= Mask;
o[2] = l[2] + 0x1ffffffffffffc - r[2] + c; c = o[2] >> 51; o[2] &= Mask;
o[3] = l[3] + 0x1ffffffffffffc - r[3] + c; c = o[3] >> 51; o[3] &= Mask;
o[4] = l[4] + 0x1ffffffffffffc - r[4] + c; c = o[4] >> 51; o[4] &= Mask;
o[0] += c * 19;
return o;
}
inline auto operator*(const Modulo25519& l, uint64_t scalar) -> Modulo25519 {
Modulo25519 o;
uint128_t a;
a = (uint128_t)l[0] * scalar; o[0] = a & Mask;
a = (uint128_t)l[1] * scalar + (a >> 51 & Mask); o[1] = a & Mask;
a = (uint128_t)l[2] * scalar + (a >> 51 & Mask); o[2] = a & Mask;
a = (uint128_t)l[3] * scalar + (a >> 51 & Mask); o[3] = a & Mask;
a = (uint128_t)l[4] * scalar + (a >> 51 & Mask); o[4] = a & Mask;
o[0] += (a >> 51) * 19;
return o;
}
inline auto operator*(const Modulo25519& l, Modulo25519 r) -> Modulo25519 {
uint128_t t[] = {
(uint128_t)r[0] * l[0],
(uint128_t)r[0] * l[1] + (uint128_t)r[1] * l[0],
(uint128_t)r[0] * l[2] + (uint128_t)r[1] * l[1] + (uint128_t)r[2] * l[0],
(uint128_t)r[0] * l[3] + (uint128_t)r[1] * l[2] + (uint128_t)r[2] * l[1] + (uint128_t)r[3] * l[0],
(uint128_t)r[0] * l[4] + (uint128_t)r[1] * l[3] + (uint128_t)r[2] * l[2] + (uint128_t)r[3] * l[1] + (uint128_t)r[4] * l[0]
};
r[1] *= 19, r[2] *= 19, r[3] *= 19, r[4] *= 19;
t[0] += (uint128_t)r[4] * l[1] + (uint128_t)r[3] * l[2] + (uint128_t)r[2] * l[3] + (uint128_t)r[1] * l[4];
t[1] += (uint128_t)r[4] * l[2] + (uint128_t)r[3] * l[3] + (uint128_t)r[2] * l[4];
t[2] += (uint128_t)r[4] * l[3] + (uint128_t)r[3] * l[4];
t[3] += (uint128_t)r[4] * l[4];
uint64_t c; r[0] = t[0] & Mask; c = (uint64_t)(t[0] >> 51);
t[1] += c; r[1] = t[1] & Mask; c = (uint64_t)(t[1] >> 51);
t[2] += c; r[2] = t[2] & Mask; c = (uint64_t)(t[2] >> 51);
t[3] += c; r[3] = t[3] & Mask; c = (uint64_t)(t[3] >> 51);
t[4] += c; r[4] = t[4] & Mask; c = (uint64_t)(t[4] >> 51);
r[0] += c * 19; c = r[0] >> 51; r[0] &= Mask;
r[1] += c; c = r[1] >> 51; r[1] &= Mask;
r[2] += c;
return r;
}
inline auto operator&(const Modulo25519& lhs, uint256_t rhs) -> uint256_t {
return lhs() & rhs;
}
inline auto square(const Modulo25519& lhs) -> Modulo25519 {
Modulo25519 r{lhs};
Modulo25519 d{r[0] * 2, r[1] * 2, r[2] * 2 * 19, r[4] * 19, r[4] * 19 * 2};
uint128_t t[5];
t[0] = (uint128_t)r[0] * r[0] + (uint128_t)d[4] * r[1] + (uint128_t)d[2] * r[3];
t[1] = (uint128_t)d[0] * r[1] + (uint128_t)d[4] * r[2] + (uint128_t)r[3] * r[3] * 19;
t[2] = (uint128_t)d[0] * r[2] + (uint128_t)r[1] * r[1] + (uint128_t)d[4] * r[3];
t[3] = (uint128_t)d[0] * r[3] + (uint128_t)d[1] * r[2] + (uint128_t)r[4] * d[3];
t[4] = (uint128_t)d[0] * r[4] + (uint128_t)d[1] * r[3] + (uint128_t)r[2] * r[2];
uint64_t c; r[0] = t[0] & Mask; c = (uint64_t)(t[0] >> 51);
t[1] += c; r[1] = t[1] & Mask; c = (uint64_t)(t[1] >> 51);
t[2] += c; r[2] = t[2] & Mask; c = (uint64_t)(t[2] >> 51);
t[3] += c; r[3] = t[3] & Mask; c = (uint64_t)(t[3] >> 51);
t[4] += c; r[4] = t[4] & Mask; c = (uint64_t)(t[4] >> 51);
r[0] += c * 19; c = r[0] >> 51; r[0] &= Mask;
r[1] += c; c = r[1] >> 51; r[1] &= Mask;
r[2] += c;
return r;
}
inline auto exponentiate(const Modulo25519& lhs, uint256_t exponent) -> Modulo25519 {
Modulo25519 x = 1, y;
for(uint bit : reverse(range(256))) {
x = square(x);
y = x * lhs;
cmove(exponent >> bit & 1, x, y);
}
return x;
}
inline auto reciprocal(const Modulo25519& lhs) -> Modulo25519 {
return exponentiate(lhs, P - 2);
}
inline auto squareRoot(const Modulo25519& lhs) -> Modulo25519 {
static const Modulo25519 I = exponentiate(Modulo25519(2), P - 1 >> 2); //I == sqrt(-1)
Modulo25519 x = exponentiate(lhs, P + 3 >> 3);
Modulo25519 y = x * I;
cmove(bool(square(x) - lhs), x, y);
y = -x;
cmove(x & 1, x, y);
return x;
}
#undef Mask
}}

View File

@ -1,79 +1,84 @@
#pragma once
//warning: this implementation leaks side-channel information
//use modulo25519-optimized.hpp in production
#include <nall/arithmetic/barrett.hpp>
namespace nall { namespace EllipticCurve {
static const uint256_t P = (1_u256 << 255) - 19;
static const uint256_t L = (1_u256 << 252) + 27742317777372353535851937790883648493_u256;
static BarrettReduction modP{P};
static BarrettReduction modL{L};
struct Modulo25519 {
inline Modulo25519() = default;
inline Modulo25519(const Modulo25519& source) : value(source.value) {}
template<typename T> inline Modulo25519(const T& value) : value(value) {}
inline explicit operator bool() const { return (bool)value; }
inline auto operator()() const -> uint256_t { return value; }
struct Modulo25519 : uint256_t {
using type = Modulo25519;
using uint256_t::uint256_t;
alwaysinline auto operator()() const -> uint256_t {
return *this;
}
alwaysinline auto operator-() const -> type {
return P.operator-(*this);
}
template<typename T> alwaysinline auto operator+(const T& rhs) const -> type {
auto lhs = (uint512_t)*this + rhs;
if(lhs >= P) lhs -= P;
return lhs;
}
template<typename T> alwaysinline auto operator-(const T& rhs) const -> type {
auto lhs = (uint512_t)*this;
if(lhs < rhs) lhs += P;
return lhs - rhs;
}
template<typename T> alwaysinline auto operator*(const T& rhs) const -> type {
uint256_t hi, lo;
nall::mul(*this, rhs, hi, lo);
return modP(uint512_t{hi, lo});
}
alwaysinline auto square() const -> type {
uint256_t hi, lo;
nall::square(*this, hi, lo);
return modP(uint512_t{hi, lo});
}
inline auto expmod(uint256_t e) const -> type {
type x = 1;
for(auto n : rrange(256)) {
x = x.square();
if(e >> n & 1) x = operator*(x);
}
return x;
}
inline auto reciprocal() const -> type {
return expmod(P - 2);
}
inline auto squareRoot() const -> type {
static const type i = type(2).expmod((P - 1) >> 2); //i = sqrt(-1)
type x = expmod((P + 3) >> 3);
if(operator!=(x.square())) x = x * i;
if(x & 1) x = -x;
return x;
}
private:
uint256_t value;
};
inline auto cmove(bool bit, Modulo25519& lhs, const Modulo25519& rhs) -> void {
if(bit) lhs = rhs;
inline auto operator-(const Modulo25519& lhs) -> Modulo25519 {
return P - lhs();
}
inline auto cswap(bool bit, Modulo25519& lhs, Modulo25519& rhs) -> void {
if(bit) swap(lhs, rhs);
inline auto operator+(const Modulo25519& lhs, const Modulo25519& rhs) -> Modulo25519 {
uint512_t value = (uint512_t)lhs() + rhs();
if(value >= P) value -= P;
return value;
}
inline auto operator-(const Modulo25519& lhs, const Modulo25519& rhs) -> Modulo25519 {
uint512_t value = (uint512_t)lhs();
if(value < rhs()) value += P;
return uint256_t(value - rhs());
}
inline auto operator*(const Modulo25519& lhs, const Modulo25519& rhs) -> Modulo25519 {
static const BarrettReduction<256> P{EllipticCurve::P};
uint256_t hi, lo;
mul(lhs(), rhs(), hi, lo);
return uint512_t{hi, lo} % P;
}
inline auto operator&(const Modulo25519& lhs, uint256_t rhs) -> uint256_t {
return lhs() & rhs;
}
inline auto square(const Modulo25519& lhs) -> Modulo25519 {
static const BarrettReduction<256> P{EllipticCurve::P};
uint256_t hi, lo;
square(lhs(), hi, lo);
return uint512_t{hi, lo} % P;
}
inline auto exponentiate(const Modulo25519& lhs, uint256_t exponent) -> Modulo25519 {
if(exponent == 0) return 1;
Modulo25519 value = square(exponentiate(lhs, exponent >> 1));
if(exponent & 1) value = value * lhs;
return value;
}
inline auto reciprocal(const Modulo25519& lhs) -> Modulo25519 {
return exponentiate(lhs, P - 2);
}
inline auto squareRoot(const Modulo25519& lhs) -> Modulo25519 {
static const Modulo25519 I = exponentiate(Modulo25519(2), P - 1 >> 2); //I = sqrt(-1)
Modulo25519 value = exponentiate(lhs, P + 3 >> 3);
if(square(value) - lhs) value = value * I;
if(value & 1) value = -value;
return value;
}
inline auto cmove(bool condition, Modulo25519& lhs, const Modulo25519& rhs) -> void {
if(condition) lhs = rhs;
}
inline auto cswap(bool condition, Modulo25519& lhs, Modulo25519& rhs) -> void {
if(condition) swap(lhs, rhs);
}
}}

View File

@ -1,234 +0,0 @@
#pragma once
#include <nall/arithmetic/barrett.hpp>
namespace nall { namespace EllipticCurve {
static const uint256_t P = (1_u256 << 255) - 19;
static const uint256_t L = (1_u256 << 252) + 27742317777372353535851937790883648493_u256;
static BarrettReduction modP{P};
static BarrettReduction modL{L};
struct Modulo25519;
auto cmove(bool move, Modulo25519& l, const Modulo25519& r) -> void;
auto cswap(bool swap, Modulo25519& l, Modulo25519& r) -> void;
struct Modulo25519 {
using type = Modulo25519;
#define Mask ((1ull << 51) - 1)
inline Modulo25519() = default;
inline Modulo25519(const Modulo25519&) = default;
inline Modulo25519(uint64_t a, uint64_t b = 0, uint64_t c = 0, uint64_t d = 0, uint64_t e = 0) : l{a, b, c, d, e} {}
inline Modulo25519(uint256_t n) {
l[0] = n >> 0 & Mask;
l[1] = n >> 51 & Mask;
l[2] = n >> 102 & Mask;
l[3] = n >> 153 & Mask;
l[4] = n >> 204 & Mask;
}
inline auto operator()() const -> uint256_t { return operator uint256_t(); }
inline auto& operator[](uint index) { return l[index]; }
inline auto operator[](uint index) const { return l[index]; }
inline explicit operator bool() const {
return operator uint256_t();
}
inline operator uint256_t() const {
type o = *this;
o[1] += (o[0] >> 51); o[0] &= Mask;
o[2] += (o[1] >> 51); o[1] &= Mask;
o[3] += (o[2] >> 51); o[2] &= Mask;
o[4] += (o[3] >> 51); o[3] &= Mask;
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
o[1] += (o[0] >> 51); o[0] &= Mask;
o[2] += (o[1] >> 51); o[1] &= Mask;
o[3] += (o[2] >> 51); o[2] &= Mask;
o[4] += (o[3] >> 51); o[3] &= Mask;
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
o[0] += 19;
o[1] += (o[0] >> 51); o[0] &= Mask;
o[2] += (o[1] >> 51); o[1] &= Mask;
o[3] += (o[2] >> 51); o[2] &= Mask;
o[4] += (o[3] >> 51); o[3] &= Mask;
o[0] += 19 * (o[4] >> 51); o[4] &= Mask;
o[0] += Mask - 18;
o[1] += Mask;
o[2] += Mask;
o[3] += Mask;
o[4] += Mask;
o[1] += o[0] >> 51; o[0] &= Mask;
o[2] += o[1] >> 51; o[1] &= Mask;
o[3] += o[2] >> 51; o[2] &= Mask;
o[4] += o[3] >> 51; o[3] &= Mask;
o[4] &= Mask;
return (uint256_t)o[0] << 0 | (uint256_t)o[1] << 51 | (uint256_t)o[2] << 102 | (uint256_t)o[3] << 153 | (uint256_t)o[4] << 204;
}
inline auto operator!=(type r) const -> bool {
bool e = 1;
e &= l[0] == r[0];
e &= l[1] == r[1];
e &= l[2] == r[2];
e &= l[3] == r[3];
e &= l[4] == r[4];
return e == 0;
}
inline auto operator-() const -> type { //P - l
type o;
uint64_t c;
o[0] = 0xfffffffffffda - l[0]; c = o[0] >> 51; o[0] &= Mask;
o[1] = 0xffffffffffffe - l[1] + c; c = o[1] >> 51; o[1] &= Mask;
o[2] = 0xffffffffffffe - l[2] + c; c = o[2] >> 51; o[2] &= Mask;
o[3] = 0xffffffffffffe - l[3] + c; c = o[3] >> 51; o[3] &= Mask;
o[4] = 0xffffffffffffe - l[4] + c; c = o[4] >> 51; o[4] &= Mask;
o[0] += c * 19;
return o;
}
inline auto operator+(type r) const -> type {
type o;
uint64_t c;
o[0] = l[0] + r[0]; c = o[0] >> 51; o[0] &= Mask;
o[1] = l[1] + r[1] + c; c = o[1] >> 51; o[1] &= Mask;
o[2] = l[2] + r[2] + c; c = o[2] >> 51; o[2] &= Mask;
o[3] = l[3] + r[3] + c; c = o[3] >> 51; o[3] &= Mask;
o[4] = l[4] + r[4] + c; c = o[4] >> 51; o[4] &= Mask;
o[0] += c * 19;
return o;
}
inline auto operator-(type r) const -> type {
type o;
uint64_t c;
o[0] = l[0] + 0x1fffffffffffb4 - r[0]; c = o[0] >> 51; o[0] &= Mask;
o[1] = l[1] + 0x1ffffffffffffc - r[1] + c; c = o[1] >> 51; o[1] &= Mask;
o[2] = l[2] + 0x1ffffffffffffc - r[2] + c; c = o[2] >> 51; o[2] &= Mask;
o[3] = l[3] + 0x1ffffffffffffc - r[3] + c; c = o[3] >> 51; o[3] &= Mask;
o[4] = l[4] + 0x1ffffffffffffc - r[4] + c; c = o[4] >> 51; o[4] &= Mask;
o[0] += c * 19;
return o;
}
inline auto operator*(uint64_t scalar) const -> type {
type o;
uint128_t a;
a = (uint128_t)l[0] * scalar; o[0] = a & Mask;
a = (uint128_t)l[1] * scalar + (a >> 51 & Mask); o[1] = a & Mask;
a = (uint128_t)l[2] * scalar + (a >> 51 & Mask); o[2] = a & Mask;
a = (uint128_t)l[3] * scalar + (a >> 51 & Mask); o[3] = a & Mask;
a = (uint128_t)l[4] * scalar + (a >> 51 & Mask); o[4] = a & Mask;
o[0] += (a >> 51) * 19;
return o;
}
inline auto operator*(type r) const -> type {
uint128_t t[] = {
(uint128_t)r[0] * l[0],
(uint128_t)r[0] * l[1] + (uint128_t)r[1] * l[0],
(uint128_t)r[0] * l[2] + (uint128_t)r[1] * l[1] + (uint128_t)r[2] * l[0],
(uint128_t)r[0] * l[3] + (uint128_t)r[1] * l[2] + (uint128_t)r[2] * l[1] + (uint128_t)r[3] * l[0],
(uint128_t)r[0] * l[4] + (uint128_t)r[1] * l[3] + (uint128_t)r[2] * l[2] + (uint128_t)r[3] * l[1] + (uint128_t)r[4] * l[0]
};
r[1] *= 19, r[2] *= 19, r[3] *= 19, r[4] *= 19;
t[0] += (uint128_t)r[4] * l[1] + (uint128_t)r[3] * l[2] + (uint128_t)r[2] * l[3] + (uint128_t)r[1] * l[4];
t[1] += (uint128_t)r[4] * l[2] + (uint128_t)r[3] * l[3] + (uint128_t)r[2] * l[4];
t[2] += (uint128_t)r[4] * l[3] + (uint128_t)r[3] * l[4];
t[3] += (uint128_t)r[4] * l[4];
uint64_t c; r[0] = t[0] & Mask; c = (uint64_t)(t[0] >> 51);
t[1] += c; r[1] = t[1] & Mask; c = (uint64_t)(t[1] >> 51);
t[2] += c; r[2] = t[2] & Mask; c = (uint64_t)(t[2] >> 51);
t[3] += c; r[3] = t[3] & Mask; c = (uint64_t)(t[3] >> 51);
t[4] += c; r[4] = t[4] & Mask; c = (uint64_t)(t[4] >> 51);
r[0] += c * 19; c = r[0] >> 51; r[0] &= Mask;
r[1] += c; c = r[1] >> 51; r[1] &= Mask;
r[2] += c;
return r;
}
inline auto square() const -> type {
type r{*this};
type d{r[0] * 2, r[1] * 2, r[2] * 2 * 19, r[4] * 19, r[4] * 19 * 2};
uint128_t t[5];
t[0] = (uint128_t)r[0] * r[0] + (uint128_t)d[4] * r[1] + (uint128_t)d[2] * r[3];
t[1] = (uint128_t)d[0] * r[1] + (uint128_t)d[4] * r[2] + (uint128_t)r[3] * r[3] * 19;
t[2] = (uint128_t)d[0] * r[2] + (uint128_t)r[1] * r[1] + (uint128_t)d[4] * r[3];
t[3] = (uint128_t)d[0] * r[3] + (uint128_t)d[1] * r[2] + (uint128_t)r[4] * d[3];
t[4] = (uint128_t)d[0] * r[4] + (uint128_t)d[1] * r[3] + (uint128_t)r[2] * r[2];
uint64_t c; r[0] = t[0] & Mask; c = (uint64_t)(t[0] >> 51);
t[1] += c; r[1] = t[1] & Mask; c = (uint64_t)(t[1] >> 51);
t[2] += c; r[2] = t[2] & Mask; c = (uint64_t)(t[2] >> 51);
t[3] += c; r[3] = t[3] & Mask; c = (uint64_t)(t[3] >> 51);
t[4] += c; r[4] = t[4] & Mask; c = (uint64_t)(t[4] >> 51);
r[0] += c * 19; c = r[0] >> 51; r[0] &= Mask;
r[1] += c; c = r[1] >> 51; r[1] &= Mask;
r[2] += c;
return r;
}
inline auto expmod(uint256_t e) const -> type {
type x = 1, y;
for(uint n : reverse(range(256))) {
x = x.square();
y = operator*(x);
cmove(e >> n & 1, x, y);
}
return x;
}
inline auto reciprocal() const -> type {
return expmod(P - 2);
}
inline auto squareRoot() const -> type {
static const type i = type(2).expmod((P - 1) >> 2); //i == sqrt(-1)
type x = expmod((P + 3) >> 3);
type y = x * i;
cmove(operator!=(x.square()), x, y);
y = -x;
cmove(x() & 1, x, y);
return x;
}
private:
uint64_t l[5]; //51-bits per limb; 255-bits total
#undef Mask
};
inline auto cmove(bool move, Modulo25519& l, const Modulo25519& r) -> void {
uint64_t mask = -move;
l[0] ^= mask & (l[0] ^ r[0]);
l[1] ^= mask & (l[1] ^ r[1]);
l[2] ^= mask & (l[2] ^ r[2]);
l[3] ^= mask & (l[3] ^ r[3]);
l[4] ^= mask & (l[4] ^ r[4]);
}
inline auto cswap(bool swap, Modulo25519& l, Modulo25519& r) -> void {
uint64_t mask = -swap, x;
x = mask & (l[0] ^ r[0]); l[0] ^= x; r[0] ^= x;
x = mask & (l[1] ^ r[1]); l[1] ^= x; r[1] ^= x;
x = mask & (l[2] ^ r[2]); l[2] ^= x; r[2] ^= x;
x = mask & (l[3] ^ r[3]); l[3] ^= x; r[3] ^= x;
x = mask & (l[4] ^ r[4]); l[4] ^= x; r[4] ^= x;
}
}}

View File

@ -17,7 +17,7 @@ using uint32 = Natural<32>;
using uint64 = Natural<64>;
struct FX {
auto open(vector<string> arguments) -> bool;
auto open(Arguments& arguments) -> bool;
auto close() -> void;
auto readable() -> bool;
auto read() -> uint8_t;
@ -35,16 +35,10 @@ struct FX {
serial device;
};
auto FX::open(vector<string> arguments) -> bool {
auto FX::open(Arguments& arguments) -> bool {
//device name override support
string name;
for(auto argument : arguments) {
if(argument.beginsWith("--device=")) {
name = argument.trimLeft("--device=", 1L);
break;
}
}
arguments.take("--device", name);
if(!device.open(name)) {
print("[21fx] error: unable to open hardware device\n");
return false;

View File

@ -4,7 +4,7 @@ namespace nall { namespace Encode {
struct BMP {
static auto create(const string& filename, const void* data, uint pitch, uint width, uint height, bool alpha) -> bool {
file fp{filename, file::mode::write};
auto fp = file::open(filename, file::mode::write);
if(!fp) return false;
uint bitsPerPixel = alpha ? 32 : 24;

View File

@ -49,14 +49,14 @@ inline auto LZSA(array_view<uint8_t> input) -> vector<uint8_t> {
int length, offset;
suffixArray.previous(length, offset, index);
for(uint ahead = 1; ahead <= 2; ahead++) {
/* for(uint ahead = 1; ahead <= 2; ahead++) {
int aheadLength, aheadOffset;
suffixArray.previous(aheadLength, aheadOffset, index + ahead);
if(aheadLength > length && aheadOffset >= 0) {
length = 0;
break;
}
}
} */
if(length < 6 || offset < 0) {
flagWrite(0);

View File

@ -19,7 +19,7 @@ struct ZIP {
filename.transform("\\", "/");
if(!timestamp) timestamp = this->timestamp;
uint32_t checksum = Hash::CRC32({data, size}).digest().hex();
directory.append({filename, timestamp, checksum, size, fp.offset()});
directory.append({filename, timestamp, checksum, size, (uint32_t)fp.offset()});
fp.writel(0x04034b50, 4); //signature
fp.writel(0x0014, 2); //minimum version (2.0)
@ -34,7 +34,7 @@ struct ZIP {
fp.writel(0x0000, 2); //extra field length
fp.print(filename); //file name
fp.write(data, size); //file data
fp.write({data, size}); //file data
}
~ZIP() {
@ -86,7 +86,7 @@ protected:
return ((info->tm_year - 80) << 9) | ((1 + info->tm_mon) << 5) + (info->tm_mday);
}
file fp;
file_buffer fp;
time_t timestamp;
struct entry_t {
string filename;

249
nall/file-buffer.hpp Normal file
View File

@ -0,0 +1,249 @@
#pragma once
#include <nall/platform.hpp>
#include <nall/array-span.hpp>
#include <nall/array-view.hpp>
#include <nall/inode.hpp>
#include <nall/range.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/hash/sha256.hpp>
namespace nall {
//on Windows (at least for 7 and earlier), FILE* is not buffered
//thus, reading/writing one byte at a time will be dramatically slower
//on all other OSes, FILE* is buffered
//in order to ensure good performance, file_buffer implements its own buffer
//this speeds up Windows substantially, without harming performance elsewhere much
struct file_buffer {
struct mode { enum : uint { read, write, modify, append }; };
struct index { enum : uint { absolute, relative }; };
file_buffer(const file_buffer&) = delete;
auto operator=(const file_buffer&) -> file_buffer& = delete;
file_buffer() = default;
file_buffer(const string& filename, uint mode) { open(filename, mode); }
file_buffer(file_buffer&& source) { operator=(move(source)); }
~file_buffer() { close(); }
auto operator=(file_buffer&& source) -> file_buffer& {
buffer = source.buffer;
bufferOffset = source.bufferOffset;
bufferDirty = source.bufferDirty;
fileHandle = source.fileHandle;
fileOffset = source.fileOffset;
fileSize = source.fileSize;
fileMode = source.fileMode;
source.bufferOffset = -1;
source.bufferDirty = false;
source.fileHandle = nullptr;
source.fileOffset = 0;
source.fileSize = 0;
source.fileMode = mode::read;
return *this;
}
explicit operator bool() const {
return (bool)fileHandle;
}
auto read() -> uint8_t {
if(!fileHandle) return 0; //file not open
if(fileMode == mode::write) return 0; //reads not permitted
if(fileOffset >= fileSize) return 0; //cannot read past end of file
bufferSynchronize();
return buffer[fileOffset++ & buffer.size() - 1];
}
template<typename T = uint64_t> auto readl(uint length = 1) -> T {
T data = 0;
for(uint n : range(length)) {
data |= (T)read() << n * 8;
}
return data;
}
template<typename T = uint64_t> auto readm(uint length = 1) -> T {
T data = 0;
while(length--) {
data <<= 8;
data |= read();
}
return data;
}
auto reads(uint length) -> string {
string result;
result.resize(length);
for(auto& byte : result) byte = read();
return result;
}
auto read(array_span<uint8_t> memory) -> void {
for(auto& byte : memory) byte = read();
}
auto write(uint8_t data) -> void {
if(!fileHandle) return; //file not open
if(fileMode == mode::read) return; //writes not permitted
bufferSynchronize();
buffer[fileOffset++ & buffer.size() - 1] = data;
bufferDirty = true;
if(fileOffset > fileSize) fileSize = fileOffset;
}
template<typename T = uint64_t> auto writel(T data, uint length = 1) -> void {
while(length--) {
write(uint8_t(data));
data >>= 8;
}
}
template<typename T = uint64_t> auto writem(T data, uint length = 1) -> void {
for(uint n : reverse(range(length))) {
write(uint8_t(data >> n * 8));
}
}
auto writes(const string& s) -> void {
for(auto& byte : s) write(byte);
}
auto write(array_view<uint8_t> memory) -> void {
for(auto& byte : memory) write(byte);
}
template<typename... P> auto print(P&&... p) -> void {
string s{forward<P>(p)...};
for(auto& byte : s) write(byte);
}
auto flush() -> void {
bufferFlush();
fflush(fileHandle);
}
auto seek(int64_t offset, uint index_ = index::absolute) -> void {
if(!fileHandle) return;
bufferFlush();
int64_t seekOffset = fileOffset;
switch(index_) {
case index::absolute: seekOffset = offset; break;
case index::relative: seekOffset += offset; break;
}
if(seekOffset < 0) seekOffset = 0; //cannot seek before start of file
if(seekOffset > fileSize) {
if(fileMode == mode::read) { //cannot seek past end of file
seekOffset = fileSize;
} else { //pad file to requested location
fileOffset = fileSize;
while(fileSize < seekOffset) write(0);
}
}
fileOffset = seekOffset;
}
auto offset() const -> uint64_t {
if(!fileHandle) return 0;
return fileOffset;
}
auto size() const -> uint64_t {
if(!fileHandle) return 0;
return fileSize;
}
auto truncate(uint64_t size) -> bool {
if(!fileHandle) return false;
#if defined(API_POSIX)
return ftruncate(fileno(fileHandle), size) == 0;
#elif defined(API_WINDOWS)
return _chsize(fileno(fileHandle), size) == 0;
#endif
}
auto end() const -> bool {
if(!fileHandle) return true;
return fileOffset >= fileSize;
}
auto open(const string& filename, uint mode_) -> bool {
close();
switch(fileMode = mode_) {
#if defined(API_POSIX)
case mode::read: fileHandle = fopen(filename, "rb" ); break;
case mode::write: fileHandle = fopen(filename, "wb+"); break; //need read permission for buffering
case mode::modify: fileHandle = fopen(filename, "rb+"); break;
case mode::append: fileHandle = fopen(filename, "wb+"); break;
#elif defined(API_WINDOWS)
case mode::read: fileHandle = _wfopen(utf16_t(filename), L"rb" ); break;
case mode::write: fileHandle = _wfopen(utf16_t(filename), L"wb+"); break;
case mode::modify: fileHandle = _wfopen(utf16_t(filename), L"rb+"); break;
case mode::append: fileHandle = _wfopen(utf16_t(filename), L"wb+"); break;
#endif
}
if(!fileHandle) return false;
bufferOffset = -1;
fileOffset = 0;
fseek(fileHandle, 0, SEEK_END);
fileSize = ftell(fileHandle);
fseek(fileHandle, 0, SEEK_SET);
return true;
}
auto close() -> void {
if(!fileHandle) return;
bufferFlush();
fclose(fileHandle);
fileHandle = nullptr;
}
private:
array<uint8_t[4096]> buffer;
int bufferOffset = -1;
bool bufferDirty = false;
FILE* fileHandle = nullptr;
uint64_t fileOffset = 0;
uint64_t fileSize = 0;
uint fileMode = mode::read;
auto bufferSynchronize() -> void {
if(!fileHandle) return;
if(bufferOffset == (fileOffset & ~(buffer.size() - 1))) return;
bufferFlush();
bufferOffset = fileOffset & ~(buffer.size() - 1);
fseek(fileHandle, bufferOffset, SEEK_SET);
uint64_t length = bufferOffset + buffer.size() <= fileSize ? buffer.size() : fileSize & buffer.size() - 1;
if(length) (void)fread(buffer.data(), 1, length, fileHandle);
}
auto bufferFlush() -> void {
if(!fileHandle) return; //file not open
if(fileMode == mode::read) return; //buffer cannot be written to
if(bufferOffset < 0) return; //buffer unused
if(!bufferDirty) return; //buffer unmodified since read
fseek(fileHandle, bufferOffset, SEEK_SET);
uint64_t length = bufferOffset + buffer.size() <= fileSize ? buffer.size() : fileSize & buffer.size() - 1;
if(length) (void)fwrite(buffer.data(), 1, length, fileHandle);
bufferOffset = -1;
bufferDirty = false;
}
};
}

215
nall/file-map.hpp Normal file
View File

@ -0,0 +1,215 @@
#pragma once
#include <nall/file.hpp>
#include <nall/stdint.hpp>
#include <stdio.h>
#include <stdlib.h>
#if defined(PLATFORM_WINDOWS)
#include <nall/windows/utf8.hpp>
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
namespace nall {
struct file_map {
struct mode { enum : uint { read, write, modify, append }; };
file_map(const file_map&) = delete;
auto operator=(const file_map&) = delete;
file_map() = default;
file_map(file_map&& source) { operator=(move(source)); }
file_map(const string& filename, uint mode) { open(filename, mode); }
~file_map() { close(); }
explicit operator bool() const { return _open; }
auto size() const -> uint64_t { return _size; }
auto data() -> uint8_t* { return _data; }
auto data() const -> const uint8_t* { return _data; }
//auto operator=(file_map&& source) -> file_map&;
//auto open(const string& filename, uint mode) -> bool;
//auto close() -> void;
private:
bool _open = false;
uint8_t* _data = nullptr;
uint64_t _size = 0;
#if defined(API_WINDOWS)
HANDLE _file = INVALID_HANDLE_VALUE;
HANDLE _map = INVALID_HANDLE_VALUE;
public:
auto operator=(file_map&& source) -> file_map& {
_open = source._open;
_data = source._data;
_size = source._size;
_file = source._file;
_map = source._map;
source._open = false;
source._data = nullptr;
source._size = 0;
source._file = INVALID_HANDLE_VALUE;
source._map = INVALID_HANDLE_VALUE;
return *this;
}
auto open(const string& filename, uint mode_) -> bool {
int desiredAccess, creationDisposition, protection, mapAccess;
switch(mode_) {
default: return false;
case mode::read:
desiredAccess = GENERIC_READ;
creationDisposition = OPEN_EXISTING;
protection = PAGE_READONLY;
mapAccess = FILE_MAP_READ;
break;
case mode::write:
//write access requires read access
desiredAccess = GENERIC_WRITE;
creationDisposition = CREATE_ALWAYS;
protection = PAGE_READWRITE;
mapAccess = FILE_MAP_ALL_ACCESS;
break;
case mode::modify:
desiredAccess = GENERIC_READ | GENERIC_WRITE;
creationDisposition = OPEN_EXISTING;
protection = PAGE_READWRITE;
mapAccess = FILE_MAP_ALL_ACCESS;
break;
case mode::append:
desiredAccess = GENERIC_READ | GENERIC_WRITE;
creationDisposition = CREATE_NEW;
protection = PAGE_READWRITE;
mapAccess = FILE_MAP_ALL_ACCESS;
break;
}
_file = CreateFileW(utf16_t(filename), desiredAccess, FILE_SHARE_READ, nullptr,
creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
if(_file == INVALID_HANDLE_VALUE) return false;
_size = GetFileSize(_file, nullptr);
_map = CreateFileMapping(_file, nullptr, protection, 0, _size, nullptr);
if(_map == INVALID_HANDLE_VALUE) {
CloseHandle(_file);
_file = INVALID_HANDLE_VALUE;
return false;
}
_data = (uint8_t*)MapViewOfFile(_map, mapAccess, 0, 0, _size);
return _open = _data;
}
auto close() -> void {
if(_data) {
UnmapViewOfFile(_data);
_data = nullptr;
}
if(_map != INVALID_HANDLE_VALUE) {
CloseHandle(_map);
_map = INVALID_HANDLE_VALUE;
}
if(_file != INVALID_HANDLE_VALUE) {
CloseHandle(_file);
_file = INVALID_HANDLE_VALUE;
}
_open = false;
}
#else
int _fd = -1;
public:
auto operator=(file_map&& source) -> file_map& {
_open = source._open;
_data = source._data;
_size = source._size;
_fd = source._fd;
source._open = false;
source._data = nullptr;
source._size = 0;
source._fd = -1;
return *this;
}
auto open(const string& filename, uint mode_) -> bool {
close();
int openFlags = 0;
int mmapFlags = 0;
switch(mode_) {
default: return false;
case mode::read:
openFlags = O_RDONLY;
mmapFlags = PROT_READ;
break;
case mode::write:
openFlags = O_RDWR | O_CREAT; //mmap() requires read access
mmapFlags = PROT_WRITE;
break;
case mode::modify:
openFlags = O_RDWR;
mmapFlags = PROT_READ | PROT_WRITE;
break;
case mode::append:
openFlags = O_RDWR | O_CREAT;
mmapFlags = PROT_READ | PROT_WRITE;
break;
}
_fd = ::open(filename, openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if(_fd < 0) return false;
struct stat _stat;
fstat(_fd, &_stat);
_size = _stat.st_size;
_data = (uint8_t*)mmap(nullptr, _size, mmapFlags, MAP_SHARED | MAP_NORESERVE, _fd, 0);
if(_data == MAP_FAILED) {
_data = nullptr;
::close(_fd);
_fd = -1;
return false;
}
return _open = _data;
}
auto close() -> void {
if(_data) {
munmap(_data, _size);
_data = nullptr;
}
if(_fd >= 0) {
::close(_fd);
_fd = -1;
}
_open = false;
}
#endif
};
}

View File

@ -1,27 +1,29 @@
#pragma once
#include <nall/platform.hpp>
#include <nall/inode.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/hash/sha256.hpp>
#include <nall/file-buffer.hpp>
namespace nall {
struct file : inode, varint {
enum class mode : uint { read, write, modify, append, readwrite = modify, writeread = append };
enum class index : uint { absolute, relative };
struct file : inode {
struct mode { enum : uint { read, write, modify, append }; };
struct index { enum : uint { absolute, relative }; };
file() = delete;
static auto open(const string& filename, uint mode) -> file_buffer {
return file_buffer{filename, mode};
}
static auto copy(const string& sourcename, const string& targetname) -> bool {
if(sourcename == targetname) return true;
file rd, wr;
if(rd.open(sourcename, mode::read) == false) return false;
if(wr.open(targetname, mode::write) == false) return false;
for(uint n = 0; n < rd.size(); n++) wr.write(rd.read());
if(auto reader = file::open(sourcename, mode::read)) {
if(auto writer = file::open(targetname, mode::write)) {
for(uint64_t n : range(reader.size())) writer.write(reader.read());
return true;
}
}
return false;
}
//attempt to rename file first
//this will fail if paths point to different file systems; fall back to copy+remove in this case
@ -29,14 +31,11 @@ struct file : inode, varint {
if(sourcename == targetname) return true;
if(rename(sourcename, targetname)) return true;
if(!writable(sourcename)) return false;
if(copy(sourcename, targetname)) {
remove(sourcename);
return true;
}
if(copy(sourcename, targetname)) return remove(sourcename), true;
return false;
}
static auto truncate(const string& filename, uint size) -> bool {
static auto truncate(const string& filename, uint64_t size) -> bool {
#if defined(API_POSIX)
return truncate(filename, size) == 0;
#elif defined(API_WINDOWS)
@ -61,7 +60,7 @@ struct file : inode, varint {
return !(data.st_mode & S_IFDIR);
}
static auto size(const string& filename) -> uintmax {
static auto size(const string& filename) -> uint64_t {
#if defined(API_POSIX)
struct stat data;
stat(filename, &data);
@ -72,263 +71,33 @@ struct file : inode, varint {
return S_ISREG(data.st_mode) ? data.st_size : 0u;
}
static auto read(const string& filename, uint reserve = 0) -> vector<uint8_t> {
static auto read(const string& filename) -> vector<uint8_t> {
vector<uint8_t> memory;
file fp;
if(fp.open(filename, mode::read)) {
memory.reserve(fp.size() + reserve);
if(auto fp = file::open(filename, mode::read)) {
memory.resize(fp.size());
fp.read(memory.data(), memory.size());
fp.read(memory);
}
return memory;
}
static auto read(const string& filename, void* data, uint size) -> bool {
file fp;
if(fp.open(filename, mode::read) == false) return false;
fp.read(data, size);
fp.close();
return true;
static auto read(const string& filename, array_span<uint8_t> memory) -> bool {
if(auto fp = file::open(filename, mode::read)) return fp.read(memory), true;
return false;
}
static auto write(const string& filename, const string& text) -> bool {
return write(filename, text.data(), text.size());
static auto write(const string& filename, array_view<uint8_t> memory) -> bool {
if(auto fp = file::open(filename, mode::write)) return fp.write(memory), true;
return false;
}
static auto write(const string& filename, const vector<uint8_t>& buffer) -> bool {
return write(filename, buffer.data(), buffer.size());
}
static auto write(const string& filename, const void* data, uint size) -> bool {
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.write(data, size);
fp.close();
return true;
}
static auto create(const string& filename) -> bool {
//create an empty file (will replace existing files)
file fp;
if(fp.open(filename, mode::write) == false) return false;
fp.close();
return true;
static auto create(const string& filename) -> bool {
if(auto fp = file::open(filename, mode::write)) return true;
return false;
}
static auto sha256(const string& filename) -> string {
auto buffer = read(filename);
return Hash::SHA256(buffer).digest();
}
auto read() -> uint8_t {
if(!fp) return 0xff; //file not open
if(file_mode == mode::write) return 0xff; //reads not permitted
if(file_offset >= file_size) return 0xff; //cannot read past end of file
buffer_sync();
return buffer[(file_offset++) & buffer_mask];
}
auto readl(uint length = 1) -> uintmax {
uintmax data = 0;
for(int i = 0; i < length; i++) {
data |= (uintmax)read() << (i << 3);
}
return data;
}
auto readm(uint length = 1) -> uintmax {
uintmax data = 0;
while(length--) {
data <<= 8;
data |= read();
}
return data;
}
auto reads(uint length) -> string {
string result;
result.resize(length);
for(auto& byte : result) byte = read();
return result;
}
auto read(void* data, uint size) -> void {
auto output = (uint8_t*)data;
while(size--) *output++ = read();
}
auto write(uint8_t data) -> void {
if(!fp) return; //file not open
if(file_mode == mode::read) return; //writes not permitted
buffer_sync();
buffer[(file_offset++) & buffer_mask] = data;
buffer_dirty = true;
if(file_offset > file_size) file_size = file_offset;
}
auto writel(uintmax data, uint length = 1) -> void {
while(length--) {
write(data);
data >>= 8;
}
}
auto writem(uintmax data, uint length = 1) -> void {
for(int i = length - 1; i >= 0; i--) {
write(data >> (i << 3));
}
}
auto writes(const string& s) -> void {
for(auto byte : s) write(byte);
}
auto write(const void* data, uint size) -> void {
auto input = (const uint8_t*)data;
while(size--) write(*input++);
}
template<typename... Args> auto print(Args... args) -> void {
string data(args...);
const char* p = data;
while(*p) write(*p++);
}
auto flush() -> void {
buffer_flush();
fflush(fp);
}
auto seek(int offset, index index_ = index::absolute) -> void {
if(!fp) return; //file not open
buffer_flush();
intmax req_offset = file_offset;
switch(index_) {
case index::absolute: req_offset = offset; break;
case index::relative: req_offset += offset; break;
}
if(req_offset < 0) req_offset = 0; //cannot seek before start of file
if(req_offset > file_size) {
if(file_mode == mode::read) { //cannot seek past end of file
req_offset = file_size;
} else { //pad file to requested location
file_offset = file_size;
while(file_size < req_offset) write(0x00);
}
}
file_offset = req_offset;
}
auto offset() const -> uint {
if(!fp) return 0; //file not open
return file_offset;
}
auto size() const -> uint {
if(!fp) return 0; //file not open
return file_size;
}
auto truncate(uint size) -> bool {
if(!fp) return false; //file not open
#if defined(API_POSIX)
return ftruncate(fileno(fp), size) == 0;
#elif defined(API_WINDOWS)
return _chsize(fileno(fp), size) == 0;
#endif
}
auto end() const -> bool {
if(!fp) return true; //file not open
return file_offset >= file_size;
}
auto open() const -> bool {
return fp;
}
explicit operator bool() const {
return open();
}
auto open(const string& filename, mode mode_) -> bool {
if(fp) return false;
switch(file_mode = mode_) {
#if defined(API_POSIX)
case mode::read: fp = fopen(filename, "rb" ); break;
case mode::write: fp = fopen(filename, "wb+"); break; //need read permission for buffering
case mode::readwrite: fp = fopen(filename, "rb+"); break;
case mode::writeread: fp = fopen(filename, "wb+"); break;
#elif defined(API_WINDOWS)
case mode::read: fp = _wfopen(utf16_t(filename), L"rb" ); break;
case mode::write: fp = _wfopen(utf16_t(filename), L"wb+"); break;
case mode::readwrite: fp = _wfopen(utf16_t(filename), L"rb+"); break;
case mode::writeread: fp = _wfopen(utf16_t(filename), L"wb+"); break;
#endif
}
if(!fp) return false;
buffer_offset = -1; //invalidate buffer
file_offset = 0;
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
return true;
}
auto close() -> void {
if(!fp) return;
buffer_flush();
fclose(fp);
fp = nullptr;
}
auto operator=(const file&) -> file& = delete;
file(const file&) = delete;
file() = default;
file(const string& filename, mode mode_) {
open(filename, mode_);
}
~file() {
close();
}
private:
enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 };
char buffer[buffer_size] = {0};
int buffer_offset = -1; //invalidate buffer
bool buffer_dirty = false;
FILE* fp = nullptr;
uint file_offset = 0;
uint file_size = 0;
mode file_mode = mode::read;
auto buffer_sync() -> void {
if(!fp) return; //file not open
if(buffer_offset != (file_offset & ~buffer_mask)) {
buffer_flush();
buffer_offset = file_offset & ~buffer_mask;
fseek(fp, buffer_offset, SEEK_SET);
uint length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
if(length) auto unused = fread(buffer, 1, length, fp);
}
}
auto buffer_flush() -> void {
if(!fp) return; //file not open
if(file_mode == mode::read) return; //buffer cannot be written to
if(buffer_offset < 0) return; //buffer unused
if(buffer_dirty == false) return; //buffer unmodified since read
fseek(fp, buffer_offset, SEEK_SET);
uint length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
if(length) auto unused = fwrite(buffer, 1, length, fp);
buffer_offset = -1; //invalidate buffer
buffer_dirty = false;
return Hash::SHA256(read(filename)).digest();
}
};

View File

@ -1,213 +0,0 @@
#pragma once
#include <nall/file.hpp>
#include <nall/stdint.hpp>
#include <stdio.h>
#include <stdlib.h>
#if defined(PLATFORM_WINDOWS)
#include <nall/windows/utf8.hpp>
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
namespace nall {
struct filemap {
enum class mode : unsigned { read, write, readwrite, writeread };
filemap() { p_ctor(); }
filemap(const string& filename, mode mode_) { p_ctor(); p_open(filename, mode_); }
~filemap() { p_dtor(); }
explicit operator bool() const { return open(); }
auto open() const -> bool { return p_open(); }
auto open(const string& filename, mode mode_) -> bool { return p_open(filename, mode_); }
auto close() -> void { return p_close(); }
auto size() const -> unsigned { return p_size; }
auto data() -> uint8_t* { return p_handle; }
auto data() const -> const uint8_t* { return p_handle; }
private:
uint8_t* p_handle = nullptr;
unsigned p_size = 0;
#if defined(API_WINDOWS)
//=============
//MapViewOfFile
//=============
HANDLE p_filehandle;
HANDLE p_maphandle;
auto p_open() const -> bool {
return p_handle;
}
auto p_open(const string& filename, mode mode_) -> bool {
if(file::exists(filename) && file::size(filename) == 0) {
p_handle = nullptr;
p_size = 0;
return true;
}
int desired_access, creation_disposition, flprotect, map_access;
switch(mode_) {
default: return false;
case mode::read:
desired_access = GENERIC_READ;
creation_disposition = OPEN_EXISTING;
flprotect = PAGE_READONLY;
map_access = FILE_MAP_READ;
break;
case mode::write:
//write access requires read access
desired_access = GENERIC_WRITE;
creation_disposition = CREATE_ALWAYS;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
case mode::readwrite:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = OPEN_EXISTING;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
case mode::writeread:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = CREATE_NEW;
flprotect = PAGE_READWRITE;
map_access = FILE_MAP_ALL_ACCESS;
break;
}
p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, nullptr,
creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
if(p_filehandle == INVALID_HANDLE_VALUE) return false;
p_size = GetFileSize(p_filehandle, nullptr);
p_maphandle = CreateFileMapping(p_filehandle, nullptr, flprotect, 0, p_size, nullptr);
if(p_maphandle == INVALID_HANDLE_VALUE) {
CloseHandle(p_filehandle);
p_filehandle = INVALID_HANDLE_VALUE;
return false;
}
p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size);
return p_handle;
}
auto p_close() -> void {
if(p_handle) {
UnmapViewOfFile(p_handle);
p_handle = nullptr;
}
if(p_maphandle != INVALID_HANDLE_VALUE) {
CloseHandle(p_maphandle);
p_maphandle = INVALID_HANDLE_VALUE;
}
if(p_filehandle != INVALID_HANDLE_VALUE) {
CloseHandle(p_filehandle);
p_filehandle = INVALID_HANDLE_VALUE;
}
}
auto p_ctor() -> void {
p_filehandle = INVALID_HANDLE_VALUE;
p_maphandle = INVALID_HANDLE_VALUE;
}
auto p_dtor() -> void {
close();
}
#else
//====
//mmap
//====
int p_fd;
auto p_open() const -> bool {
return p_handle;
}
auto p_open(const string& filename, mode mode_) -> bool {
if(file::exists(filename) && file::size(filename) == 0) {
p_handle = nullptr;
p_size = 0;
return true;
}
int open_flags, mmap_flags;
switch(mode_) {
default: return false;
case mode::read:
open_flags = O_RDONLY;
mmap_flags = PROT_READ;
break;
case mode::write:
open_flags = O_RDWR | O_CREAT; //mmap() requires read access
mmap_flags = PROT_WRITE;
break;
case mode::readwrite:
open_flags = O_RDWR;
mmap_flags = PROT_READ | PROT_WRITE;
break;
case mode::writeread:
open_flags = O_RDWR | O_CREAT;
mmap_flags = PROT_READ | PROT_WRITE;
break;
}
p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if(p_fd < 0) return false;
struct stat p_stat;
fstat(p_fd, &p_stat);
p_size = p_stat.st_size;
p_handle = (uint8_t*)mmap(nullptr, p_size, mmap_flags, MAP_SHARED, p_fd, 0);
if(p_handle == MAP_FAILED) {
p_handle = nullptr;
::close(p_fd);
p_fd = -1;
return false;
}
return p_handle;
}
auto p_close() -> void {
if(p_handle) {
munmap(p_handle, p_size);
p_handle = nullptr;
}
if(p_fd >= 0) {
::close(p_fd);
p_fd = -1;
}
}
auto p_ctor() -> void {
p_fd = -1;
}
auto p_dtor() -> void {
p_close();
}
#endif
};
}

View File

@ -2,7 +2,7 @@
#include <algorithm>
#include <nall/filemap.hpp>
#include <nall/file-map.hpp>
#include <nall/interpolation.hpp>
#include <nall/stdint.hpp>
#include <nall/decode/bmp.hpp>

View File

@ -11,6 +11,10 @@ namespace nall {
struct inode {
enum class time : uint { create, modify, access };
inode() = delete;
inode(const inode&) = delete;
auto operator=(const inode&) -> inode& = delete;
static auto exists(const string& name) -> bool {
return access(name, F_OK) == 0;
}
@ -45,19 +49,21 @@ struct inode {
return data.st_gid;
}
static auto owner(const string& name) -> string {
#if !defined(PLATFORM_WINDOWS)
static auto user(const string& name) -> string {
struct passwd* pw = getpwuid(uid(name));
if(pw && pw->pw_name) return pw->pw_name;
#endif
return {};
}
static auto group(const string& name) -> string {
#if !defined(PLATFORM_WINDOWS)
struct group* gr = getgrgid(gid(name));
if(gr && gr->gr_name) return gr->gr_name;
#endif
return {};
}
#endif
static auto timestamp(const string& name, time mode = time::modify) -> uint64_t {
struct stat data{};
@ -83,6 +89,41 @@ struct inode {
return 0;
}
static auto setMode(const string& name, uint mode) -> bool {
#if !defined(PLATFORM_WINDOWS)
return chmod(name, mode) == 0;
#else
return _wchmod(utf16_t(name), (mode & 0400 ? _S_IREAD : 0) | (mode & 0200 ? _S_IWRITE : 0)) == 0;
#endif
}
static auto setOwner(const string& name, const string& owner) -> bool {
#if !defined(PLATFORM_WINDOWS)
struct passwd* pwd = getpwnam(owner);
if(!pwd) return false;
return chown(name, pwd->pw_uid, inode::gid(name)) == 0;
#else
return true;
#endif
}
static auto setGroup(const string& name, const string& group) -> bool {
#if !defined(PLATFORM_WINDOWS)
struct group* grp = getgrnam(group);
if(!grp) return false;
return chown(name, inode::uid(name), grp->gr_gid) == 0;
#else
return true;
#endif
}
static auto setTimestamp(const string& name, uint64_t value, time mode = time::modify) -> bool {
struct utimbuf timeBuffer;
timeBuffer.modtime = mode == time::modify ? value : inode::timestamp(name, time::modify);
timeBuffer.actime = mode == time::access ? value : inode::timestamp(name, time::access);
return utime(name, &timeBuffer) == 0;
}
//returns true if 'name' already exists
static auto create(const string& name, uint permissions = 0755) -> bool {
if(exists(name)) return true;

View File

@ -1,25 +0,0 @@
#pragma once
//note: gcc 4.9 does not support user-defined literals with arguments other than const char*
//once nall increases the minimum required GCC version, the use of nall/atoi.hpp can beremoved
#include <nall/atoi.hpp>
namespace nall { namespace Literal {
struct Capacity { uint value; };
struct Size { uint value; };
}}
namespace nall {
constexpr inline auto operator"" _capacity(const char* s) -> Literal::Capacity {
return {(uint)toNatural(s)};
}
constexpr inline auto operator"" _size(const char* s) -> Literal::Size {
return {(uint)toNatural(s)};
}
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <nall/string.hpp>
namespace nall { namespace Location {
// (/parent/child.type/)

View File

@ -5,6 +5,12 @@
namespace nall { namespace MAC {
struct Poly1305 {
auto authenticate(array_view<uint8_t> memory, uint256_t nonce) -> uint128_t {
initialize(nonce);
process(memory.data(), memory.size());
return finish();
}
auto initialize(uint256_t key) -> void {
uint64_t t0 = key >> 0;
uint64_t t1 = key >> 64;

View File

@ -1,10 +1,11 @@
#pragma once
#include <nall/platform.hpp>
#include <nall/arguments.hpp>
#include <nall/string.hpp>
namespace nall {
auto main(vector<string> arguments) -> void;
auto main(Arguments arguments) -> void;
auto main(int argc, char** argv) -> int {
#if defined(PLATFORM_WINDOWS)
@ -14,21 +15,8 @@ namespace nall {
_setmode(_fileno(stdin), O_BINARY);
_setmode(_fileno(stdout), O_BINARY);
_setmode(_fileno(stderr), O_BINARY);
utf8_arguments(argc, argv);
#endif
vector<string> arguments;
for(auto n : range(argc)) {
string argument = argv[n];
//normalize directory and file path arguments
if(directory::exists(argument)) argument.transform("\\", "/").trimRight("/").append("/");
else if(file::exists(argument)) argument.transform("\\", "/").trimRight("/");
arguments.append(argument);
}
return main(move(arguments)), EXIT_SUCCESS;
return main(move(Arguments{argc, argv})), EXIT_SUCCESS;
}
}

View File

@ -15,6 +15,7 @@
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
//#include <nall/arguments.hpp>
#include <nall/arithmetic.hpp>
#include <nall/array.hpp>
#include <nall/array-span.hpp>
@ -28,7 +29,8 @@
#include <nall/dl.hpp>
#include <nall/endian.hpp>
#include <nall/file.hpp>
#include <nall/filemap.hpp>
#include <nall/file-buffer.hpp>
#include <nall/file-map.hpp>
#include <nall/function.hpp>
#include <nall/hashset.hpp>
#include <nall/hid.hpp>
@ -58,6 +60,7 @@
#include <nall/simd.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/terminal.hpp>
#include <nall/thread.hpp>
#include <nall/traits.hpp>
#include <nall/unique-pointer.hpp>
@ -65,6 +68,7 @@
#include <nall/varint.hpp>
#include <nall/vector.hpp>
#include <nall/view.hpp>
#include <nall/arguments.hpp> //todo: compilation errors when included earlier
#include <nall/decode/base.hpp>
#include <nall/decode/base64.hpp>
#include <nall/decode/bmp.hpp>

View File

@ -6,7 +6,7 @@ namespace nall { namespace Path {
inline auto active() -> string {
char path[PATH_MAX] = "";
auto unused = getcwd(path, PATH_MAX);
(void)getcwd(path, PATH_MAX);
string result = path;
if(!result) result = ".";
result.transform("\\", "/");

View File

@ -37,6 +37,7 @@ namespace Math {
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <utime.h>
#include <fcntl.h>
#include <sys/types.h>

View File

@ -54,8 +54,10 @@ protected:
}
};
namespace PRNG {
//Galois linear feedback shift register using CRC64 polynomials
struct PRNG_LFSR : RNG<PRNG_LFSR> {
struct LFSR : RNG<LFSR> {
auto seed(maybe<uint64_t> seed = {}) -> void {
lfsr = seed ? seed() : (uint64_t)randomSeed();
for(uint n : range(8)) read(); //hide the CRC64 polynomial from initial output
@ -73,10 +75,10 @@ private:
static const uint64_t crc64 = 0xc96c'5795'd787'0f42;
uint64_t lfsr = crc64;
friend class RNG<PRNG_LFSR>;
friend class RNG<LFSR>;
};
struct PRNG_PCG : RNG<PRNG_PCG> {
struct PCG : RNG<PCG> {
auto seed(maybe<uint32_t> seed = {}, maybe<uint32_t> sequence = {}) -> void {
if(!seed) seed = (uint32_t)randomSeed();
if(!sequence) sequence = 0;
@ -105,12 +107,16 @@ private:
uint64_t state = 0;
uint64_t increment = 0;
friend class RNG<PRNG_PCG>;
friend class RNG<PCG>;
};
}
namespace CSPRNG {
//XChaCha20 cryptographically secure pseudo-random number generator
struct CSPRNG_XChaCha20 : RNG<CSPRNG_XChaCha20> {
CSPRNG_XChaCha20() { seed(); }
struct XChaCha20 : RNG<XChaCha20> {
XChaCha20() { seed(); }
auto seed(maybe<uint256_t> key = {}, maybe<uint192_t> nonce = {}) -> void {
//the randomness comes from the key; the nonce just adds a bit of added entropy
@ -130,16 +136,15 @@ private:
Cipher::XChaCha20 context{0, 0};
uint counter = 0;
friend class RNG<CSPRNG_XChaCha20>;
friend class RNG<XChaCha20>;
};
}
//
using PRNG = PRNG_PCG;
using CSPRNG = CSPRNG_XChaCha20;
template<typename T = uint64_t> inline auto random() -> T {
static PRNG_PCG pcg; //note: unseeded
static PRNG::PCG pcg; //note: unseeded
return pcg.random<T>();
}

View File

@ -31,6 +31,10 @@ struct has_serialize {
struct serializer {
enum Mode : uint { Load, Save, Size };
explicit operator bool() const {
return _size;
}
auto mode() const -> Mode {
return _mode;
}

View File

@ -234,6 +234,20 @@ template<> struct stringify<const string_view&> {
const string_view& _view;
};
template<> struct stringify<array_view<uint8_t>> {
stringify(const array_view<uint8_t>& source) : _view(source) {}
auto data() const -> const char* { return _view.data<const char>(); }
auto size() const -> uint { return _view.size(); }
const array_view<uint8_t>& _view;
};
template<> struct stringify<const array_view<uint8_t>&> {
stringify(const array_view<uint8_t>& source) : _view(source) {}
auto data() const -> const char* { return _view.data<const char>(); }
auto size() const -> uint { return _view.size(); }
const array_view<uint8_t>& _view;
};
template<> struct stringify<string_pascal> {
stringify(const string_pascal& source) : _text(source) {}
auto data() const -> const char* { return _text.data(); }
@ -250,6 +264,7 @@ template<> struct stringify<const string_pascal&> {
//pointers
//note: T = char* is matched by stringify<string_view>
template<typename T> struct stringify<T*> {
stringify(const T* source) {
if(!source) {

View File

@ -18,7 +18,7 @@ auto string::read(string_view filename) -> string {
rewind(fp);
result.resize(filesize);
auto unused = fread(result.get(), 1, filesize, fp);
(void)fread(result.get(), 1, filesize, fp);
return fclose(fp), result;
}

65
nall/terminal.hpp Normal file
View File

@ -0,0 +1,65 @@
#pragma once
#include <nall/string.hpp>
namespace nall { namespace terminal {
inline auto escapable() -> bool {
#if defined(PLATFORM_WINDOWS)
//todo: colors are supported by Windows 10+ and with alternate terminals (eg msys)
//disabled for now for compatibility with Windows 7 and 8.1's cmd.exe
return false;
#endif
return true;
}
namespace color {
template<typename... P> inline auto black(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[30m", string{forward<P>(p)...}, "\e[0m"};
}
template<typename... P> inline auto blue(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[94m", string{forward<P>(p)...}, "\e[0m"};
}
template<typename... P> inline auto green(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[92m", string{forward<P>(p)...}, "\e[0m"};
}
template<typename... P> inline auto cyan(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[96m", string{forward<P>(p)...}, "\e[0m"};
}
template<typename... P> inline auto red(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[91m", string{forward<P>(p)...}, "\e[0m"};
}
template<typename... P> inline auto magenta(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[95m", string{forward<P>(p)...}, "\e[0m"};
}
template<typename... P> inline auto yellow(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[93m", string{forward<P>(p)...}, "\e[0m"};
}
template<typename... P> inline auto white(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[97m", string{forward<P>(p)...}, "\e[0m"};
}
template<typename... P> inline auto gray(P&&... p) -> string {
if(!escapable()) return string{forward<P>(p)...};
return {"\e[37m", string{forward<P>(p)...}, "\e[0m"};
}
}
}}

View File

@ -1,6 +1,7 @@
#pragma once
#include <type_traits>
#include <nall/stdint.hpp>
//pull all type traits used by nall from std namespace into nall namespace
//this removes the requirement to prefix type traits with std:: within nall
@ -29,3 +30,10 @@ namespace nall {
using std::swap;
using std::true_type;
}
namespace std {
#if INTMAX_BITS >= 128
template<> struct is_signed<int128_t> : true_type {};
template<> struct is_unsigned<uint128_t> : true_type {};
#endif
}

View File

@ -7,7 +7,6 @@
#include <nall/bit.hpp>
#include <nall/function.hpp>
#include <nall/iterator.hpp>
#include <nall/literals.hpp>
#include <nall/maybe.hpp>
#include <nall/memory.hpp>
#include <nall/merge-sort.hpp>
@ -23,8 +22,6 @@ struct vector_base {
//core.hpp
vector_base() = default;
vector_base(Literal::Capacity capacity);
vector_base(Literal::Size size);
vector_base(const initializer_list<T>& values);
vector_base(const type& source);
vector_base(type&& source);
@ -121,6 +118,7 @@ struct vector_base {
//utility.hpp
auto fill(const T& value = {}) -> void;
auto sort(const function<bool (const T& lhs, const T& rhs)>& comparator = [](auto& lhs, auto& rhs) { return lhs < rhs; }) -> void;
auto reverse() -> void;
auto find(const function<bool (const T& lhs)>& comparator) -> maybe<uint>;
auto find(const T& value) const -> maybe<uint>;
auto findSorted(const T& value) const -> maybe<uint>;

View File

@ -2,14 +2,6 @@
namespace nall {
template<typename T> vector<T>::vector(Literal::Capacity capacity) {
reserve(capacity.value);
}
template<typename T> vector<T>::vector(Literal::Size size) {
resize(size.value);
}
template<typename T> vector<T>::vector(const initializer_list<T>& values) {
reserveRight(values.size());
for(auto& value : values) append(value);

View File

@ -119,7 +119,7 @@ template<typename T> auto vector<T>::resizeLeft(uint size, const T& value) -> bo
if(size > _size) { //grow
reserveLeft(size);
_pool -= size - _size;
for(uint n : reverse(range(size - _size))) new(_pool + n) T(value);
for(uint n : nall::reverse(range(size - _size))) new(_pool + n) T(value);
_left -= size - _size;
_size = size;
return true;

View File

@ -11,7 +11,27 @@ template<> struct vector<uint8_t> : vector_base<uint8_t> {
}
template<typename U> auto appendm(U value, uint size) -> void {
for(uint byte : reverse(range(size))) append(uint8_t(value >> byte * 8));
for(uint byte : nall::reverse(range(size))) append(uint8_t(value >> byte * 8));
}
//note: string_view is not declared here yet ...
auto appends(array_view<uint8_t> memory) -> void {
for(uint8_t byte : memory) append(byte);
}
template<typename U> auto readl(int offset, uint size) -> U {
if(offset < 0) offset = this->size() - abs(offset);
U value = 0;
for(uint byte : range(size)) value |= (U)operator[](offset + byte) << byte * 8;
return value;
}
auto view(uint offset, uint length) -> array_view<uint8_t> {
#ifdef DEBUG
struct out_of_bounds {};
if(offset + length >= size()) throw out_of_bounds{};
#endif
return {data() + offset, length};
}
};

View File

@ -10,6 +10,12 @@ template<typename T> auto vector<T>::sort(const function<bool (const T& lhs, con
nall::sort(_pool, _size, comparator);
}
template<typename T> auto vector<T>::reverse() -> void {
vector<T> reversed;
for(uint n : range(size())) reversed.prepend(_pool[n]);
operator=(move(reversed));
}
template<typename T> auto vector<T>::find(const function<bool (const T& lhs)>& comparator) -> maybe<uint> {
for(uint n : range(size())) if(comparator(_pool[n])) return n;
return nothing;

View File

@ -20,7 +20,7 @@ struct file : vfs::file {
}
auto seek(intmax offset_, index index_) -> void override {
_fp.seek(offset_, (nall::file::index)index_);
_fp.seek(offset_, (uint)index_);
}
auto read() -> uint8_t override {
@ -41,11 +41,11 @@ private:
auto operator=(const file&) -> file& = delete;
auto _open(string location_, mode mode_) -> bool {
if(!_fp.open(location_, (nall::file::mode)mode_)) return false;
if(!_fp.open(location_, (uint)mode_)) return false;
return true;
}
nall::file _fp;
file_buffer _fp;
};
}}}

View File

@ -123,7 +123,7 @@ struct InputJoypadUdev {
play.type = EV_FF;
play.code = jp.effectID;
play.value = enable;
auto unused = write(jp.fd, &play, sizeof(input_event));
(void)write(jp.fd, &play, sizeof(input_event));
}
return true;