From bd814f0358a55a995344500914719a68e1f7f3c0 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 26 Aug 2018 16:49:54 +1000 Subject: [PATCH] Update to v106r59 release. byuu says: Changelog: - fixed bug in Emulator::Game::Memory::operator bool() - nall: renamed view back to `string_view` - nall:: implemented `array_view` - Game Boy: split cartridge-specific input mappings (rumble, accelerometer) to their own separate ports - Game Boy: fixed MBC7 accelerometer x-axis - icarus: Game Boy, Super Famicom, Mega Drive cores output internal header game titles to heuristics manifests - higan, icarus, hiro/gtk: improve viewport geometry configuration; fixed higan crashing bug with XShm driver - higan: connect Video::poll(),update() functionality - hiro, ruby: several compilation / bugfixes, should get the macOS port compiling again, hopefully [Sintendo] - ruby/video/xshm: fix crashing bug on window resize - a bit hacky; it's throwing BadAccess Xlib warnings, but they're not fatal, so I am catching and ignoring them - bsnes: removed Application::Windows::onModalChange hook that's no longer needed [Screwtape] --- higan/emulator/emulator.hpp | 2 +- higan/emulator/game.hpp | 6 +- higan/gb/cartridge/cartridge.cpp | 2 +- higan/gb/cartridge/mbc5/mbc5.cpp | 2 +- higan/gb/cartridge/mbc7/mbc7.cpp | 4 +- higan/gb/interface/interface.cpp | 22 +- higan/gb/interface/interface.hpp | 3 + higan/gba/cartridge/cartridge.cpp | 2 +- higan/gba/player/player.cpp | 2 +- higan/pce/cartridge/cartridge.cpp | 2 +- higan/sfc/cartridge/cartridge.cpp | 2 +- .../presentation/presentation.cpp | 9 +- higan/target-bsnes/program/game.cpp | 2 +- higan/target-bsnes/program/hacks.cpp | 18 +- higan/target-bsnes/program/patch.cpp | 12 +- higan/target-bsnes/program/program.hpp | 2 +- higan/target-bsnes/program/states.cpp | 6 +- higan/target-bsnes/tools/state-manager.cpp | 2 +- .../presentation/presentation.cpp | 15 +- higan/target-higan/program/program.cpp | 4 +- higan/target-higan/program/utility.cpp | 4 + higan/ws/cartridge/cartridge.cpp | 2 +- hiro/cocoa/widget/table-view-column.cpp | 2 +- hiro/cocoa/widget/table-view.cpp | 34 +- hiro/core/shared.hpp | 4 +- hiro/extension/browser-dialog.cpp | 2 + hiro/gtk/window.cpp | 1 + icarus/heuristics/famicom.cpp | 2 +- icarus/heuristics/mega-drive.cpp | 5 + icarus/heuristics/super-famicom.cpp | 25 +- nall/array-view.hpp | 60 + nall/array.hpp | 6 + nall/beat/single/apply.hpp | 44 +- nall/beat/single/create.hpp | 73 +- nall/decode/bwt.hpp | 15 +- nall/decode/huffman.hpp | 8 +- nall/decode/lzsa.hpp | 1 - nall/decode/lzss.hpp | 44 - nall/decode/mtf.hpp | 22 +- nall/decode/rle.hpp | 7 +- nall/decode/url.hpp | 2 +- nall/div-suf-sort.hpp | 1440 ----------------- nall/encode/bwt.hpp | 89 +- nall/encode/dictionary.hpp | 73 - nall/encode/huffman.hpp | 16 +- nall/encode/lzsa.hpp | 28 +- nall/encode/lzss.hpp | 76 - nall/encode/mtf.hpp | 16 +- nall/encode/rle.hpp | 17 +- nall/encode/url.hpp | 2 +- nall/encode/zip.hpp | 2 +- nall/file.hpp | 2 +- nall/hash/crc16.hpp | 7 +- nall/hash/crc32.hpp | 7 +- nall/hash/crc64.hpp | 7 +- nall/hash/hash.hpp | 4 + nall/hash/sha224.hpp | 7 +- nall/hash/sha256.hpp | 7 +- nall/hash/sha384.hpp | 7 +- nall/hash/sha512.hpp | 7 +- nall/induced-sort.hpp | 175 ++ nall/location.hpp | 14 +- nall/nall.hpp | 1 + nall/path.hpp | 2 +- nall/shared-pointer.hpp | 6 +- nall/string.hpp | 127 +- nall/string/cast.hpp | 12 +- nall/string/compare.hpp | 20 +- nall/string/convert.hpp | 2 +- nall/string/find.hpp | 16 +- nall/string/markup/bml.hpp | 14 +- nall/string/match.hpp | 4 +- nall/string/pascal.hpp | 4 +- nall/string/replace.hpp | 10 +- nall/string/split.hpp | 10 +- nall/string/trim.hpp | 12 +- nall/string/utility.hpp | 6 +- nall/string/vector.hpp | 8 +- nall/string/view.hpp | 26 +- nall/suffix-array.hpp | 397 +++-- nall/vector.hpp | 30 +- nall/vector/core.hpp | 12 +- nall/vector/memory.hpp | 57 + nall/vector/utility.hpp | 4 + ruby/audio/openal.cpp | 2 +- ruby/input/quartz.cpp | 2 +- ruby/video/cgl.cpp | 2 +- ruby/video/direct3d.cpp | 6 +- ruby/video/xshm.cpp | 43 +- 89 files changed, 1079 insertions(+), 2241 deletions(-) create mode 100644 nall/array-view.hpp delete mode 100644 nall/decode/lzss.hpp delete mode 100644 nall/div-suf-sort.hpp delete mode 100644 nall/encode/dictionary.hpp delete mode 100644 nall/encode/lzss.hpp create mode 100644 nall/induced-sort.hpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index f3d77c4c..267d2bb3 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -28,7 +28,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.58"; + static const string Version = "106.59"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/emulator/game.hpp b/higan/emulator/game.hpp index 13f1b5cb..3617ed65 100644 --- a/higan/emulator/game.hpp +++ b/higan/emulator/game.hpp @@ -6,14 +6,14 @@ struct Game { struct Memory; struct Oscillator; - inline auto load(view) -> void; + inline auto load(string_view) -> void; inline auto memory(Markup::Node) -> maybe; inline auto oscillator(natural = 0) -> maybe; struct Memory { Memory() = default; inline Memory(Markup::Node); - explicit operator bool() const { return type; } + explicit operator bool() const { return (bool)type; } inline auto name() const -> string; string type; @@ -44,7 +44,7 @@ struct Game { vector oscillatorList; }; -auto Game::load(view text) -> void { +auto Game::load(string_view text) -> void { document = BML::unserialize(text); sha256 = document["game/sha256"].text(); diff --git a/higan/gb/cartridge/cartridge.cpp b/higan/gb/cartridge/cartridge.cpp index 1e8688c9..36141e53 100644 --- a/higan/gb/cartridge/cartridge.cpp +++ b/higan/gb/cartridge/cartridge.cpp @@ -109,7 +109,7 @@ auto Cartridge::load() -> bool { } } - information.sha256 = Hash::SHA256(rom.data, rom.size).digest(); + information.sha256 = Hash::SHA256({rom.data, rom.size}).digest(); mapper->load(document); return true; } diff --git a/higan/gb/cartridge/mbc5/mbc5.cpp b/higan/gb/cartridge/mbc5/mbc5.cpp index a4d8785b..d4ed53be 100644 --- a/higan/gb/cartridge/mbc5/mbc5.cpp +++ b/higan/gb/cartridge/mbc5/mbc5.cpp @@ -32,7 +32,7 @@ auto Cartridge::MBC5::write(uint16 address, uint8 data) -> void { } if((address & 0xe000) == 0x4000) { //$4000-5fff - if(cartridge.rumble) platform->inputRumble(ID::Port::Hardware, ID::Device::Controls, 10, data.bit(3)); + if(cartridge.rumble) platform->inputRumble(ID::Port::Cartridge, ID::Device::MBC5, 0, data.bit(3)); io.ram.bank = data.bits(0,3); return; } diff --git a/higan/gb/cartridge/mbc7/mbc7.cpp b/higan/gb/cartridge/mbc7/mbc7.cpp index 7e74ff07..51d4a9bd 100644 --- a/higan/gb/cartridge/mbc7/mbc7.cpp +++ b/higan/gb/cartridge/mbc7/mbc7.cpp @@ -72,8 +72,8 @@ auto Cartridge::MBC7::write(uint16 address, uint8 data) -> void { case 1: { if(data != 0xaa) break; - io.accelerometer.x = Center + platform->inputPoll(ID::Port::Hardware, ID::Device::Controls, 8); - io.accelerometer.y = Center + platform->inputPoll(ID::Port::Hardware, ID::Device::Controls, 9); + io.accelerometer.x = Center - platform->inputPoll(ID::Port::Cartridge, ID::Device::MBC7, 0); + io.accelerometer.y = Center + platform->inputPoll(ID::Port::Cartridge, ID::Device::MBC7, 1); break; } diff --git a/higan/gb/interface/interface.cpp b/higan/gb/interface/interface.cpp index 0f156df3..0663925c 100644 --- a/higan/gb/interface/interface.cpp +++ b/higan/gb/interface/interface.cpp @@ -46,7 +46,8 @@ auto Interface::unload() -> void { } auto Interface::ports() -> vector { return { - {ID::Port::Hardware, "Hardware"}}; + {ID::Port::Hardware, "Hardware"}, + {ID::Port::Cartridge, "Cartridge"}}; } auto Interface::devices(uint port) -> vector { @@ -54,6 +55,11 @@ auto Interface::devices(uint port) -> vector { {ID::Device::Controls, "Controls"} }; + if(port == ID::Port::Cartridge) return { + {ID::Device::MBC5, "MBC5"}, + {ID::Device::MBC7, "MBC7"} + }; + return {}; } @@ -68,10 +74,16 @@ auto Interface::inputs(uint device) -> vector { {Type::Button, "B" }, {Type::Button, "A" }, {Type::Control, "Select"}, - {Type::Control, "Start" }, - {Type::Axis, "X-axis"}, - {Type::Axis, "Y-axis"}, - {Type::Rumble, "Rumble"} + {Type::Control, "Start" } + }; + + if(device == ID::Device::MBC5) return { + {Type::Rumble, "Rumble"} + }; + + if(device == ID::Device::MBC7) return { + {Type::Axis, "Accelerometer - X-axis"}, + {Type::Axis, "Accelerometer - Y-axis"} }; return {}; diff --git a/higan/gb/interface/interface.hpp b/higan/gb/interface/interface.hpp index cf07b310..58d26bae 100644 --- a/higan/gb/interface/interface.hpp +++ b/higan/gb/interface/interface.hpp @@ -12,10 +12,13 @@ struct ID { struct Port { enum : uint { Hardware, + Cartridge, };}; struct Device { enum : uint { Controls, + MBC5, + MBC7, };}; }; diff --git a/higan/gba/cartridge/cartridge.cpp b/higan/gba/cartridge/cartridge.cpp index 549ac472..022fe3e2 100644 --- a/higan/gba/cartridge/cartridge.cpp +++ b/higan/gba/cartridge/cartridge.cpp @@ -94,7 +94,7 @@ auto Cartridge::load() -> bool { } } - information.sha256 = Hash::SHA256(mrom.data, mrom.size).digest(); + information.sha256 = Hash::SHA256({mrom.data, mrom.size}).digest(); return true; } diff --git a/higan/gba/player/player.cpp b/higan/gba/player/player.cpp index bcbd4ea9..a68989e0 100644 --- a/higan/gba/player/player.cpp +++ b/higan/gba/player/player.cpp @@ -42,7 +42,7 @@ auto Player::power() -> void { auto Player::frame() -> void { //todo: this is not a very performant way of detecting the GBP logo ... - uint32 hash = Hash::CRC32(ppu.output, 240 * 160 * sizeof(uint32)).value(); + uint32 hash = Hash::CRC32({ppu.output, 240 * 160 * sizeof(uint32)}).value(); status.logoDetected = (hash == 0x7776eb55); if(status.logoDetected) { diff --git a/higan/pce/cartridge/cartridge.cpp b/higan/pce/cartridge/cartridge.cpp index 9f3704db..38f0e991 100644 --- a/higan/pce/cartridge/cartridge.cpp +++ b/higan/pce/cartridge/cartridge.cpp @@ -34,7 +34,7 @@ auto Cartridge::load() -> bool { } } - information.sha256 = Hash::SHA256(rom.data, rom.size).digest(); + information.sha256 = Hash::SHA256({rom.data, rom.size}).digest(); return true; } diff --git a/higan/sfc/cartridge/cartridge.cpp b/higan/sfc/cartridge/cartridge.cpp index f65215db..0e6db152 100644 --- a/higan/sfc/cartridge/cartridge.cpp +++ b/higan/sfc/cartridge/cartridge.cpp @@ -63,7 +63,7 @@ auto Cartridge::load() -> bool { //BS Memory else if(cartridge.has.MCC && cartridge.has.BSMemorySlot) { - information.sha256 = Hash::SHA256(bsmemory.memory.data(), bsmemory.memory.size()).digest(); + information.sha256 = Hash::SHA256({bsmemory.memory.data(), bsmemory.memory.size()}).digest(); } //Sufami Turbo diff --git a/higan/target-bsnes/presentation/presentation.cpp b/higan/target-bsnes/presentation/presentation.cpp index a404be11..502a45ba 100644 --- a/higan/target-bsnes/presentation/presentation.cpp +++ b/higan/target-bsnes/presentation/presentation.cpp @@ -144,6 +144,8 @@ auto Presentation::create() -> void { program.gameQueue = locations; program.load(); setFocused(); + }).onSize([&] { + configureViewport(); }); iconLayout.setAlignment(0.0); @@ -219,10 +221,7 @@ auto Presentation::configureViewport() -> void { } auto Presentation::clearViewport() -> void { - if(!emulator->loaded()) { - viewportLayout.setPadding(); - configureViewport(); - } + if(!emulator->loaded()) viewportLayout.setPadding(); if(!visible() || !video) return; uint32_t* output; @@ -286,8 +285,6 @@ auto Presentation::resizeViewport() -> void { paddingWidth / 2, paddingHeight / 2, paddingWidth - paddingWidth / 2, paddingHeight - paddingHeight / 2 }); - - configureViewport(); } auto Presentation::resizeWindow() -> void { diff --git a/higan/target-bsnes/program/game.cpp b/higan/target-bsnes/program/game.cpp index 9da3a222..bf2d5ed2 100644 --- a/higan/target-bsnes/program/game.cpp +++ b/higan/target-bsnes/program/game.cpp @@ -115,7 +115,7 @@ auto Program::loadSuperFamicom(string location) -> bool { superFamicom.verified = true; } } - superFamicom.label = heuristics.label(); + superFamicom.title = heuristics.title(); superFamicom.manifest = manifest ? manifest : heuristics.manifest(); hackPatchMemory(rom); hackOverclockSuperFX(); diff --git a/higan/target-bsnes/program/hacks.cpp b/higan/target-bsnes/program/hacks.cpp index 260277ed..13e3ec1d 100644 --- a/higan/target-bsnes/program/hacks.cpp +++ b/higan/target-bsnes/program/hacks.cpp @@ -4,10 +4,10 @@ auto Program::hackCompatibility() -> void { bool fastPPUHiresMode7 = emulatorSettings.hiresMode7.checked(); bool fastDSP = emulatorSettings.fastDSPOption.checked(); - auto label = superFamicom.label; - if(label == "AIR STRIKE PATROL" || label == "DESERT FIGHTER") fastPPU = false; - if(label == "KOUSHIEN_2") fastDSP = false; - if(label == "RENDERING RANGER R2") fastDSP = false; + auto title = superFamicom.title; + if(title == "AIR STRIKE PATROL" || title == "DESERT FIGHTER") fastPPU = false; + if(title == "KOUSHIEN_2") fastDSP = false; + if(title == "RENDERING RANGER R2") fastDSP = false; //todo: update to new emulator->configuration API emulator->set("Fast PPU", fastPPU); @@ -17,9 +17,9 @@ auto Program::hackCompatibility() -> void { } auto Program::hackPatchMemory(vector& data) -> void { - auto label = superFamicom.label; + auto title = superFamicom.title; - if(label == "Satellaview BS-X" && data.size() >= 0x100000) { + if(title == "Satellaview BS-X" && data.size() >= 0x100000) { //BS-X: Sore wa Namae o Nusumareta Machi no Monogatari (JPN) (1.1) //disable limited play check for BS Memory flash cartridges //benefit: allow locked out BS Memory flash games to play without manual header patching @@ -37,8 +37,8 @@ auto Program::hackOverclockSuperFX() -> void { double multiplier = emulatorSettings.superFXValue.text().natural() / 100.0; if(multiplier == 1.0) return; - auto label = superFamicom.label; - if(label == "NIDAN MORITASHOGI2") return; //ST018 uses same clock speed as SuperFX + auto title = superFamicom.title; + if(title == "NIDAN MORITASHOGI2") return; //ST018 uses same clock speed as SuperFX auto document = BML::unserialize(superFamicom.manifest); @@ -53,7 +53,7 @@ auto Program::hackOverclockSuperFX() -> void { //MARIO CHIP 1 uses CPU oscillator; force it to use its own crystal to overclock it bool marioChip1 = false; - if(label == "STAR FOX" || label == "STAR WING") marioChip1 = true; + if(title == "STAR FOX" || title == "STAR WING") marioChip1 = true; if(marioChip1) { document("game/board/oscillator/frequency").setValue(uint(21440000 * multiplier)); superFamicom.manifest = BML::serialize(document); diff --git a/higan/target-bsnes/program/patch.cpp b/higan/target-bsnes/program/patch.cpp index 7ebb58f4..c01a772f 100644 --- a/higan/target-bsnes/program/patch.cpp +++ b/higan/target-bsnes/program/patch.cpp @@ -108,14 +108,16 @@ auto Program::applyPatchBPS(vector& input, string location) -> bool { if(!patch) return false; string manifest; - string result; - if(auto output = Beat::Single::apply(input.data(), input.size(), patch.data(), patch.size(), manifest, result)) { - input = move(*output); - return true; + string error; + if(auto output = Beat::Single::apply(input, patch, manifest, error)) { + if(!error) { + input = move(*output); + return true; + } } MessageDialog({ - result, "\n\n", + error, "\n\n", "Please ensure you are using the correct (headerless) ROM for this patch." }).setParent(*presentation).error(); return false; diff --git a/higan/target-bsnes/program/program.hpp b/higan/target-bsnes/program/program.hpp index 48c660f7..98281d54 100644 --- a/higan/target-bsnes/program/program.hpp +++ b/higan/target-bsnes/program/program.hpp @@ -117,7 +117,7 @@ public: }; struct SuperFamicom : Game { - string label; + string title; vector program; vector data; vector expansion; diff --git a/higan/target-bsnes/program/states.cpp b/higan/target-bsnes/program/states.cpp index e1f18fb9..6407eefe 100644 --- a/higan/target-bsnes/program/states.cpp +++ b/higan/target-bsnes/program/states.cpp @@ -62,7 +62,7 @@ auto Program::loadState(string filename) -> bool { if(auto memory = loadStateData(filename)) { if(filename != "Quick/Undo") saveUndoState(); if(filename == "Quick/Undo") saveRedoState(); - auto serializerRLE = Decode::RLE<1>(memory.data() + 3 * sizeof(uint)); + auto serializerRLE = Decode::RLE<1>({memory.data() + 3 * sizeof(uint), memory.size() - 3 * sizeof(uint)}); serializer s{serializerRLE.data(), serializerRLE.size()}; if(!emulator->unserialize(s)) return showMessage({"[", prefix, "] is in incompatible format"}), false; return showMessage({"Loaded [", prefix, "]"}), true; @@ -77,7 +77,7 @@ auto Program::saveState(string filename) -> bool { serializer s = emulator->serialize(); if(!s.size()) return showMessage({"Failed to save [", prefix, "]"}), false; - auto serializerRLE = Encode::RLE<1>(s.data(), s.size()); + auto serializerRLE = Encode::RLE<1>({s.data(), s.size()}); vector previewRLE; //this can be null if a state is captured before the first frame of video output after power/reset @@ -86,7 +86,7 @@ auto Program::saveState(string filename) -> bool { preview.copy(screenshot.data, screenshot.pitch, screenshot.width, screenshot.height); if(preview.width() != 256 || preview.height() != 240) preview.scale(256, 240, true); preview.transform(0, 15, 0x8000, 0x7c00, 0x03e0, 0x001f); - previewRLE = Encode::RLE<2>(preview.data(), preview.size() / sizeof(uint16_t)); + previewRLE = Encode::RLE<2>({preview.data(), preview.size()}); } vector saveState; diff --git a/higan/target-bsnes/tools/state-manager.cpp b/higan/target-bsnes/tools/state-manager.cpp index c3532301..11dbe9dd 100644 --- a/higan/target-bsnes/tools/state-manager.cpp +++ b/higan/target-bsnes/tools/state-manager.cpp @@ -158,7 +158,7 @@ auto StateManager::updateSelection() -> void { uint preview = memory::readl(saveState.data() + 2 * sizeof(uint)); if(signature == Program::State::Signature && preview) { uint offset = 3 * sizeof(uint) + serializer; - auto preview = Decode::RLE<2>(saveState.data() + offset, max(offset, saveState.size()) - offset); + auto preview = Decode::RLE<2>({saveState.data() + offset, max(offset, saveState.size()) - offset}); image icon{0, 15, 0x8000, 0x7c00, 0x03e0, 0x001f}; icon.copy(preview.data(), 256 * sizeof(uint16_t), 256, 240); icon.transform(); diff --git a/higan/target-higan/presentation/presentation.cpp b/higan/target-higan/presentation/presentation.cpp index 0f5488d7..26355f51 100644 --- a/higan/target-higan/presentation/presentation.cpp +++ b/higan/target-higan/presentation/presentation.cpp @@ -138,6 +138,8 @@ Presentation::Presentation() { if(!locations || !directory::exists(locations.first())) return; program->gameQueue.append(locations.first()); program->load(); + }).onSize([&] { + configureViewport(); }); iconLayout.setAlignment(0.0); @@ -183,12 +185,6 @@ Presentation::Presentation() { resizeWindow(); setCentered(); - #if defined(PLATFORM_WINDOWS) - Application::Windows::onModalChange([&](bool modal) { - if(modal && audio) audio->clear(); - }); - #endif - #if defined(PLATFORM_MACOS) about.setVisible(false); Application::Cocoa::onAbout([&] { about.doActivate(); }); @@ -324,10 +320,7 @@ auto Presentation::configureViewport() -> void { } auto Presentation::clearViewport() -> void { - if(!emulator || !emulator->loaded()) { - viewportLayout.setPadding(); - configureViewport(); - } + if(!emulator || !emulator->loaded()) viewportLayout.setPadding(); if(!visible() || !video) return; uint32_t* output; @@ -407,8 +400,6 @@ auto Presentation::resizeViewport() -> void { paddingWidth / 2, paddingHeight / 2, paddingWidth - paddingWidth / 2, paddingHeight - paddingHeight / 2 }); - - configureViewport(); } auto Presentation::resizeWindow() -> void { diff --git a/higan/target-higan/program/program.cpp b/higan/target-higan/program/program.cpp index f27b3025..4582f6cd 100644 --- a/higan/target-higan/program/program.cpp +++ b/higan/target-higan/program/program.cpp @@ -89,12 +89,11 @@ Program::Program(vector arguments) { updateAudioDriver(); updateAudioEffects(); - arguments.takeLeft(); //ignore program location in argument parsing + arguments.takeFirst(); //ignore program location in argument parsing for(auto& argument : arguments) { if(argument == "--fullscreen") { presentation->toggleFullScreen(); } else if(directory::exists(argument.split("|", 1L).right())) { - if(!argument.transform("\\", "/").endsWith("/")) argument.append("/"); gameQueue.append(argument); } else if(file::exists(argument)) { if(auto result = execute("icarus", "--import", argument)) { @@ -109,6 +108,7 @@ Program::Program(vector arguments) { auto Program::main() -> void { updateStatusText(); + video->poll(); inputManager->poll(); inputManager->pollHotkeys(); diff --git a/higan/target-higan/program/utility.cpp b/higan/target-higan/program/utility.cpp index 21cac21f..618f3ce6 100644 --- a/higan/target-higan/program/utility.cpp +++ b/higan/target-higan/program/utility.cpp @@ -14,6 +14,10 @@ auto Program::initializeVideoDriver() -> void { video->create("None"); } + video->onUpdate([&](uint width, uint height) { + if(!emulator || !emulator->loaded()) presentation->clearViewport(); + }); + presentation->configureViewport(); presentation->clearViewport(); } diff --git a/higan/ws/cartridge/cartridge.cpp b/higan/ws/cartridge/cartridge.cpp index 2300409b..6714c808 100644 --- a/higan/ws/cartridge/cartridge.cpp +++ b/higan/ws/cartridge/cartridge.cpp @@ -107,7 +107,7 @@ auto Cartridge::load() -> bool { information.title = document["game/label"].text(); information.orientation = document["game/orientation"].text() == "vertical"; - information.sha256 = Hash::SHA256(rom.data, rom.size).digest(); + information.sha256 = Hash::SHA256({rom.data, rom.size}).digest(); return true; } diff --git a/hiro/cocoa/widget/table-view-column.cpp b/hiro/cocoa/widget/table-view-column.cpp index 61e12067..6d159aeb 100644 --- a/hiro/cocoa/widget/table-view-column.cpp +++ b/hiro/cocoa/widget/table-view-column.cpp @@ -76,7 +76,7 @@ auto pTableViewColumn::setWidth(signed width) -> void { } auto pTableViewColumn::_parent() -> maybe { - if(auto parent = self().parentTableViewHeader()) { + if(auto parent = self().parentTableView()) { if(auto self = parent->self()) return *self; } return {}; diff --git a/hiro/cocoa/widget/table-view.cpp b/hiro/cocoa/widget/table-view.cpp index b43a437c..6a390b38 100644 --- a/hiro/cocoa/widget/table-view.cpp +++ b/hiro/cocoa/widget/table-view.cpp @@ -60,22 +60,20 @@ [content removeTableColumn:[[content tableColumns] lastObject]]; } - if(auto tableViewHeader = tableView->state.header) { - for(auto& tableViewColumn : tableViewHeader->state.columns) { - auto column = tableViewColumn->offset(); + for(auto& tableViewColumn : tableView->state.columns) { + auto column = tableViewColumn->offset(); - NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]]; - NSTableHeaderCell* headerCell = [[NSTableHeaderCell alloc] initTextCell:[NSString stringWithUTF8String:tableViewColumn->state.text]]; - CocoaTableViewCell* dataCell = [[CocoaTableViewCell alloc] initWith:*tableView]; + NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]]; + NSTableHeaderCell* headerCell = [[NSTableHeaderCell alloc] initTextCell:[NSString stringWithUTF8String:tableViewColumn->state.text]]; + CocoaTableViewCell* dataCell = [[CocoaTableViewCell alloc] initWith:*tableView]; - [dataCell setEditable:NO]; + [dataCell setEditable:NO]; - [tableColumn setResizingMask:NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask]; - [tableColumn setHeaderCell:headerCell]; - [tableColumn setDataCell:dataCell]; + [tableColumn setResizingMask:NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask]; + [tableColumn setHeaderCell:headerCell]; + [tableColumn setDataCell:dataCell]; - [content addTableColumn:tableColumn]; - } + [content addTableColumn:tableColumn]; } } @@ -369,13 +367,17 @@ auto pTableView::setHeadered(bool headered) -> void { @autoreleasepool { if(headered == state().headered) return; if(headered) { - [[pTableView->cocoaView content] setHeaderView:[[[NSTableHeaderView alloc] init] autorelease]]; + [[cocoaView content] setHeaderView:[[[NSTableHeaderView alloc] init] autorelease]]; } else { - [[pTableView->cocoaView content] setHeaderView:nil]; + [[cocoaView content] setHeaderView:nil]; } } } +auto pTableView::setSortable(bool sortable) -> void { + //TODO +} + auto pTableView::_cellWidth(uint row, uint column) -> uint { uint width = 8; if(auto pTableViewItem = self().item(row)) { @@ -394,9 +396,9 @@ auto pTableView::_cellWidth(uint row, uint column) -> uint { return width; } -auto pTableView::_columnWidth(uint column) -> uint { +auto pTableView::_columnWidth(uint column_) -> uint { uint width = 8; - if(auto column = self().column(column)) { + if(auto column = self().column(column_)) { if(auto& icon = column->state.icon) { width += icon.width() + 2; } diff --git a/hiro/core/shared.hpp b/hiro/core/shared.hpp index c7e019ea..c904a7e5 100644 --- a/hiro/core/shared.hpp +++ b/hiro/core/shared.hpp @@ -20,10 +20,10 @@ if(parent) (*parent)->append(*this, std::forward

(p)...); \ } \ template auto is() -> bool { \ - return dynamic_cast(data()); \ + return dynamic_cast(s##Name::data()); \ } \ template auto cast() -> T { \ - if(auto pointer = dynamic_cast(data())) { \ + if(auto pointer = dynamic_cast(s##Name::data())) { \ if(auto shared = pointer->instance.acquire()) return T(shared); \ } \ return T(); \ diff --git a/hiro/extension/browser-dialog.cpp b/hiro/extension/browser-dialog.cpp index 7268d08c..0e4ba984 100644 --- a/hiro/extension/browser-dialog.cpp +++ b/hiro/extension/browser-dialog.cpp @@ -193,6 +193,8 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response { window.setDismissable(); window.setVisible(); view.setFocused(); + Application::processEvents(); + view->resizeColumns(); window.setModal(); window.setVisible(false); diff --git a/hiro/gtk/window.cpp b/hiro/gtk/window.cpp index 5e718625..5b0348b0 100644 --- a/hiro/gtk/window.cpp +++ b/hiro/gtk/window.cpp @@ -415,6 +415,7 @@ auto pWindow::setTitle(const string& title) -> void { auto pWindow::setVisible(bool visible) -> void { gtk_widget_set_visible(widget, visible); + _synchronizeGeometry(); _synchronizeMargin(); } diff --git a/icarus/heuristics/famicom.cpp b/icarus/heuristics/famicom.cpp index 66746231..903605bb 100644 --- a/icarus/heuristics/famicom.cpp +++ b/icarus/heuristics/famicom.cpp @@ -34,7 +34,7 @@ auto Famicom::manifest() const -> string { string output; output.append("game\n"); - output.append(" sha256: ", Hash::SHA256(&data[16], data.size() - 16).digest(), "\n"); + output.append(" sha256: ", Hash::SHA256({&data[16], data.size() - 16}).digest(), "\n"); output.append(" label: ", Location::prefix(location), "\n"); output.append(" name: ", Location::prefix(location), "\n"); diff --git a/icarus/heuristics/mega-drive.cpp b/icarus/heuristics/mega-drive.cpp index 7e5d33b5..24d4d9e8 100644 --- a/icarus/heuristics/mega-drive.cpp +++ b/icarus/heuristics/mega-drive.cpp @@ -71,11 +71,15 @@ auto MegaDrive::manifest() const -> string { string domesticName; domesticName.resize(48); memory::copy(domesticName.get(), &data[0x0120], domesticName.size()); + for(auto& c : domesticName) if(c < 0x20 || c > 0x7e) c = ' '; + while(domesticName.find(" ")) domesticName.replace(" ", " "); domesticName.strip(); string internationalName; internationalName.resize(48); memory::copy(internationalName.get(), &data[0x0150], internationalName.size()); + for(auto& c : internationalName) if(c < 0x20 || c > 0x7e) c = ' '; + while(internationalName.find(" ")) internationalName.replace(" ", " "); internationalName.strip(); string output; @@ -83,6 +87,7 @@ auto MegaDrive::manifest() const -> string { output.append(" sha256: ", Hash::SHA256(data).digest(), "\n"); output.append(" label: ", Location::prefix(location), "\n"); output.append(" name: ", Location::prefix(location), "\n"); + output.append(" title: ", domesticName, "\n"); output.append(" region: ", regions.left(), "\n"); output.append(" board\n"); if(domesticName == "Game Genie") { diff --git a/icarus/heuristics/super-famicom.cpp b/icarus/heuristics/super-famicom.cpp index 7de976ae..be21c911 100644 --- a/icarus/heuristics/super-famicom.cpp +++ b/icarus/heuristics/super-famicom.cpp @@ -8,7 +8,7 @@ struct SuperFamicom { auto region() const -> string; auto revision() const -> string; auto board() const -> string; - auto label() const -> string; + auto title() const -> string; auto serial() const -> string; auto romSize() const -> uint; auto programRomSize() const -> uint; @@ -67,6 +67,7 @@ auto SuperFamicom::manifest() const -> string { output.append(" sha256: ", Hash::SHA256(data).digest(), "\n"); output.append(" label: ", Location::prefix(location), "\n"); output.append(" name: ", Location::prefix(location), "\n"); + output.append(" title: ", title(), "\n"); output.append(" region: ", region(), "\n"); output.append(" revision: ", revision(), "\n"); output.append(" board: ", board(), "\n"); @@ -292,7 +293,7 @@ auto SuperFamicom::board() const -> string { return board; } -auto SuperFamicom::label() const -> string { +auto SuperFamicom::title() const -> string { string label; for(uint n = 0; n < 0x15; n++) { @@ -527,15 +528,15 @@ auto SuperFamicom::firmwareARM() const -> string { } auto SuperFamicom::firmwareEXNEC() const -> string { - if(label() == "EXHAUST HEAT2") return "ST010"; - if(label() == "F1 ROC II") return "ST010"; - if(label() == "2DAN MORITA SHOUGI") return "ST011"; + if(title() == "EXHAUST HEAT2") return "ST010"; + if(title() == "F1 ROC II") return "ST010"; + if(title() == "2DAN MORITA SHOUGI") return "ST011"; return "ST010"; } auto SuperFamicom::firmwareGB() const -> string { - if(label() == "Super GAMEBOY") return "SGB1"; - if(label() == "Super GAMEBOY2") return "SGB2"; + if(title() == "Super GAMEBOY") return "SGB1"; + if(title() == "Super GAMEBOY2") return "SGB2"; return "SGB1"; } @@ -544,11 +545,11 @@ auto SuperFamicom::firmwareHITACHI() const -> string { } auto SuperFamicom::firmwareNEC() const -> string { - if(label() == "PILOTWINGS") return "DSP1"; - if(label() == "DUNGEON MASTER") return "DSP2"; - if(label() == "SDガンダムGX") return "DSP3"; - if(label() == "PLANETS CHAMP TG3000") return "DSP4"; - if(label() == "TOP GEAR 3000") return "DSP4"; + if(title() == "PILOTWINGS") return "DSP1"; + if(title() == "DUNGEON MASTER") return "DSP2"; + if(title() == "SDガンダムGX") return "DSP3"; + if(title() == "PLANETS CHAMP TG3000") return "DSP4"; + if(title() == "TOP GEAR 3000") return "DSP4"; return "DSP1B"; } diff --git a/nall/array-view.hpp b/nall/array-view.hpp new file mode 100644 index 00000000..eb95600a --- /dev/null +++ b/nall/array-view.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include + +namespace nall { + +struct string; +template struct vector; +template struct array; + +template struct array_view { + using type = array_view; + + inline array_view() { + _data = nullptr; + _size = 0; + } + + inline array_view(const void* data, uint64_t size) { + _data = (const T*)data; + _size = (uint)size; + } + + inline explicit operator bool() const { return _data && _size > 0; } + inline operator const T*() const { return _data; } + + inline auto operator++() -> type& { _data++; _size--; return *this; } + inline auto operator--() -> type& { _data--; _size++; return *this; } + + inline auto operator++(int) -> type { auto copy = *this; ++(*this); return copy; } + inline auto operator--(int) -> type { auto copy = *this; --(*this); return copy; } + + inline auto operator[](uint index) const -> const T& { + #ifdef DEBUG + struct out_of_bounds {}; + if(index >= _size) throw out_of_bounds{}; + #endif + return _data[index]; + } + + inline auto operator()(uint index, const T& fallback = {}) const -> T { + if(index >= _size) return fallback; + return _data[index]; + } + + template inline auto data() const -> const U* { return (const U*)_data; } + template inline auto size() const -> uint64_t { return _size * sizeof(T) / sizeof(U); } + + inline auto begin() const -> iterator_const { return {_data, (uint)0}; } + inline auto end() const -> iterator_const { return {_data, (uint)_size}; } + + inline auto rbegin() const -> reverse_iterator_const { return {_data, (uint)_size - 1}; } + inline auto rend() const -> reverse_iterator_const { return {_data, (uint)-1}; } + +protected: + const T* _data; + int _size; +}; + +} diff --git a/nall/array.hpp b/nall/array.hpp index 48e3ecc4..598e9b46 100644 --- a/nall/array.hpp +++ b/nall/array.hpp @@ -1,6 +1,8 @@ #pragma once +#include #include +#include namespace nall { @@ -17,6 +19,10 @@ template struct array { } } + operator array_view() const { + return {data(), size()}; + } + alwaysinline auto operator[](uint index) -> T& { #ifdef DEBUG struct out_of_bounds {}; diff --git a/nall/beat/single/apply.hpp b/nall/beat/single/apply.hpp index e3146971..57002298 100644 --- a/nall/beat/single/apply.hpp +++ b/nall/beat/single/apply.hpp @@ -2,22 +2,19 @@ namespace nall { namespace Beat { namespace Single { -inline auto apply( - const uint8_t* sourceData, uint64_t sourceSize, - const uint8_t* beatData, uint64_t beatSize, - maybe manifest = {}, maybe result = {} -) -> maybe> { +inline auto apply(array_view source, array_view beat, maybe manifest = {}, maybe result = {}) -> maybe> { #define error(text) { if(result) *result = {"error: ", text}; return {}; } - #define warning(text) { if(result) *result = {"warning: ", text}; return targetData; } - #define success() { if(result) *result = ""; return targetData; } - if(beatSize < 19) error("beat size mismatch"); + #define warning(text) { if(result) *result = {"warning: ", text}; return target; } + #define success() { if(result) *result = ""; return target; } + if(beat.size() < 19) error("beat size mismatch"); - vector targetData; + vector target; uint beatOffset = 0; auto read = [&]() -> uint8_t { - return beatData[beatOffset++]; + return beat[beatOffset++]; }; + auto decode = [&]() -> uint64_t { uint64_t data = 0, shift = 1; while(true) { @@ -30,19 +27,18 @@ inline auto apply( return data; }; - auto targetOffset = 0; auto write = [&](uint8_t data) { - targetData.append(data); + target.append(data); }; if(read() != 'B') error("beat header invalid"); if(read() != 'P') error("beat header invalid"); if(read() != 'S') error("beat header invalid"); if(read() != '1') error("beat version mismatch"); - if(decode() != sourceSize) error("source size mismatch"); + if(decode() != source.size()) error("source size mismatch"); uint targetSize = decode(); - targetData.reserve(targetSize); - auto metadataSize = decode(); + target.reserve(targetSize); + uint metadataSize = decode(); for(uint n : range(metadataSize)) { auto data = read(); if(manifest) manifest->append((char)data); @@ -51,13 +47,13 @@ inline auto apply( enum : uint { SourceRead, TargetRead, SourceCopy, TargetCopy }; uint sourceRelativeOffset = 0, targetRelativeOffset = 0; - while(beatOffset < beatSize - 12) { + while(beatOffset < beat.size() - 12) { uint length = decode(); uint mode = length & 3; length = (length >> 2) + 1; if(mode == SourceRead) { - while(length--) write(sourceData[targetOffset]); + while(length--) write(source[target.size()]); } else if(mode == TargetRead) { while(length--) write(read()); } else { @@ -65,10 +61,10 @@ inline auto apply( offset = offset & 1 ? -(offset >> 1) : (offset >> 1); if(mode == SourceCopy) { sourceRelativeOffset += offset; - while(length--) write(sourceData[sourceRelativeOffset++]); + while(length--) write(source[sourceRelativeOffset++]); } else { targetRelativeOffset += offset; - while(length--) write(targetData[targetRelativeOffset++]); + while(length--) write(target[targetRelativeOffset++]); } } } @@ -76,12 +72,12 @@ inline auto apply( uint32_t sourceHash = 0, targetHash = 0, beatHash = 0; for(uint shift : range(0, 32, 8)) sourceHash |= read() << shift; for(uint shift : range(0, 32, 8)) targetHash |= read() << shift; - for(uint shift : range(0, 32, 8)) beatHash |= read() << shift; + for(uint shift : range(0, 32, 8)) beatHash |= read() << shift; - if(targetOffset != targetSize) warning("target size mismatch"); - if(sourceHash != Hash::CRC32(sourceData, sourceSize).value()) warning("source hash mismatch"); - if(targetHash != Hash::CRC32(targetData).value()) warning("target hash mismatch"); - if(beatHash != Hash::CRC32(beatData, beatSize - 4).value()) warning("beat hash mismatch"); + if(target.size() != targetSize) warning("target size mismatch"); + if(sourceHash != Hash::CRC32(source).value()) warning("source hash mismatch"); + if(targetHash != Hash::CRC32(target).value()) warning("target hash mismatch"); + if(beatHash != Hash::CRC32({beat.data(), beat.size() - 4}).value()) warning("beat hash mismatch"); success(); #undef error diff --git a/nall/beat/single/create.hpp b/nall/beat/single/create.hpp index 224b5388..4999afad 100644 --- a/nall/beat/single/create.hpp +++ b/nall/beat/single/create.hpp @@ -1,18 +1,14 @@ #pragma once -#include +#include namespace nall { namespace Beat { namespace Single { -inline auto create( - const uint8_t* sourceData, uint64_t sourceSize, - const uint8_t* targetData, uint64_t targetSize, - const string& manifest = {} -) -> maybe> { - vector beatData; +inline auto create(array_view source, array_view target, string_view manifest = {}) -> vector { + vector beat; auto write = [&](uint8_t data) { - beatData.append(data); + beat.append(data); }; auto encode = [&](uint64_t data) { @@ -26,18 +22,11 @@ inline auto create( }; write('B'), write('P'), write('S'), write('1'); - encode(sourceSize), encode(targetSize), encode(manifest.size()); + encode(source.size()), encode(target.size()), encode(manifest.size()); for(auto& byte : manifest) write(byte); - auto read = [&](const uint8_t* data, uint size, uint offset) -> uint { - if(offset + 3 >= size) return 0; - return data[offset + 0] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3] << 0; - }; - - Encode::Dictionary sourceDictionary(sourceData, sourceSize); - Encode::Dictionary targetDictionary(targetData, targetSize); - sourceDictionary.scan(); - targetDictionary.scan(); + auto sourceArray = SuffixArray(source).lrcp(); + auto targetArray = SuffixArray(target).lpf(); enum : uint { SourceRead, TargetRead, SourceCopy, TargetCopy }; uint outputOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0; @@ -47,46 +36,30 @@ inline auto create( if(!targetReadLength) return; encode(TargetRead | ((targetReadLength - 1) << 2)); uint offset = outputOffset - targetReadLength; - while(targetReadLength) write(targetData[offset++]), targetReadLength--; + while(targetReadLength) write(target[offset++]), targetReadLength--; }; - uint longestSize = max(sourceSize, targetSize); - while(outputOffset < targetSize) { + uint longestSize = max(source.size(), target.size()); + while(outputOffset < target.size()) { uint mode = TargetRead, longestLength = 3, longestOffset = 0; - uint prefix = read(targetData, targetSize, outputOffset), lower, upper; + int length = 0, offset = outputOffset; - uint length = 0, offset = outputOffset; - while(offset < longestOffset) { - if(sourceData[offset] != targetData[offset]) break; + while(offset < longestSize) { + if(source[offset] != target[offset]) break; length++, offset++; } if(length > longestLength) { mode = SourceRead, longestLength = length; } - sourceDictionary.find(prefix, lower, upper); - for(uint index = lower; index <= upper; index++) { - uint length = 0, sourceOffset = sourceDictionary[index], targetOffset = outputOffset; - while(sourceOffset < sourceSize && targetOffset < targetSize) { - if(sourceData[sourceOffset++] != targetData[targetOffset++]) break; - length++; - } - if(length > longestLength) { - mode = SourceCopy, longestLength = length, longestOffset = sourceDictionary[index]; - } + sourceArray.find(length, offset, {target.data() + outputOffset, target.size() - outputOffset}); + if(length > longestLength) { + mode = SourceCopy, longestLength = length, longestOffset = offset; } - targetDictionary.find(prefix, lower, upper); - for(uint index = lower; index <= upper; index++) { - uint length = 0, sourceOffset = targetDictionary[index], targetOffset = outputOffset; - if(sourceOffset >= outputOffset) continue; - while(targetOffset < targetSize) { - if(targetData[sourceOffset++] != targetData[targetOffset++]) break; - length++; - } - if(length > longestLength) { - mode = TargetCopy, longestLength = length, longestOffset = targetDictionary[index]; - } + targetArray.previous(length, offset, outputOffset); + if(length > longestLength) { + mode = TargetCopy, longestLength = length, longestOffset = offset; } if(mode == TargetRead) { @@ -110,14 +83,14 @@ inline auto create( } flush(); - auto sourceHash = Hash::CRC32(sourceData, sourceSize); + auto sourceHash = Hash::CRC32(source); for(uint shift : range(0, 32, 8)) write(sourceHash.value() >> shift); - auto targetHash = Hash::CRC32(targetData, targetSize); + auto targetHash = Hash::CRC32(target); for(uint shift : range(0, 32, 8)) write(targetHash.value() >> shift); - auto beatHash = Hash::CRC32(beatData); + auto beatHash = Hash::CRC32(beat); for(uint shift : range(0, 32, 8)) write(beatHash.value() >> shift); - return beatData; + return beat; } }}} diff --git a/nall/decode/bwt.hpp b/nall/decode/bwt.hpp index 76af8daa..3c7fc5f9 100644 --- a/nall/decode/bwt.hpp +++ b/nall/decode/bwt.hpp @@ -6,8 +6,7 @@ namespace nall { namespace Decode { -inline auto BWT(const void* data) -> vector { - auto input = (const uint8_t*)data; +inline auto BWT(array_view input) -> vector { vector output; uint size = 0; @@ -17,14 +16,11 @@ inline auto BWT(const void* data) -> vector { uint I = 0; for(uint byte : range(8)) I |= *input++ << byte * 8; - auto suffixes = new int[size]; - suffix_array(suffixes, input, size); + auto suffixes = SuffixArray(input); auto L = input; auto F = new uint8_t[size]; - for(uint byte : range(size)) F[byte] = L[suffixes[byte]]; - - delete[] suffixes; + for(uint offset : range(size)) F[offset] = L[suffixes[offset + 1]]; uint64_t K[256] = {}; auto C = new int[size]; @@ -48,9 +44,4 @@ inline auto BWT(const void* data) -> vector { return output; } -template -inline auto BWT(const vector& buffer) -> vector { - return move(BWT(buffer.data())); -} - }} diff --git a/nall/decode/huffman.hpp b/nall/decode/huffman.hpp index be89b9c6..7a19dc56 100644 --- a/nall/decode/huffman.hpp +++ b/nall/decode/huffman.hpp @@ -2,8 +2,7 @@ namespace nall { namespace Decode { -inline auto Huffman(const void* data) -> vector { - auto input = (const uint8_t*)data; +inline auto Huffman(array_view input) -> vector { vector output; uint size = 0; @@ -34,9 +33,4 @@ inline auto Huffman(const void* data) -> vector { return output; } -template -inline auto Huffman(const vector& buffer) -> vector { - return move(Huffman(buffer.data())); -} - }} diff --git a/nall/decode/lzsa.hpp b/nall/decode/lzsa.hpp index cb29801c..77c72b67 100644 --- a/nall/decode/lzsa.hpp +++ b/nall/decode/lzsa.hpp @@ -25,7 +25,6 @@ inline auto LZSA(const void* data) -> vector { auto flags = Decode::Huffman(load()); auto literals = Decode::Huffman(load()); -//auto literals = Decode::BWT(Decode::Huffman(load())); auto lengths = Decode::Huffman(load()); auto offsets = Decode::Huffman(load()); diff --git a/nall/decode/lzss.hpp b/nall/decode/lzss.hpp deleted file mode 100644 index 2345c366..00000000 --- a/nall/decode/lzss.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -namespace nall { namespace Decode { - -inline auto LZSS(const void* data) -> vector { - vector output; - - auto input = (const uint8_t*)data; - uint64_t size = 0; - for(uint byte : range(8)) size |= *input++ << byte * 8; - output.resize(size); - const uint windowBits = *input++; - const uint lengthBits = *input++; - - const uint lengthExtends = 4 + (1 << lengthBits) - 1; - const uint windowMask = (1 << windowBits) - 1; - - for(uint offset = 0, flags = 0, bit = 7; offset < size;) { - if(++bit == 8) bit = 0, flags = *input++; - - if(flags & 1 << bit) { - uint encoding = 0; - encoding |= *input++ << 0; - encoding |= *input++ << 8; - encoding |= *input++ << 16; - - uint length = 4 + (encoding >> windowBits); - uint window = 1 + (encoding & windowMask); - if(length == lengthExtends) length += *input++; - - for(uint index : range(length)) { - if(offset + index >= size) break; - output[offset + index] = output[offset + index - window]; - } - offset += length; - } else { - output[offset++] = *input++; - } - } - - return output; -} - -}} diff --git a/nall/decode/mtf.hpp b/nall/decode/mtf.hpp index 96643aab..d105b93e 100644 --- a/nall/decode/mtf.hpp +++ b/nall/decode/mtf.hpp @@ -4,25 +4,19 @@ namespace nall { namespace Decode { -inline auto MTF(const void* data, uint size) -> vector { - auto input = (const uint8_t*)data; +inline auto MTF(array_view input) -> vector { vector output; - output.resize(size); + output.resize(input.size()); uint8_t order[256]; for(uint n : range(256)) order[n] = n; - for(uint offset = 0; offset < size; offset++) { - auto data = input[offset]; - for(uint index = 0; index < 256; index++) { - uint value = order[data]; - if(value == index) { - output[offset] = value; - memory::move(&order[1], &order[0], index); - order[0] = index; - break; - } - } + for(uint offset : range(input.size())) { + uint data = input[offset]; + uint value = order[data]; + output[offset] = value; + memory::move(&order[1], &order[0], data); + order[0] = value; } return output; diff --git a/nall/decode/rle.hpp b/nall/decode/rle.hpp index e19f0787..f7f83abb 100644 --- a/nall/decode/rle.hpp +++ b/nall/decode/rle.hpp @@ -3,14 +3,11 @@ namespace nall { namespace Decode { template //S = word size; M = match length -inline auto RLE(const void* data, uint remaining = ~0) -> vector { +inline auto RLE(array_view input) -> vector { vector output; - auto input = (const uint8_t*)data; - auto load = [&]() -> uint8_t { - if(!remaining) return 0x00; - return --remaining, *input++; + return input ? *input++ : 0; }; uint base = 0; diff --git a/nall/decode/url.hpp b/nall/decode/url.hpp index 1b8a217d..7f059386 100644 --- a/nall/decode/url.hpp +++ b/nall/decode/url.hpp @@ -3,7 +3,7 @@ namespace nall { namespace Decode { //returns empty string on malformed content -inline auto URL(const string& input) -> string { +inline auto URL(string_view input) -> string { string output; for(uint n = 0; n < input.size();) { char c = input[n]; diff --git a/nall/div-suf-sort.hpp b/nall/div-suf-sort.hpp deleted file mode 100644 index e715a825..00000000 --- a/nall/div-suf-sort.hpp +++ /dev/null @@ -1,1440 +0,0 @@ -#pragma once - -//divsufsort -//author: Yuta Mori -//license: MIT - -//class: suffix sort (variant) -//average: O(n) -//worst: O(n log n) -//memory: O(5n) -//stable?: yes (all values unique) - -//omitted functionality: -//* OpenMP support -//* assertions -//* non 8-bit values -//* non 32-bit indexes - -#define ALPHABET_SIZE (256) -#define BUCKET_A(_c0) bucket_A[(_c0)] -#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) -#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) -#define BUCKET_A_SIZE (ALPHABET_SIZE) -#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) -#define SS_INSERTIONSORT_THRESHOLD (8) -#define SS_BLOCKSIZE (1024) //0..32767 -#if SS_BLOCKSIZE == 0 - #define SS_MISORT_STACKSIZE (64) -#elif SS_BLOCKSIZE <= 4096 - #define SS_MISORT_STACKSIZE (16) -#else - #define SS_MISORT_STACKSIZE (24) -#endif -#define SS_SMERGE_STACKSIZE (32) -#define TR_INSERTIONSORT_THRESHOLD (8) -#define TR_STACKSIZE (64) -#define STACK_PUSH(_a, _b, _c, _d) \ - do { \ - stack[ssize].a = (_a), stack[ssize].b = (_b), \ - stack[ssize].c = (_c), stack[ssize++].d = (_d); \ - } while(0) -#define STACK_PUSH5(_a, _b, _c, _d, _e) \ - do { \ - stack[ssize].a = (_a), stack[ssize].b = (_b), \ - stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e); \ - } while(0) -#define STACK_POP(_a, _b, _c, _d) \ - do { \ - if(ssize == 0) return; \ - (_a) = stack[--ssize].a, (_b) = stack[ssize].b, \ - (_c) = stack[ssize].c, (_d) = stack[ssize].d; \ - } while(0) -#define STACK_POP5(_a, _b, _c, _d, _e) \ - do { \ - if(ssize == 0) return; \ - (_a) = stack[--ssize].a, (_b) = stack[ssize].b, \ - (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e; \ - } while(0) - -namespace nall { namespace libdivsufsort { - -inline auto ilg16(int n) -> int { - static int lg_table[256] = {-1}; - if(!lg_table[255]) for(uint n : range(1, 256)) lg_table[n] = floor(log2(n)); - return (n & 0xff00) ? - 8 + lg_table[(n >> 8) & 0xff]: - 0 + lg_table[(n >> 0) & 0xff]; -} - -inline auto ilg32(int n) -> int { - static int lg_table[256] = {-1}; - if(!lg_table[255]) for(uint n : range(1, 256)) lg_table[n] = floor(log2(n)); - return (n & 0xffff0000) ? - ((n & 0xff000000) ? - 24 + lg_table[(n >> 24) & 0xff] : - 16 + lg_table[(n >> 16) & 0xff]): - ((n & 0x0000ff00) ? - 8 + lg_table[(n >> 8) & 0xff] : - 0 + lg_table[(n >> 0) & 0xff]); -} - -// [[sssort.c]] - -#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) - -inline auto ss_ilg(int n) -> int { - #if SS_BLOCKSIZE == 0 - return ilg32(n); - #elif SS_BLOCKSIZE < 256 - static int lg_table[256] = {-1}; - if(!lg_table[255]) for(uint n : range(1, 256)) lg_table[n] = floor(log2(n)); - return lg_table[n]; - #else - return ilg16(n); - #endif -} - -#endif - -#if SS_BLOCKSIZE != 0 - -inline auto ss_isqrt(int x) -> int { - static int sqq_table[256] = {}; - if(!sqq_table[255]) for(uint n : range(256)) sqq_table[n] = floor(sqrt(n) * 16.0); - - int y, e; - - if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } - e = ilg32(x); - - if(e >= 16) { - y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); - if(e >= 24) { y = (y + 1 + x / y) >> 1; } - y = (y + 1 + x / y) >> 1; - } else if(e >= 8) { - y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; - } else { - return sqq_table[x] >> 4; - } - - return (x < (y * y)) ? y - 1 : y; -} - -#endif - -//compare two suffixes -inline auto ss_compare(const uint8_t* T, const int* p1, const int* p2, int depth) -> int { - const uint8_t *U1, *U2, *U1n, *U2n; - for(U1 = T + depth + *p1, U2 = T + depth + *p2, U1n = T + *(p1 + 1) + 2, U2n = T + *(p2 + 1) + 2; - (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); - ++U1, ++U2 - ); - return U1 < U1n ? (U2 < U2n ? *U1 - *U2 : 1) : (U2 < U2n ? -1 : 0); -} - -#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) - -inline auto ss_insertionsort(const uint8_t* T, const int* PA, int* first, int* last, int depth) -> void { - int *i, *j, t, r; - - for(i = last - 2; first <= i; --i) { - for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { - do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); - if(last <= j) break; - } - if(r == 0) *j = ~*j; - *(j - 1) = t; - } -} - -#endif - -#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) - -inline auto ss_fixdown(const uint8_t* Td, const int* PA, int* SA, int i, int size) -> void { - int j, k, v, c, d, e; - - for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { - d = Td[PA[SA[k = j++]]]; - if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } - if(d <= c) break; - } - SA[i] = v; -} - -//simple top-down heapsort -inline auto ss_heapsort(const uint8_t* Td, const int* PA, int* SA, int size) -> void { - int i, m, t; - - m = size; - if((size % 2) == 0) { - m--; - if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) swap(SA[m], SA[m / 2]); - } - - for(i = m / 2 - 1; 0 <= i; --i) ss_fixdown(Td, PA, SA, i, m); - if((size % 2) == 0) { swap(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } - for(i = m - 1; 0 < i; --i) { - t = SA[0], SA[0] = SA[i]; - ss_fixdown(Td, PA, SA, 0, i); - SA[i] = t; - } -} - -//returns the median of three elements -inline auto ss_median3(const uint8_t* Td, const int* PA, int* v1, int* v2, int* v3) -> int* { - if(Td[PA[*v1]] > Td[PA[*v2]]) swap(v1, v2); - if(Td[PA[*v2]] > Td[PA[*v3]]) return Td[PA[*v1]] > Td[PA[*v3]] ? v1 : v3; - return v2; -} - -//returns the median of five elements -inline auto ss_median5(const uint8_t* Td, const int* PA, int* v1, int* v2, int* v3, int* v4, int* v5) -> int* { - if(Td[PA[*v2]] > Td[PA[*v3]]) swap(v2, v3); - if(Td[PA[*v4]] > Td[PA[*v5]]) swap(v4, v5); - if(Td[PA[*v2]] > Td[PA[*v4]]) swap(v2, v4), swap(v3, v5); - if(Td[PA[*v1]] > Td[PA[*v3]]) swap(v1, v3); - if(Td[PA[*v1]] > Td[PA[*v4]]) swap(v1, v4), swap(v3, v5); - if(Td[PA[*v3]] > Td[PA[*v4]]) return v4; - return v3; -} - -//returns the pivot element -inline auto ss_pivot(const uint8_t* Td, const int* PA, int* first, int* last) -> int* { - int *middle, t; - - t = last - first; - middle = first + t / 2; - - if(t <= 512) { - if(t <= 32) { - return ss_median3(Td, PA, first, middle, last - 1); - } else { - t >>= 2; - return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); - } - } - t >>= 3; - first = ss_median3(Td, PA, first, first + t, first + (t << 1)); - middle = ss_median3(Td, PA, middle - t, middle, middle + t); - last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); - return ss_median3(Td, PA, first, middle, last); -} - -//binary partition for substrings -inline auto ss_partition(const int* PA, int* first, int* last, int depth) -> int* { - int *a, *b, t; - for(a = first - 1, b = last;;) { - for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) *a = ~*a; - for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));); - if(b <= a) break; - t = ~*b; - *b = *a; - *a = t; - } - if(first < a) *first = ~*first; - return a; -} - -//multikey introsort for medium size groups -inline auto ss_mintrosort(const uint8_t* T, const int* PA, int* first, int* last, int depth) -> void { - #define STACK_SIZE SS_MISORT_STACKSIZE - struct { int *a, *b, c, d; } stack[STACK_SIZE]; - const uint8_t* Td; - int *a, *b, *c, *d, *e, *f; - int s, t, ssize, limit, v, x = 0; - - for(ssize = 0, limit = ss_ilg(last - first);;) { - if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { - #if 1 < SS_INSERTIONSORT_THRESHOLD - if(1 < (last - first)) ss_insertionsort(T, PA, first, last, depth); - #endif - STACK_POP(first, last, depth, limit); - continue; - } - - Td = T + depth; - if(limit-- == 0) ss_heapsort(Td, PA, first, last - first); - if(limit < 0) { - for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { - if((x = Td[PA[*a]]) != v) { - if(1 < (a - first)) break; - v = x; - first = a; - } - } - if(Td[PA[*first] - 1] < v) { - first = ss_partition(PA, first, a, depth); - } - if((a - first) <= (last - a)) { - if(1 < (a - first)) { - STACK_PUSH(a, last, depth, -1); - last = a, depth += 1, limit = ss_ilg(a - first); - } else { - first = a, limit = -1; - } - } else { - if(1 < (last - a)) { - STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); - first = a, limit = -1; - } else { - last = a, depth += 1, limit = ss_ilg(a - first); - } - } - continue; - } - - //choose pivot - a = ss_pivot(Td, PA, first, last); - v = Td[PA[*a]]; - swap(*first, *a); - - //partition - for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);); - if(((a = b) < last) && (x < v)) { - for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { - if(x == v) { swap(*b, *a); ++a; } - } - } - for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);); - if((b < (d = c)) && (x > v)) { - for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { - if(x == v) { swap(*c, *d); --d; } - } - } - for(; b < c;) { - swap(*b, *c); - for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { - if(x == v) { swap(*b, *a); ++a; } - } - for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { - if(x == v) { swap(*c, *d); --d; } - } - } - - if(a <= d) { - c = b - 1; - - if((s = a - first) > (t = b - a)) s = t; - for(e = first, f = b - s; 0 < s; --s, ++e, ++f) swap(*e, *f); - if((s = d - c) > (t = last - d - 1)) s = t; - for(e = b, f = last - s; 0 < s; --s, ++e, ++f) swap(*e, *f); - - a = first + (b - a), c = last - (d - c); - b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); - - if((a - first) <= (last - c)) { - if((last - c) <= (c - b)) { - STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); - STACK_PUSH(c, last, depth, limit); - last = a; - } else if((a - first) <= (c - b)) { - STACK_PUSH(c, last, depth, limit); - STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); - last = a; - } else { - STACK_PUSH(c, last, depth, limit); - STACK_PUSH(first, a, depth, limit); - first = b, last = c, depth += 1, limit = ss_ilg(c - b); - } - } else { - if((a - first) <= (c - b)) { - STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); - STACK_PUSH(first, a, depth, limit); - first = c; - } else if((last - c) <= (c - b)) { - STACK_PUSH(first, a, depth, limit); - STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); - first = c; - } else { - STACK_PUSH(first, a, depth, limit); - STACK_PUSH(c, last, depth, limit); - first = b, last = c, depth += 1, limit = ss_ilg(c - b); - } - } - } else { - limit += 1; - if(Td[PA[*first] - 1] < v) { - first = ss_partition(PA, first, last, depth); - limit = ss_ilg(last - first); - } - depth += 1; - } - } - #undef STACK_SIZE -} - -#endif - -#if SS_BLOCKSIZE != 0 - -inline auto ss_blockswap(int* a, int* b, int n) -> void { - int t; - for(; 0 < n; --n, ++a, ++b) { - t = *a, *a = *b, *b = t; - } -} - -inline auto ss_rotate(int* first, int* middle, int* last) -> void { - int *a, *b, t, l, r; - l = middle - first, r = last - middle; - for(; (0 < l) && (0 < r);) { - if(l == r) { ss_blockswap(first, middle, l); break; } - if(l < r) { - a = last - 1, b = middle - 1; - t = *a; - do { - *a-- = *b, *b-- = *a; - if(b < first) { - *a = t; - last = a; - if((r -= l + 1) <= l) break; - a -= 1, b = middle - 1; - t = *a; - } - } while(1); - } else { - a = first, b = middle; - t = *a; - do { - *a++ = *b, *b++ = *a; - if(last <= b) { - *a = t; - first = a + 1; - if((l -= r + 1) <= r) break; - a += 1, b = middle; - t = *a; - } - } while(1); - } - } -} - -inline auto ss_inplacemerge( - const uint8_t* T, const int* PA, int* first, int* middle, int* last, int depth -) -> void { - const int* p; - int *a, *b, len, half, q, r, x; - - for(;;) { - if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } - else { x = 0; p = PA + *(last - 1); } - for(a = first, len = middle - first, half = len >> 1, r = -1; 0 < len; len = half, half >>= 1) { - b = a + half; - q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); - if(q < 0) { - a = b + 1; - half -= (len & 1) ^ 1; - } else { - r = q; - } - } - if(a < middle) { - if(r == 0) *a = ~*a; - ss_rotate(a, middle, last); - last -= middle - a; - middle = a; - if(first == middle) break; - } - --last; - if(x != 0) { while(*--last < 0); } - if(middle == last) break; - } -} - -//merge-forward with internal buffer -inline auto ss_mergeforward( - const uint8_t* T, const int* PA, int* first, int* middle, int* last, int* buf, int depth -) -> void { - int *a, *b, *c, *bufend, t, r; - - bufend = buf + (middle - first) - 1; - ss_blockswap(buf, first, middle - first); - - for(t = *(a = first), b = buf, c = middle;;) { - r = ss_compare(T, PA + *b, PA + *c, depth); - if(r < 0) { - do { - *a++ = *b; - if(bufend <= b) { *bufend = t; return; } - *b++ = *a; - } while(*b < 0); - } else if(r > 0) { - do { - *a++ = *c, *c++ = *a; - if(last <= c) { - while(b < bufend) { *a++ = *b, *b++ = *a; } - *a = *b, *b = t; - return; - } - } while(*c < 0); - } else { - *c = ~*c; - do { - *a++ = *b; - if(bufend <= b) { *bufend = t; return; } - *b++ = *a; - } while(*b < 0); - - do { - *a++ = *c, *c++ = *a; - if(last <= c) { - while(b < bufend) *a++ = *b, *b++ = *a; - *a = *b, *b = t; - return; - } - } while(*c < 0); - } - } -} - -//merge-backward with internal buffer -inline auto ss_mergebackward( - const uint8_t* T, const int* PA, int* first, int* middle, int* last, int* buf, int depth -) -> void { - const int *p1, *p2; - int *a, *b, *c, *bufend, t, r, x; - - bufend = buf + (last - middle - 1); - ss_blockswap(buf, middle, last - middle); - - x = 0; - if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } - else { p1 = PA + *bufend; } - if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } - else { p2 = PA + *(middle - 1); } - for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { - r = ss_compare(T, p1, p2, depth); - if(0 < r) { - if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } - *a-- = *b; - if(b <= buf) { *buf = t; break; } - *b-- = *a; - if(*b < 0) { p1 = PA + ~*b; x |= 1; } - else { p1 = PA + *b; } - } else if(r < 0) { - if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } - *a-- = *c, *c-- = *a; - if(c < first) { - while(buf < b) *a-- = *b, *b-- = *a; - *a = *b, *b = t; - break; - } - if(*c < 0) { p2 = PA + ~*c; x |= 2; } - else { p2 = PA + *c; } - } else { - if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } - *a-- = ~*b; - if(b <= buf) { *buf = t; break; } - *b-- = *a; - if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } - *a-- = *c, *c-- = *a; - if(c < first) { - while(buf < b) *a-- = *b, *b-- = *a; - *a = *b, *b = t; - break; - } - if(*b < 0) { p1 = PA + ~*b; x |= 1; } - else { p1 = PA + *b; } - if(*c < 0) { p2 = PA + ~*c; x |= 2; } - else { p2 = PA + *c; } - } - } -} - -//D&C based merge -inline auto ss_swapmerge( - const uint8_t* T, const int* PA, int* first, int* middle, int* last, int* buf, int bufsize, int depth -) -> void { - #define STACK_SIZE SS_SMERGE_STACKSIZE - #define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) - #define MERGE_CHECK(a, b, c) \ - do { \ - if(((c) & 1) || (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) { \ - *(a) = ~*(a); \ - } \ - if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) { \ - *(b) = ~*(b); \ - } \ - } while(0) - struct { int *a, *b, *c, d; } stack[STACK_SIZE]; - int *l, *r, *lm, *rm; - int m, len, half, ssize, check, next; - - for(check = 0, ssize = 0;;) { - if((last - middle) <= bufsize) { - if((first < middle) && (middle < last)) { - ss_mergebackward(T, PA, first, middle, last, buf, depth); - } - MERGE_CHECK(first, last, check); - STACK_POP(first, middle, last, check); - continue; - } - - if((middle - first) <= bufsize) { - if(first < middle) { - ss_mergeforward(T, PA, first, middle, last, buf, depth); - } - MERGE_CHECK(first, last, check); - STACK_POP(first, middle, last, check); - continue; - } - - for(m = 0, len = min(middle - first, last - middle), half = len >> 1; 0 < len; len = half, half >>= 1) { - if(ss_compare(T, PA + GETIDX(*(middle + m + half)), - PA + GETIDX(*(middle - m - half - 1)), depth) < 0 - ) { - m += half + 1; - half -= (len & 1) ^ 1; - } - } - - if(0 < m) { - lm = middle - m, rm = middle + m; - ss_blockswap(lm, middle, m); - l = r = middle, next = 0; - if(rm < last) { - if(*rm < 0) { - *rm = ~*rm; - if(first < lm) { for(; *--l < 0;); next |= 4; } - next |= 1; - } else if(first < lm) { - for(; *r < 0; ++r); - next |= 2; - } - } - - if((l - first) <= (last - r)) { - STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); - middle = lm, last = l, check = (check & 3) | (next & 4); - } else { - if((next & 2) && (r == middle)) next ^= 6; - STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); - first = r, middle = rm, check = (next & 3) | (check & 4); - } - } else { - if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { - *middle = ~*middle; - } - MERGE_CHECK(first, last, check); - STACK_POP(first, middle, last, check); - } - } - - #undef STACK_SIZE - #undef GETIDX - #undef MERGE_CHECK -} - -#endif - -//substring sort -inline auto sssort( - const uint8_t* T, const int* PA, int* first, int* last, int* buf, int bufsize, int depth, int n, int lastsuffix -) -> void { - int* a; - #if SS_BLOCKSIZE != 0 - int *b, *middle, *curbuf; - int j, k, curbufsize, limit; - #endif - int i; - - if(lastsuffix != 0) ++first; - - #if SS_BLOCKSIZE == 0 - ss_mintrosort(T, PA, first, last, depth); - #else - if((bufsize < SS_BLOCKSIZE) && - (bufsize < (last - first)) && - (bufsize < (limit = ss_isqrt(last - first))) - ) { - if(SS_BLOCKSIZE < limit) limit = SS_BLOCKSIZE; - buf = middle = last - limit, bufsize = limit; - } else { - middle = last, limit = 0; - } - for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { - #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE - ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); - #elif 1 < SS_BLOCKSIZE - ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); - #endif - curbufsize = last - (a + SS_BLOCKSIZE); - curbuf = a + SS_BLOCKSIZE; - if(curbufsize <= bufsize) curbufsize = bufsize, curbuf = buf; - for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { - ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); - } - } - #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE - ss_mintrosort(T, PA, a, middle, depth); - #elif 1 < SS_BLOCKSIZE - ss_insertionsort(T, PA, a, middle, depth); - #endif - for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { - if(i & 1) { - ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); - a -= k; - } - } - if(limit != 0) { - #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE - ss_mintrosort(T, PA, middle, last, depth); - #elif 1 < SS_BLOCKSIZE - ss_insertionsort(T, PA, middle, last, depth); - #endif - ss_inplacemerge(T, PA, first, middle, last, depth); - } - #endif - - if(lastsuffix != 0) { - //insert last type B* suffix - int PAi[2]; - PAi[0] = PA[*(first - 1)]; - PAi[1] = n - 2; - for(a = first, i = *(first - 1); (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); ++a) { - *(a - 1) = *a; - } - *(a - 1) = i; - } -} - -// [[trsort.c]] - -inline auto tr_ilg(int n) -> int { - return ilg32(n); -} - -//simple insertionsort for small size groups -inline auto tr_insertionsort(const int* ISAd, int* first, int* last) -> void { - int *a, *b; - int t, r; - - for(a = first + 1; a < last; ++a) { - for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { - do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); - if(b < first) { break; } - } - if(r == 0) { *b = ~*b; } - *(b + 1) = t; - } -} - -inline auto tr_fixdown(const int* ISAd, int* SA, int i, int size) -> void { - int j, k, v, c, d, e; - - for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { - d = ISAd[SA[k = j++]]; - if(d < (e = ISAd[SA[j]])) { k = j; d = e; } - if(d <= c) { break; } - } - SA[i] = v; -} - -//simple top-down heapsort -inline auto tr_heapsort(const int* ISAd, int* SA, int size) -> void { - int i, m, t; - - m = size; - if((size % 2) == 0) { - m--; - if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { swap(SA[m], SA[m / 2]); } - } - - for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } - if((size % 2) == 0) { swap(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } - for(i = m - 1; 0 < i; --i) { - t = SA[0], SA[0] = SA[i]; - tr_fixdown(ISAd, SA, 0, i); - SA[i] = t; - } -} - -//returns the median of three elements -inline auto tr_median3(const int* ISAd, int* v1, int* v2, int* v3) -> int* { - if(ISAd[*v1] > ISAd[*v2]) swap(v1, v2); - if(ISAd[*v2] > ISAd[*v3]) return ISAd[*v1] > ISAd[*v3] ? v1 : v3; - return v2; -} - -//returns the median of five elements -inline auto tr_median5(const int* ISAd, int* v1, int* v2, int* v3, int* v4, int* v5) -> int* { - if(ISAd[*v2] > ISAd[*v3]) swap(v2, v3); - if(ISAd[*v4] > ISAd[*v5]) swap(v4, v5); - if(ISAd[*v2] > ISAd[*v4]) swap(v2, v4), swap(v3, v5); - if(ISAd[*v1] > ISAd[*v3]) swap(v1, v3); - if(ISAd[*v1] > ISAd[*v4]) swap(v1, v4), swap(v3, v5); - if(ISAd[*v3] > ISAd[*v4]) return v4; - return v3; -} - -//returns the pivot element -inline auto tr_pivot(const int* ISAd, int* first, int* last) -> int* { - int* middle; - int t; - - t = last - first; - middle = first + t / 2; - - if(t <= 512) { - if(t <= 32) { - return tr_median3(ISAd, first, middle, last - 1); - } else { - t >>= 2; - return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); - } - } - t >>= 3; - first = tr_median3(ISAd, first, first + t, first + (t << 1)); - middle = tr_median3(ISAd, middle - t, middle, middle + t); - last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); - return tr_median3(ISAd, first, middle, last); -} - -struct trbudget_t { - int chance; - int remain; - int incval; - int count; -}; - -inline auto trbudget_init(trbudget_t* budget, int chance, int incval) -> void { - budget->chance = chance; - budget->remain = budget->incval = incval; -} - -inline auto trbudget_check(trbudget_t* budget, int size) -> int { - if(size <= budget->remain) { budget->remain -= size; return 1; } - if(budget->chance == 0) { budget->count += size; return 0; } - budget->remain += budget->incval - size; - budget->chance -= 1; - return 1; -} - -inline auto tr_partition(const int* ISAd, int* first, int* middle, int* last, int** pa, int** pb, int v) -> void { - int *a, *b, *c, *d, *e, *f; - int t, s; - int x = 0; - - for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);); - if(((a = b) < last) && (x < v)) { - for(; (++b < last) && ((x = ISAd[*b]) <= v);) { - if(x == v) { swap(*b, *a); ++a; } - } - } - for(c = last; (b < --c) && ((x = ISAd[*c]) == v);); - if((b < (d = c)) && (x > v)) { - for(; (b < --c) && ((x = ISAd[*c]) >= v);) { - if(x == v) { swap(*c, *d); --d; } - } - } - for(; b < c;) { - swap(*b, *c); - for(; (++b < c) && ((x = ISAd[*b]) <= v);) { - if(x == v) { swap(*b, *a); ++a; } - } - for(; (b < --c) && ((x = ISAd[*c]) >= v);) { - if(x == v) { swap(*c, *d); --d; } - } - } - - if(a <= d) { - c = b - 1; - if((s = a - first) > (t = b - a)) { s = t; } - for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { swap(*e, *f); } - if((s = d - c) > (t = last - d - 1)) { s = t; } - for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { swap(*e, *f); } - first += (b - a), last -= (d - c); - } - *pa = first, *pb = last; -} - -//sort suffixes of middle partition by using sorted order of suffixes of left and right partition -inline auto tr_copy(int* ISA, int* SA, int* first, int* a, int* b, int* last, int depth) -> void { - int *c, *d, *e; - int s, v; - - v = b - SA - 1; - for(c = first, d = a - 1; c <= d; ++c) { - if((0 <= (s = *c - depth)) && (ISA[s] == v)) { - *++d = s; - ISA[s] = d - SA; - } - } - for(c = last - 1, e = d + 1, d = b; e < d; --c) { - if((0 <= (s = *c - depth)) && (ISA[s] == v)) { - *--d = s; - ISA[s] = d - SA; - } - } -} - -inline auto tr_partialcopy(int* ISA, int* SA, int* first, int* a, int* b, int* last, int depth) -> void { - int *c, *d, *e; - int s, v; - int rank, lastrank, newrank = -1; - - v = b - SA - 1; - lastrank = -1; - for(c = first, d = a - 1; c <= d; ++c) { - if((0 <= (s = *c - depth)) && (ISA[s] == v)) { - *++d = s; - rank = ISA[s + depth]; - if(lastrank != rank) { lastrank = rank; newrank = d - SA; } - ISA[s] = newrank; - } - } - - lastrank = -1; - for(e = d; first <= e; --e) { - rank = ISA[*e]; - if(lastrank != rank) { lastrank = rank; newrank = e - SA; } - if(newrank != rank) { ISA[*e] = newrank; } - } - - lastrank = -1; - for(c = last - 1, e = d + 1, d = b; e < d; --c) { - if((0 <= (s = *c - depth)) && (ISA[s] == v)) { - *--d = s; - rank = ISA[s + depth]; - if(lastrank != rank) { lastrank = rank; newrank = d - SA; } - ISA[s] = newrank; - } - } -} - -inline auto tr_introsort(int* ISA, const int* ISAd, int* SA, int* first, int* last, trbudget_t* budget) -> void { - struct { const int *a; int *b, *c; int d, e; } stack[TR_STACKSIZE]; - int *a, *b, *c; - int t; - int v, x = 0; - int incr = ISAd - ISA; - int limit, next; - int ssize, trlink = -1; - - for(ssize = 0, limit = tr_ilg(last - first);;) { - if(limit < 0) { - if(limit == -1) { - //tandem repeat partition - tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); - - //update ranks - if(a < last) { - for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } - } - if(b < last) { - for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } - } - - //push - if(1 < (b - a)) { - STACK_PUSH5(nullptr, a, b, 0, 0); - STACK_PUSH5(ISAd - incr, first, last, -2, trlink); - trlink = ssize - 2; - } - if((a - first) <= (last - b)) { - if(1 < (a - first)) { - STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); - last = a, limit = tr_ilg(a - first); - } else if(1 < (last - b)) { - first = b, limit = tr_ilg(last - b); - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } else { - if(1 < (last - b)) { - STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); - first = b, limit = tr_ilg(last - b); - } else if(1 < (a - first)) { - last = a, limit = tr_ilg(a - first); - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - } else if(limit == -2) { - //tandem repeat copy - a = stack[--ssize].b, b = stack[ssize].c; - if(stack[ssize].d == 0) { - tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); - } else { - if(0 <= trlink) { stack[trlink].d = -1; } - tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); - } - STACK_POP5(ISAd, first, last, limit, trlink); - } else { - //sorted partition - if(0 <= *first) { - a = first; - do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); - first = a; - } - if(first < last) { - a = first; do { *a = ~*a; } while(*++a < 0); - next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; - if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } - - //push - if(trbudget_check(budget, a - first)) { - if((a - first) <= (last - a)) { - STACK_PUSH5(ISAd, a, last, -3, trlink); - ISAd += incr, last = a, limit = next; - } else { - if(1 < (last - a)) { - STACK_PUSH5(ISAd + incr, first, a, next, trlink); - first = a, limit = -3; - } else { - ISAd += incr, last = a, limit = next; - } - } - } else { - if(0 <= trlink) { stack[trlink].d = -1; } - if(1 < (last - a)) { - first = a, limit = -3; - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - continue; - } - - if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { - tr_insertionsort(ISAd, first, last); - limit = -3; - continue; - } - - if(limit-- == 0) { - tr_heapsort(ISAd, first, last - first); - for(a = last - 1; first < a; a = b) { - for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } - } - limit = -3; - continue; - } - - //choose pivot - a = tr_pivot(ISAd, first, last); - swap(*first, *a); - v = ISAd[*first]; - - //partition - tr_partition(ISAd, first, first + 1, last, &a, &b, v); - if((last - first) != (b - a)) { - next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; - - //update ranks - for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } - if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } - - //push - if((1 < (b - a)) && (trbudget_check(budget, b - a))) { - if((a - first) <= (last - b)) { - if((last - b) <= (b - a)) { - if(1 < (a - first)) { - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - STACK_PUSH5(ISAd, b, last, limit, trlink); - last = a; - } else if(1 < (last - b)) { - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - first = b; - } else { - ISAd += incr, first = a, last = b, limit = next; - } - } else if((a - first) <= (b - a)) { - if(1 < (a - first)) { - STACK_PUSH5(ISAd, b, last, limit, trlink); - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - last = a; - } else { - STACK_PUSH5(ISAd, b, last, limit, trlink); - ISAd += incr, first = a, last = b, limit = next; - } - } else { - STACK_PUSH5(ISAd, b, last, limit, trlink); - STACK_PUSH5(ISAd, first, a, limit, trlink); - ISAd += incr, first = a, last = b, limit = next; - } - } else { - if((a - first) <= (b - a)) { - if(1 < (last - b)) { - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - STACK_PUSH5(ISAd, first, a, limit, trlink); - first = b; - } else if(1 < (a - first)) { - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - last = a; - } else { - ISAd += incr, first = a, last = b, limit = next; - } - } else if((last - b) <= (b - a)) { - if(1 < (last - b)) { - STACK_PUSH5(ISAd, first, a, limit, trlink); - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - first = b; - } else { - STACK_PUSH5(ISAd, first, a, limit, trlink); - ISAd += incr, first = a, last = b, limit = next; - } - } else { - STACK_PUSH5(ISAd, first, a, limit, trlink); - STACK_PUSH5(ISAd, b, last, limit, trlink); - ISAd += incr, first = a, last = b, limit = next; - } - } - } else { - if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } - if((a - first) <= (last - b)) { - if(1 < (a - first)) { - STACK_PUSH5(ISAd, b, last, limit, trlink); - last = a; - } else if(1 < (last - b)) { - first = b; - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } else { - if(1 < (last - b)) { - STACK_PUSH5(ISAd, first, a, limit, trlink); - first = b; - } else if(1 < (a - first)) { - last = a; - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - } - } else { - if(trbudget_check(budget, last - first)) { - limit = tr_ilg(last - first), ISAd += incr; - } else { - if(0 <= trlink) { stack[trlink].d = -1; } - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - } -} - -//tandem repeat sort -inline auto trsort(int* ISA, int* SA, int n, int depth) -> void { - int *ISAd; - int *first, *last; - trbudget_t budget; - int t, skip, unsorted; - - trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); - for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { - first = SA; - skip = 0; - unsorted = 0; - do { - if((t = *first) < 0) { first -= t; skip += t; } - else { - if(skip != 0) { *(first + skip) = skip; skip = 0; } - last = SA + ISA[t] + 1; - if(1 < (last - first)) { - budget.count = 0; - tr_introsort(ISA, ISAd, SA, first, last, &budget); - if(budget.count != 0) { unsorted += budget.count; } - else { skip = first - last; } - } else if((last - first) == 1) { - skip = -1; - } - first = last; - } - } while(first < (SA + n)); - if(skip != 0) { *(first + skip) = skip; } - if(unsorted == 0) break; - } -} - -// [[divsufsort.c]] - -inline auto sort_typeBstar(const uint8_t* T, int* SA, int* bucket_A, int* bucket_B, int n) -> int { - int *PAb, *ISAb, *buf; - int i, j, k, t, m, bufsize; - int c0, c1; - - //initialize bucket arrays - for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } - for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } - - //count the number of occurrences of the first one or two characters of each type A, B, and B* suffix - //moreover, store the beginning position of all type b* suffixes into the array SA - for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { - //type A suffix - do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); - if(0 <= i) { - //type B* suffix - ++BUCKET_BSTAR(c0, c1); - SA[--m] = i; - //type B suffix - for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { - ++BUCKET_B(c0, c1); - } - } - } - m = n - m; - - //note: a type B* suffix is lexicographically smaller than a type B suffix that begins with the same first two characters - - //calculate the index of start/end point of each bucket - for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { - t = i + BUCKET_A(c0); - BUCKET_A(c0) = i + j; //start point - i = t + BUCKET_B(c0, c0); - for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { - j += BUCKET_BSTAR(c0, c1); - BUCKET_BSTAR(c0, c1) = j; //end point - i += BUCKET_B(c0, c1); - } - } - - if(0 < m) { - //sort the type B* suffixes by their first two characters - PAb = SA + n - m; ISAb = SA + m; - for(int i = m - 2; 0 <= i; --i) { - t = PAb[i], c0 = T[t], c1 = T[t + 1]; - SA[--BUCKET_BSTAR(c0, c1)] = i; - } - t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; - SA[--BUCKET_BSTAR(c0, c1)] = m - 1; - - //sort the type B* substrings using sssort - buf = SA + m, bufsize = n - (2 * m); - for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { - for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { - i = BUCKET_BSTAR(c0, c1); - if(1 < (j - i)) { - sssort(T, PAb, SA + i, SA + j, buf, bufsize, 2, n, *(SA + i) == (m - 1)); - } - } - } - - //compare ranks of type B* substrings - for(i = m - 1; 0 <= i; --i) { - if(0 <= SA[i]) { - j = i; - do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); - SA[i + 1] = i - j; - if(i <= 0) break; - } - j = i; - do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); - ISAb[SA[i]] = j; - } - - //construct the inverse suffix array of type B* suffixes using trsort - trsort(ISAb, SA, m, 1); - - //set the sorted order of type B* suffixes - for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { - for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0); - if(0 <= i) { - t = i; - for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0); - SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; - } - } - - //calculate the index of start/end point of each bucket - BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; //end point - for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { - i = BUCKET_A(c0 + 1) - 1; - for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { - t = i - BUCKET_B(c0, c1); - BUCKET_B(c0, c1) = i; //end point - - //move all type B* suffixes to the correct position - for(i = t, j = BUCKET_BSTAR(c0, c1); j <= k; --i, --k) SA[i] = SA[k]; - } - BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; //start point - BUCKET_B(c0, c0) = i; //end point - } - } - - return m; -} - -inline auto construct_SA(const uint8_t* T, int* SA, int* bucket_A, int* bucket_B, int n, int m) -> void { - int *i, *j, *k; - int s; - int c0, c1, c2; - - if(0 < m) { - //construct the sorted order of type B suffixes by using the sorted order of type B* suffixes - for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { - //scan the suffix array from right to left - for(i = SA + BUCKET_BSTAR(c1, c1 + 1), - j = SA + BUCKET_A(c1 + 1) - 1, k = nullptr, c2 = -1; - i <= j; - --j) { - if(0 < (s = *j)) { - *j = ~s; - c0 = T[--s]; - if((0 < s) && (T[s - 1] > c0)) { s = ~s; } - if(c0 != c2) { - if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } - k = SA + BUCKET_B(c2 = c0, c1); - } - *k-- = s; - } else { - *j = ~s; - } - } - } - } - - //construct the suffix array by using the sorted order of type B suffixes - k = SA + BUCKET_A(c2 = T[n - 1]); - *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); - //scan the suffix array from left to right - for(i = SA, j = SA + n; i < j; ++i) { - if(0 < (s = *i)) { - c0 = T[--s]; - if((s == 0) || (T[s - 1] < c0)) { s = ~s; } - if(c0 != c2) { - BUCKET_A(c2) = k - SA; - k = SA + BUCKET_A(c2 = c0); - } - *k++ = s; - } else { - *i = ~s; - } - } -} - -//constructs the burrows-wheeler transformed string directly by using the sorted order of type B* suffixes -inline auto construct_BWT(const uint8_t* T, int* SA, int* bucket_A, int* bucket_B, int n, int m) -> int { - int *i, *j, *k, *orig, s, c0, c1, c2; - - if(0 < m) { - //construct the sorted order of type B suffixes by using the sorted order of type B* suffixes - for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { - //scan the suffix array from right to left - for(i = SA + BUCKET_BSTAR(c1, c1 + 1), j = SA + BUCKET_A(c1 + 1) - 1, k = nullptr, c2 = -1; i <= j; --j) { - if(0 < (s = *j)) { - c0 = T[--s]; - *j = ~((int)c0); - if((0 < s) && (T[s - 1] > c0)) s = ~s; - if(c0 != c2) { - if(0 <= c2) BUCKET_B(c2, c1) = k - SA; - k = SA + BUCKET_B(c2 = c0, c1); - } - *k-- = s; - } else if(s != 0) { - *j = ~s; - } - } - } - } - - //construct the BWTed string by using the sorted order of type B suffixes - k = SA + BUCKET_A(c2 = T[n - 1]); - *k++ = (T[n - 2] < c2) ? ~((int)T[n - 2]) : (n - 1); - //scan the suffix array from left to right - for(i = SA, j = SA + n, orig = SA; i < j; ++i) { - if(0 < (s = *i)) { - c0 = T[--s]; - *i = c0; - if((0 < s) && (T[s - 1] < c0)) s = ~((int)T[s - 1]); - if(c0 != c2) { - BUCKET_A(c2) = k - SA; - k = SA + BUCKET_A(c2 = c0); - } - *k++ = s; - } else if(s != 0) { - *i = ~s; - } else { - orig = i; - } - } - - return orig - SA; -} - -}} - -namespace nall { - -inline auto div_suf_sort(int* SA, const uint8_t* T, int n) -> int { - int m, err = 0; - - if(T == nullptr || SA == nullptr || n < 0) return -1; - if(n == 0) return 0; - if(n == 1) { SA[0] = 0; return 0; } - if(n == 2) { m = T[0] < T[1]; SA[m ^ 1] = 0; SA[m] = 1; return 0; } - - auto bucket_A = memory::allocate(BUCKET_A_SIZE); - auto bucket_B = memory::allocate(BUCKET_B_SIZE); - - if(bucket_A && bucket_B) { - m = libdivsufsort::sort_typeBstar(T, SA, bucket_A, bucket_B, n); - libdivsufsort::construct_SA(T, SA, bucket_A, bucket_B, n, m); - } else { - err = -2; - } - - memory::free(bucket_A); - memory::free(bucket_B); - - return err; -} - -//byuu: note that this function is broken, and not just in my port of it to nall -//even with the original library, it is incapable of producing a correct BWT result in *any* case - -inline auto div_suf_sort_bwt(const uint8_t* T, uint8_t* U, int* A, int n) -> int { - int *B, *bucket_A, *bucket_B, m, pidx, i; - - if(T == nullptr || U == nullptr || n < 0) return -1; - if(n == 0) return 0; - if(n == 1) return U[0] = T[0], 1; - - if((B = A) == nullptr) B = memory::allocate(n + 1); - bucket_A = memory::allocate(BUCKET_A_SIZE); - bucket_B = memory::allocate(BUCKET_B_SIZE); - - //burrows-wheeler transform - if((B != nullptr) && (bucket_A != nullptr) && (bucket_B != nullptr)) { - m = libdivsufsort::sort_typeBstar(T, B, bucket_A, bucket_B, n); - pidx = libdivsufsort::construct_BWT(T, B, bucket_A, bucket_B, n, m); - - //copy to output string - U[0] = T[n - 1]; - for(i = 0; i < pidx; ++i) U[i + 1] = (uint8_t)B[i]; - for(i += 1; i < n; ++i) U[i] = (uint8_t)B[i]; - pidx += 1; - } else { - pidx -= 2; - } - - memory::free(bucket_A); - memory::free(bucket_B); - if(A == nullptr) memory::free(B); - - return pidx; -} - -#undef ALPHABET_SIZE -#undef BUCKET_A -#undef BUCKET_B -#undef BUCKET_BSTAR -#undef BUCKET_A_SIZE -#undef BUCKET_B_SIZE -#undef SS_INSERTIONSORT_THRESHOLD -#undef SS_BLOCKSIZE -#undef SS_MISORT_STACKSIZE -#undef SS_SMERGE_STACKSIZE -#undef TR_INSERTIONSORT_THRESHOLD -#undef TR_STACKSIZE -#undef STACK_PUSH -#undef STACK_PUSH5 -#undef STACK_POP -#undef STACK_POP5 - -} diff --git a/nall/encode/bwt.hpp b/nall/encode/bwt.hpp index fa077dd2..a4b104d3 100644 --- a/nall/encode/bwt.hpp +++ b/nall/encode/bwt.hpp @@ -6,44 +6,81 @@ namespace nall { namespace Encode { -inline auto BWT(const void* data, uint size) -> vector { - auto input = (const uint8_t*)data; +/* + A standard suffix array cannot produce a proper burrows-wheeler transform, due to rotations. + + Take the input string, "nall", this gives us: + nall + alln + llna + lnal + + If we suffix sort this, we produce: + all => alln + l => lnal + ll => llna + nall => nall + + If we sort this, we produce: + alln + llna + lnal + nall + + Thus, suffix sorting gives us "nlal" as the last column instead of "nall". + This is because BWT rotates the input string, whereas suffix arrays sort the input string. + + Adding a 256th character terminator before sorting will not produce the desired result, either. + A more complicated string such as "mississippi" will sort as "ssmppissiii" with terminator=256, + and as "ipssmpissii" with terminator=0, alphabet=1..256, whereas we want "pssmipissii". + + Performing a merge sort to use a specialized comparison function that wraps suffixes is too slow at O(n log n). + + Producing a custom induced sort to handle rotations would be incredibly complicated, + owing to the recursive nature of induced sorting, among other things. + + So instead, a temporary array is produced that contains the input suffix twice. + This is then fed into the suffix array sort, and the doubled matches are filtered out. + After this point, suffixes are sorted in their mirrored form, and the correct result can be derived + + The result of this is an O(2n) algorithm, which vastly outperforms a naive O(n log n) algorithm, + but is still far from ideal. However, this will have to do until a better solution is devised. + + Although to be fair, BWT is inferior to the bijective BWT anyway, so it may not be worth the effort. +*/ + +inline auto BWT(array_view input) -> vector { + auto size = input.size(); vector output; output.reserve(8 + 8 + size); for(uint byte : range(8)) output.append(size >> byte * 8); for(uint byte : range(8)) output.append(0x00); - auto suffixes = new int[size]; -//suffix_array(suffixes, input, size); - for(uint n : range(size)) suffixes[n] = n; - sort(suffixes, size, [&](int lhs, int rhs) -> bool { - uint l = size; - while(l--) { - auto x = input[lhs++]; - auto y = input[rhs++]; - if(x != y) return x - y < 0; - if(lhs >= size) lhs = 0; - if(rhs >= size) rhs = 0; - } - return 0; - }); + vector buffer; + buffer.reserve(2 * size); + for(uint offset : range(size)) buffer.append(input[offset]); + for(uint offset : range(size)) buffer.append(input[offset]); + + auto suffixes = SuffixArray(buffer); + + vector prefixes; + prefixes.reserve(size); + + for(uint offset : range(2 * size + 1)) { + uint suffix = suffixes[offset]; + if(suffix >= size) continue; //beyond the bounds of the original input string + prefixes.append(suffix); + } uint64_t root = 0; for(uint offset : range(size)) { - if(suffixes[offset] == 0) root = offset; - uint suffix = suffixes[offset]; - if(suffix == 0) suffix = size; + uint suffix = prefixes[offset]; + if(suffix == 0) root = offset, suffix = size; output.append(input[--suffix]); } - for(uint byte : range(8)) output[8 + byte] = root >> byte * 8; - delete[] suffixes; + return output; } -template -inline auto BWT(const vector& buffer) -> vector { - return move(BWT(buffer.data(), buffer.size() * sizeof(T))); -} - }} diff --git a/nall/encode/dictionary.hpp b/nall/encode/dictionary.hpp deleted file mode 100644 index 17417ae0..00000000 --- a/nall/encode/dictionary.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include - -namespace nall { namespace Encode { - -struct Dictionary { - inline Dictionary(const void* data, uint size, uint capacity = 0); - inline ~Dictionary(); - - inline auto operator[](uint index) const -> uint; - inline auto scan(uint offset = 0, uint size = 0) -> uint; - inline auto find(uint prefix, uint& lower, uint& upper) -> void; - -private: - const uint8_t* data = nullptr; - uint size = 0; - - uint capacity = 0; - uint unique = 0; - uint* suffixes = nullptr; - uint* prefixes = nullptr; -}; - -Dictionary::Dictionary(const void* data, uint size, uint capacity) { - this->data = (const uint8_t*)data; - this->size = size; - this->capacity = capacity ? capacity : size; - suffixes = new uint[2 * this->capacity]; - prefixes = &suffixes[this->capacity]; -} - -Dictionary::~Dictionary() { - delete[] suffixes; -} - -auto Dictionary::operator[](uint index) const -> uint { - return suffixes[index]; -} - -auto Dictionary::scan(uint offset, uint size) -> uint { - size = min(size ? size : capacity, this->size - offset); - partial_suffix_array<32, 32>(suffixes, prefixes, data + offset, size, offset); - uint target = 0, source = 0; - while(source < size) { - prefixes[target] = prefixes[source]; - suffixes[target] = suffixes[source]; - uint length = 1; - while(source + length < size) { - if(suffixes[source + length] != suffixes[source] + length) break; - length++; - } - source += length; - target += 1; - } - return unique = target; -} - -auto Dictionary::find(uint prefix, uint& lower, uint& upper) -> void { - uint l = 0, r = unique - 1; - while(l < r - 1) { - uint m = l + r >> 1; - prefixes[m] >= prefix ? r = m : l = m; - } - lower = l, r = unique - 1; - while(l < r - 1) { - uint m = l + r >> 1; - prefixes[m] <= prefix ? l = m : r = m; - } - upper = r; -} - -}} diff --git a/nall/encode/huffman.hpp b/nall/encode/huffman.hpp index 570f1e89..15ec855b 100644 --- a/nall/encode/huffman.hpp +++ b/nall/encode/huffman.hpp @@ -2,10 +2,9 @@ namespace nall { namespace Encode { -inline auto Huffman(const void* data, uint size) -> vector { - auto input = (const uint8_t*)data; +inline auto Huffman(array_view input) -> vector { vector output; - for(uint byte : range(8)) output.append(size >> byte * 8); + for(uint byte : range(8)) output.append(input.size() >> byte * 8); struct Node { uint frequency = 0; @@ -14,7 +13,7 @@ inline auto Huffman(const void* data, uint size) -> vector { uint rhs = 0; }; array nodes; - for(uint offset : range(size)) nodes[input[offset]].frequency++; + for(uint offset : range(input.size())) nodes[input[offset]].frequency++; uint count = 0; for(uint offset : range(511)) { @@ -61,8 +60,8 @@ inline auto Huffman(const void* data, uint size) -> vector { for(uint index : reverse(range(9))) write(nodes[256 + offset].rhs >> index & 1); } - for(uint offset : range(size)) { - uint node = input[offset], length = 0; + for(uint byte : input) { + uint node = byte, length = 0; uint256_t sequence = 0; //traversing the array produces the bitstream in reverse order do { @@ -82,9 +81,4 @@ inline auto Huffman(const void* data, uint size) -> vector { return output; } -template -inline auto Huffman(const vector& buffer) -> vector { - return move(Huffman(buffer.data(), buffer.size() * sizeof(T))); -} - }} diff --git a/nall/encode/lzsa.hpp b/nall/encode/lzsa.hpp index 86576aee..58b3f5dd 100644 --- a/nall/encode/lzsa.hpp +++ b/nall/encode/lzsa.hpp @@ -8,22 +8,12 @@ namespace nall { namespace Encode { -inline auto LZSA(const void* data, uint64_t size) -> vector { +inline auto LZSA(array_view input) -> vector { vector output; - for(uint byte : range(8)) output.append(size >> byte * 8); + for(uint byte : range(8)) output.append(input.size() >> byte * 8); - auto input = (const uint8_t*)data; + auto suffixArray = SuffixArray(input).lpf(); uint index = 0; - - auto buffers = new int[size * 4]; - auto suffixes = &buffers[0 * size]; - auto phi = &buffers[1 * size]; - auto lengths = &buffers[2 * size]; - auto offsets = &buffers[3 * size]; - suffix_array(suffixes, input, size); - suffix_array_phi(phi, suffixes, size); - suffix_array_lps(lengths, offsets, phi, input, size); - vector flags; vector literals; vector stringLengths; @@ -55,13 +45,13 @@ inline auto LZSA(const void* data, uint64_t size) -> vector { stringOffsets.append(offset >> 24); }; - while(index < size) { - int length = lengths[index]; - int offset = offsets[index]; + while(index < input.size()) { + int length, offset; + suffixArray.previous(length, offset, index); for(uint ahead = 1; ahead <= 2; ahead++) { - int aheadLength = lengths[index + ahead]; - int aheadOffset = offsets[index + ahead]; + int aheadLength, aheadOffset; + suffixArray.previous(aheadLength, aheadOffset, index + ahead); if(aheadLength > length && aheadOffset >= 0) { length = 0; break; @@ -87,11 +77,9 @@ inline auto LZSA(const void* data, uint64_t size) -> vector { save(Encode::Huffman(flags)); save(Encode::Huffman(literals)); -//save(Encode::Huffman(Encode::BWT(literals))); save(Encode::Huffman(stringLengths)); save(Encode::Huffman(stringOffsets)); - delete[] buffers; return output; } diff --git a/nall/encode/lzss.hpp b/nall/encode/lzss.hpp deleted file mode 100644 index 42182b22..00000000 --- a/nall/encode/lzss.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include - -namespace nall { namespace Encode { - -inline auto LZSS(const void* data, uint64_t size, uint windowBits = 16, uint lengthBits = 8) -> vector { - vector output; - for(uint byte : range(8)) output.append(size >> byte * 8); - output.append(windowBits); - output.append(lengthBits); - - const uint lengthExtends = 4 + (1 << lengthBits) - 1; - const uint lengthMaximum = lengthExtends + 255; - const uint windowMaximum = 1 << windowBits; - const uint windowRange = windowMaximum + lengthMaximum; - - auto input = (const uint8_t*)data; - auto read = [&](uint address) -> uint { - if(address + 3 > size) return 0; - return input[address + 0] << 24 | input[address + 1] << 16 | input[address + 2] << 8 | input[address + 3] << 0; - }; - - Dictionary dictionary(data, size, 2 * windowRange); - dictionary.scan(); - - for(uint offset = 0, base = 0, flags = 0, bit = 7; offset < size;) { - if(offset - base >= 2 * windowRange) { - dictionary.scan(base = offset - windowRange); - } - - uint prefix = read(offset), lower, upper; - dictionary.find(prefix, lower, upper); - - uint lengthLongest = 0, windowLongest = 0; - for(uint index = lower; index <= upper; index++) { - int window = (int)offset - (int)dictionary[index]; - if(window <= 0) continue; - window = min(window, windowMaximum); - - uint length = 0; - do { - if(offset + length >= size) break; - if(input[offset + length] != input[offset + length - window]) break; - } while(++length < lengthMaximum); - - if(length > lengthLongest) { - lengthLongest = length; - windowLongest = window; - if(length == lengthMaximum) break; - } - } - - if(++bit == 8) { - flags = output.size(); - output.append(bit = 0); - } - - if(lengthLongest < 4) { - output.append(input[offset++]); - } else { - output[flags] |= 1 << bit; - offset += lengthLongest; - - uint encoding = min(lengthLongest, lengthExtends) - 4 << windowBits | windowLongest - 1; - output.append(encoding >> 0); - output.append(encoding >> 8); - output.append(encoding >> 16); - if(lengthLongest >= lengthExtends) output.append(lengthLongest - lengthExtends); - } - } - - return output; -} - -}} diff --git a/nall/encode/mtf.hpp b/nall/encode/mtf.hpp index b9ed5783..b87e8668 100644 --- a/nall/encode/mtf.hpp +++ b/nall/encode/mtf.hpp @@ -4,17 +4,16 @@ namespace nall { namespace Encode { -inline auto MTF(const void* data, uint size) -> vector { - auto input = (const uint8_t*)data; +inline auto MTF(array_view input) -> vector { vector output; - output.resize(size); + output.resize(input.size()); uint8_t order[256]; for(uint n : range(256)) order[n] = n; - for(uint offset = 0; offset < size; offset++) { - auto data = input[offset]; - for(uint index = 0; index < 256; index++) { + for(uint offset : range(input.size())) { + uint data = input[offset]; + for(uint index : range(256)) { uint value = order[index]; if(value == data) { output[offset] = index; @@ -28,9 +27,4 @@ inline auto MTF(const void* data, uint size) -> vector { return output; } -template -inline auto MTF(const vector& buffer) -> vector { - return move(MTF(buffer.data(), buffer.size() * sizeof(T))); -} - }} diff --git a/nall/encode/rle.hpp b/nall/encode/rle.hpp index f481c55a..bcdae714 100644 --- a/nall/encode/rle.hpp +++ b/nall/encode/rle.hpp @@ -3,17 +3,15 @@ namespace nall { namespace Encode { template //S = word size; M = match length -inline auto RLE(const void* data, uint64_t size) -> vector { +inline auto RLE(array_view input) -> vector { vector output; - for(uint byte : range(8)) output.append(size >> byte * 8); + for(uint byte : range(8)) output.append(input.size() >> byte * 8); - auto input = (const uint8_t*)data; uint base = 0; uint skip = 0; auto load = [&](uint offset) -> uint8_t { - if(offset >= size) return 0x00; - return input[offset]; + return input(offset); }; auto read = [&](uint offset) -> uint64_t { @@ -34,9 +32,9 @@ inline auto RLE(const void* data, uint64_t size) -> vector { } while(--skip); }; - while(base + S * skip < size) { + while(base + S * skip < input.size()) { uint same = 1; - for(uint offset = base + S * (skip + 1); offset < size; offset += S) { + for(uint offset = base + S * (skip + 1); offset < input.size(); offset += S) { if(read(offset) != read(base + S * skip)) break; if(++same == 127 + M) break; } @@ -55,9 +53,4 @@ inline auto RLE(const void* data, uint64_t size) -> vector { return output; } -template -inline auto RLE(const vector& buffer) -> vector { - return move(RLE(buffer.data(), buffer.size() * sizeof(T))); -} - }} diff --git a/nall/encode/url.hpp b/nall/encode/url.hpp index c2bbd4e8..1e253814 100644 --- a/nall/encode/url.hpp +++ b/nall/encode/url.hpp @@ -2,7 +2,7 @@ namespace nall { namespace Encode { -inline auto URL(const string& input) -> string { +inline auto URL(string_view input) -> string { string output; for(auto c : input) { //unreserved characters diff --git a/nall/encode/zip.hpp b/nall/encode/zip.hpp index dcd0f28e..e2bf4664 100644 --- a/nall/encode/zip.hpp +++ b/nall/encode/zip.hpp @@ -18,7 +18,7 @@ struct ZIP { auto append(string filename, const uint8_t* data = nullptr, uint size = 0u, time_t timestamp = 0) -> void { filename.transform("\\", "/"); if(!timestamp) timestamp = this->timestamp; - uint32_t checksum = Hash::CRC32(data, size).digest().hex(); + uint32_t checksum = Hash::CRC32({data, size}).digest().hex(); directory.append({filename, timestamp, checksum, size, fp.offset()}); fp.writel(0x04034b50, 4); //signature diff --git a/nall/file.hpp b/nall/file.hpp index 03510d06..179fd49e 100644 --- a/nall/file.hpp +++ b/nall/file.hpp @@ -117,7 +117,7 @@ struct file : inode, varint { static auto sha256(const string& filename) -> string { auto buffer = read(filename); - return Hash::SHA256(buffer.data(), buffer.size()).digest(); + return Hash::SHA256(buffer).digest(); } auto read() -> uint8_t { diff --git a/nall/hash/crc16.hpp b/nall/hash/crc16.hpp index 5829196f..77441426 100644 --- a/nall/hash/crc16.hpp +++ b/nall/hash/crc16.hpp @@ -5,7 +5,12 @@ namespace nall { namespace Hash { struct CRC16 : Hash { - nallHash(CRC16) + using Hash::input; + + CRC16(array_view buffer = {}) { + reset(); + input(buffer); + } auto reset() -> void override { checksum = ~0; diff --git a/nall/hash/crc32.hpp b/nall/hash/crc32.hpp index 20bb7eed..e7cd9407 100644 --- a/nall/hash/crc32.hpp +++ b/nall/hash/crc32.hpp @@ -5,7 +5,12 @@ namespace nall { namespace Hash { struct CRC32 : Hash { - nallHash(CRC32) + using Hash::input; + + CRC32(array_view buffer = {}) { + reset(); + input(buffer); + } auto reset() -> void override { checksum = ~0; diff --git a/nall/hash/crc64.hpp b/nall/hash/crc64.hpp index 23c74a65..d32f3388 100644 --- a/nall/hash/crc64.hpp +++ b/nall/hash/crc64.hpp @@ -5,7 +5,12 @@ namespace nall { namespace Hash { struct CRC64 : Hash { - nallHash(CRC64) + using Hash::input; + + CRC64(array_view buffer = {}) { + reset(); + input(buffer); + } auto reset() -> void override { checksum = ~0; diff --git a/nall/hash/hash.hpp b/nall/hash/hash.hpp index 0ac35d43..775475f4 100644 --- a/nall/hash/hash.hpp +++ b/nall/hash/hash.hpp @@ -20,6 +20,10 @@ struct Hash { virtual auto input(uint8_t data) -> void = 0; virtual auto output() const -> vector = 0; + auto input(array_view data) -> void { + for(auto byte : data) input(byte); + } + auto input(const void* data, uint64_t size) -> void { auto p = (const uint8_t*)data; while(size--) input(*p++); diff --git a/nall/hash/sha224.hpp b/nall/hash/sha224.hpp index 9f1b74e2..ecf7572e 100644 --- a/nall/hash/sha224.hpp +++ b/nall/hash/sha224.hpp @@ -5,7 +5,12 @@ namespace nall { namespace Hash { struct SHA224 : Hash { - nallHash(SHA224) + using Hash::input; + + SHA224(array_view buffer = {}) { + reset(); + input(buffer); + } auto reset() -> void override { for(auto& n : queue) n = 0; diff --git a/nall/hash/sha256.hpp b/nall/hash/sha256.hpp index 04e81fff..8f7fe9e4 100644 --- a/nall/hash/sha256.hpp +++ b/nall/hash/sha256.hpp @@ -5,7 +5,12 @@ namespace nall { namespace Hash { struct SHA256 : Hash { - nallHash(SHA256) + using Hash::input; + + SHA256(array_view buffer = {}) { + reset(); + input(buffer); + } auto reset() -> void override { for(auto& n : queue) n = 0; diff --git a/nall/hash/sha384.hpp b/nall/hash/sha384.hpp index e0be5d9f..63ad43f2 100644 --- a/nall/hash/sha384.hpp +++ b/nall/hash/sha384.hpp @@ -5,7 +5,12 @@ namespace nall { namespace Hash { struct SHA384 : Hash { - nallHash(SHA384) + using Hash::input; + + SHA384(array_view buffer = {}) { + reset(); + input(buffer); + } auto reset() -> void override { for(auto& n : queue) n = 0; diff --git a/nall/hash/sha512.hpp b/nall/hash/sha512.hpp index 0827982e..83c91e24 100644 --- a/nall/hash/sha512.hpp +++ b/nall/hash/sha512.hpp @@ -5,7 +5,12 @@ namespace nall { namespace Hash { struct SHA512 : Hash { - nallHash(SHA512) + using Hash::input; + + SHA512(array_view buffer = {}) { + reset(); + input(buffer); + } auto reset() -> void override { for(auto& n : queue) n = 0; diff --git a/nall/induced-sort.hpp b/nall/induced-sort.hpp new file mode 100644 index 00000000..ab8cf543 --- /dev/null +++ b/nall/induced-sort.hpp @@ -0,0 +1,175 @@ +#pragma once + +//suffix array construction via induced sorting +//many thanks to Screwtape for the thorough explanation of this algorithm +//this implementation would not be possible without his help + +namespace nall { + +//note that induced_sort will return an array of size+1 characters, +//where the first character is the empty suffix, equal to size + +template +inline auto induced_sort(const T* data, const uint size, const uint characters = 256) -> vector { + if(size == 0) return vector{0}; //required to avoid out-of-bounds accesses + if(size == 1) return vector{1, 0}; //not strictly necessary; but more performant + + vector types; //0 = S-suffix (sort before next suffix), 1 = L-suffix (sort after next suffix) + types.resize(size + 1); + + types[size - 0] = 0; //empty suffix is always S-suffix + types[size - 1] = 1; //last suffix is always L-suffix compared to empty suffix + for(uint n : reverse(range(size - 1))) { + if(data[n] < data[n + 1]) { + types[n] = 0; //this suffix is smaller than the one after it + } else if(data[n] > data[n + 1]) { + types[n] = 1; //this suffix is larger than the one after it + } else { + types[n] = types[n + 1]; //this suffix will be the same as the one after it + } + } + + //left-most S-suffix + auto isLMS = [&](int n) -> bool { + if(n == 0) return 0; //no character to the left of the first suffix + return !types[n] && types[n - 1]; //true if this is the start of a new S-suffix + }; + + //test if two LMS-substrings are equal + auto isEqual = [&](int lhs, int rhs) -> bool { + if(lhs == size || rhs == size) return false; //no other suffix can be equal to the empty suffix + + for(uint n = 0;; n++) { + bool lhsLMS = isLMS(lhs + n); + bool rhsLMS = isLMS(rhs + n); + if(n && lhsLMS && rhsLMS) return true; //substrings are identical + if(lhsLMS != rhsLMS) return false; //length mismatch: substrings cannot be identical + if(data[lhs + n] != data[rhs + n]) return false; //character mismatch: substrings are different + } + }; + + //determine the sizes of each bucket: one bucket per character + vector counts; + counts.resize(characters); + for(uint n : range(size)) counts[data[n]]++; + + //bucket sorting start offsets + vector heads; + heads.resize(characters); + + uint headOffset; + auto getHeads = [&] { + headOffset = 1; + for(uint n : range(characters)) { + heads[n] = headOffset; + headOffset += counts[n]; + } + }; + + //bucket sorting end offsets + vector tails; + tails.resize(characters); + + uint tailOffset; + auto getTails = [&] { + tailOffset = 1; + for(uint n : range(characters)) { + tailOffset += counts[n]; + tails[n] = tailOffset - 1; + } + }; + + //inaccurate LMS bucket sort + vector suffixes; + suffixes.resize(size + 1, (int)-1); + + getTails(); + for(uint n : range(size)) { + if(!isLMS(n)) continue; //skip non-LMS-suffixes + suffixes[tails[data[n]]--] = n; //advance from the tail of the bucket + } + + suffixes[0] = size; //the empty suffix is always an LMS-suffix, and is the first suffix + + //sort all L-suffixes to the left of LMS-suffixes + auto sortL = [&] { + getHeads(); + for(uint n : range(size + 1)) { + if(suffixes[n] == -1) continue; //offsets may not be known yet here ... + auto l = suffixes[n] - 1; + if(l < 0 || !types[l]) continue; //skip S-suffixes + suffixes[heads[data[l]]++] = l; //advance from the head of the bucket + } + }; + + auto sortS = [&] { + getTails(); + for(uint n : reverse(range(size + 1))) { + auto l = suffixes[n] - 1; + if(l < 0 || types[l]) continue; //skip L-suffixes + suffixes[tails[data[l]]--] = l; //advance from the tail of the bucket + } + }; + + sortL(); + sortS(); + + //analyze data for the summary suffix array + vector names; + names.resize(size + 1, (int)-1); + + uint currentName = 0; //keep a count to tag each unique LMS-substring with unique IDs + auto lastLMSOffset = suffixes[0]; //location in the original data of the last checked LMS suffix + names[lastLMSOffset] = currentName; //the first LMS-substring is always the empty suffix entry, at position 0 + + for(uint n : range(1, size + 1)) { + auto offset = suffixes[n]; + if(!isLMS(offset)) continue; //only LMS suffixes are important + + //if this LMS suffix starts with a different LMS substring than the last suffix observed ... + if(!isEqual(lastLMSOffset, offset)) currentName++; //then it gets a new name + lastLMSOffset = offset; //keep track of the new most-recent LMS suffix + names[lastLMSOffset] = currentName; //store the LMS suffix name where the suffix appears at in the original data + } + + vector summaryOffsets; + vector summaryData; + for(uint n : range(size + 1)) { + if(names[n] == -1) continue; + summaryOffsets.append(n); + summaryData.append(names[n]); + } + uint summaryCharacters = currentName + 1; //zero-indexed, so the total unique characters is currentName + 1 + + //make the summary suffix array + vector summaries; + if(summaryData.size() == summaryCharacters) { + //simple bucket sort when every character in summaryData appears only once + summaries.resize(summaryData.size() + 1, (int)-1); + summaries[0] = summaryData.size(); //always include the empty suffix at the beginning + for(int x : range(summaryData.size())) { + int y = summaryData[x]; + summaries[y + 1] = x; + } + } else { + //recurse until every character in summaryData is unique ... + summaries = induced_sort(summaryData.data(), summaryData.size() - 1, summaryCharacters); + } + + suffixes.fill(-1); //reuse existing buffer for accurate sort + + //accurate LMS sort + getTails(); + for(uint n : reverse(range(1, summaries.size()))) { + auto index = summaryOffsets[summaries[n]]; + suffixes[tails[data[index]]--] = index; //advance from the tail of the bucket + } + suffixes[0] = size; //always include the empty suffix at the beginning + + sortL(); + sortS(); + + return suffixes; +} + +} diff --git a/nall/location.hpp b/nall/location.hpp index 361d0c01..59910aad 100644 --- a/nall/location.hpp +++ b/nall/location.hpp @@ -4,7 +4,7 @@ namespace nall { namespace Location { // (/parent/child.type/) // (/parent/child.type/)name.type -inline auto path(view self) -> string { +inline auto path(string_view self) -> string { const char* p = self.data() + self.size() - 1; for(int offset = self.size() - 1; offset >= 0; offset--, p--) { if(*p == '/') return slice(self, 0, offset + 1); @@ -14,7 +14,7 @@ inline auto path(view self) -> string { // /parent/child.type/() // /parent/child.type/(name.type) -inline auto file(view self) -> string { +inline auto file(string_view self) -> string { const char* p = self.data() + self.size() - 1; for(int offset = self.size() - 1; offset >= 0; offset--, p--) { if(*p == '/') return slice(self, offset + 1); @@ -24,7 +24,7 @@ inline auto file(view self) -> string { // (/parent/)child.type/ // (/parent/child.type/)name.type -inline auto dir(view self) -> string { +inline auto dir(string_view self) -> string { const char* p = self.data() + self.size() - 1, *last = p; for(int offset = self.size() - 1; offset >= 0; offset--, p--) { if(*p == '/' && p == last) continue; @@ -35,7 +35,7 @@ inline auto dir(view self) -> string { // /parent/(child.type/) // /parent/child.type/(name.type) -inline auto base(view self) -> string { +inline auto base(string_view self) -> string { const char* p = self.data() + self.size() - 1, *last = p; for(int offset = self.size() - 1; offset >= 0; offset--, p--) { if(*p == '/' && p == last) continue; @@ -46,7 +46,7 @@ inline auto base(view self) -> string { // /parent/(child).type/ // /parent/child.type/(name).type -inline auto prefix(view self) -> string { +inline auto prefix(string_view self) -> string { const char* p = self.data() + self.size() - 1, *last = p; for(int offset = self.size() - 1, suffix = -1; offset >= 0; offset--, p--) { if(*p == '/' && p == last) continue; @@ -59,7 +59,7 @@ inline auto prefix(view self) -> string { // /parent/child(.type)/ // /parent/child.type/name(.type) -inline auto suffix(view self) -> string { +inline auto suffix(string_view self) -> string { const char* p = self.data() + self.size() - 1, *last = p; for(int offset = self.size() - 1; offset >= 0; offset--, p--) { if(*p == '/' && p == last) continue; @@ -69,7 +69,7 @@ inline auto suffix(view self) -> string { return ""; //no suffix found } -inline auto notsuffix(view self) -> string { +inline auto notsuffix(string_view self) -> string { return {path(self), prefix(self)}; } diff --git a/nall/nall.hpp b/nall/nall.hpp index 95ba2ada..ea8021a1 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/nall/path.hpp b/nall/path.hpp index 2b49e155..ed4d148c 100644 --- a/nall/path.hpp +++ b/nall/path.hpp @@ -14,7 +14,7 @@ inline auto active() -> string { return result; } -inline auto real(view name) -> string { +inline auto real(string_view name) -> string { string result; char path[PATH_MAX] = ""; if(::realpath(name, path)) result = Location::path(string{path}.transform("\\", "/")); diff --git a/nall/shared-pointer.hpp b/nall/shared-pointer.hpp index 7e4307ef..f35c9f2d 100644 --- a/nall/shared-pointer.hpp +++ b/nall/shared-pointer.hpp @@ -11,7 +11,7 @@ template struct shared_pointer; struct shared_pointer_manager { void* pointer = nullptr; - function void> deleter; + function deleter; uint strong = 0; uint weak = 0; @@ -41,7 +41,9 @@ struct shared_pointer { shared_pointer(T* source, const function& deleter) { operator=(source); - manager->deleter = [=](void* p) { deleter((T*)p); }; + manager->deleter = function([=](void* p) { + deleter((T*)p); + }); } shared_pointer(const shared_pointer& source) { diff --git a/nall/string.hpp b/nall/string.hpp index a8558de4..7569356b 100644 --- a/nall/string.hpp +++ b/nall/string.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -27,21 +28,21 @@ namespace nall { struct string; struct string_format; -template<> struct view { - using type = view; +struct string_view { + using type = string_view; //view.hpp - inline view(); - inline view(const view& source); - inline view(view&& source); - inline view(const char* data); - inline view(const char* data, uint size); - inline view(const string& source); - template inline view(P&&... p); - inline ~view(); + inline string_view(); + inline string_view(const string_view& source); + inline string_view(string_view&& source); + inline string_view(const char* data); + inline string_view(const char* data, uint size); + inline string_view(const string& source); + template inline string_view(P&&... p); + inline ~string_view(); - inline auto operator=(const view& source) -> view&; - inline auto operator=(view&& source) -> view&; + inline auto operator=(const string_view& source) -> type&; + inline auto operator=(string_view&& source) -> type&; inline operator const char*() const; inline auto data() const -> const char*; @@ -80,7 +81,7 @@ inline auto tokenize(const char* s, const char* p) -> bool; inline auto tokenize(vector& list, const char* s, const char* p) -> bool; //utility.hpp -inline auto slice(view self, int offset = 0, int length = -1) -> string; +inline auto slice(string_view self, int offset = 0, int length = -1) -> string; template inline auto fromInteger(char* result, T value) -> char*; template inline auto fromNatural(char* result, T value) -> char*; template inline auto fromReal(char* str, T value) -> uint; @@ -146,6 +147,8 @@ public: explicit operator bool() const { return _size; } operator const char*() const { return (const char*)data(); } + operator array_view() const { return {(const char*)data(), size()}; } + operator array_view() const { return {(const uint8_t*)data(), size()}; } auto operator==(const string& source) const -> bool { return size() == source.size() && memory::compare(data(), source.data(), size()) == 0; @@ -157,12 +160,12 @@ public: auto operator==(const char* source) const -> bool { return strcmp(data(), source) == 0; } auto operator!=(const char* source) const -> bool { return strcmp(data(), source) != 0; } - auto operator==(view source) const -> bool { return compare(source) == 0; } - auto operator!=(view source) const -> bool { return compare(source) != 0; } - auto operator< (view source) const -> bool { return compare(source) < 0; } - auto operator<=(view source) const -> bool { return compare(source) <= 0; } - auto operator> (view source) const -> bool { return compare(source) > 0; } - auto operator>=(view source) const -> bool { return compare(source) >= 0; } + auto operator==(string_view source) const -> bool { return compare(source) == 0; } + auto operator!=(string_view source) const -> bool { return compare(source) != 0; } + auto operator< (string_view source) const -> bool { return compare(source) < 0; } + auto operator<=(string_view source) const -> bool { return compare(source) <= 0; } + auto operator> (string_view source) const -> bool { return compare(source) > 0; } + auto operator>=(string_view source) const -> bool { return compare(source) >= 0; } string(const string& source) : string() { operator=(source); } string(string&& source) : string() { operator=(move(source)); } @@ -193,17 +196,17 @@ public: inline auto length() const -> uint; //find.hpp - inline auto contains(view characters) const -> maybe; + inline auto contains(string_view characters) const -> maybe; - template inline auto _find(int, view) const -> maybe; + template inline auto _find(int, string_view) const -> maybe; - inline auto find(view source) const -> maybe; - inline auto ifind(view source) const -> maybe; - inline auto qfind(view source) const -> maybe; - inline auto iqfind(view source) const -> maybe; + inline auto find(string_view source) const -> maybe; + inline auto ifind(string_view source) const -> maybe; + inline auto qfind(string_view source) const -> maybe; + inline auto iqfind(string_view source) const -> maybe; - inline auto findFrom(int offset, view source) const -> maybe; - inline auto ifindFrom(int offset, view source) const -> maybe; + inline auto findFrom(int offset, string_view source) const -> maybe; + inline auto ifindFrom(int offset, string_view source) const -> maybe; //format.hpp inline auto format(const nall::string_format& params) -> type&; @@ -211,20 +214,20 @@ public: //compare.hpp template inline static auto _compare(const char*, uint, const char*, uint) -> int; - inline static auto compare(view, view) -> int; - inline static auto icompare(view, view) -> int; + inline static auto compare(string_view, string_view) -> int; + inline static auto icompare(string_view, string_view) -> int; - inline auto compare(view source) const -> int; - inline auto icompare(view source) const -> int; + inline auto compare(string_view source) const -> int; + inline auto icompare(string_view source) const -> int; - inline auto equals(view source) const -> bool; - inline auto iequals(view source) const -> bool; + inline auto equals(string_view source) const -> bool; + inline auto iequals(string_view source) const -> bool; - inline auto beginsWith(view source) const -> bool; - inline auto ibeginsWith(view source) const -> bool; + inline auto beginsWith(string_view source) const -> bool; + inline auto ibeginsWith(string_view source) const -> bool; - inline auto endsWith(view source) const -> bool; - inline auto iendsWith(view source) const -> bool; + inline auto endsWith(string_view source) const -> bool; + inline auto iendsWith(string_view source) const -> bool; //convert.hpp inline auto downcase() -> type&; @@ -233,41 +236,41 @@ public: inline auto qdowncase() -> type&; inline auto qupcase() -> type&; - inline auto transform(view from, viewto) -> type&; + inline auto transform(string_view from, string_view to) -> type&; //match.hpp - inline auto match(view source) const -> bool; - inline auto imatch(view source) const -> bool; + inline auto match(string_view source) const -> bool; + inline auto imatch(string_view source) const -> bool; //replace.hpp - template inline auto _replace(view, view, long) -> type&; - inline auto replace(view from, view to, long limit = LONG_MAX) -> type&; - inline auto ireplace(view from, view to, long limit = LONG_MAX) -> type&; - inline auto qreplace(view from, view to, long limit = LONG_MAX) -> type&; - inline auto iqreplace(view from, view to, long limit = LONG_MAX) -> type&; + template inline auto _replace(string_view, string_view, long) -> type&; + inline auto replace(string_view from, string_view to, long limit = LONG_MAX) -> type&; + inline auto ireplace(string_view from, string_view to, long limit = LONG_MAX) -> type&; + inline auto qreplace(string_view from, string_view to, long limit = LONG_MAX) -> type&; + inline auto iqreplace(string_view from, string_view to, long limit = LONG_MAX) -> type&; //split.hpp - inline auto split(view key, long limit = LONG_MAX) const -> vector; - inline auto isplit(view key, long limit = LONG_MAX) const -> vector; - inline auto qsplit(view key, long limit = LONG_MAX) const -> vector; - inline auto iqsplit(view key, long limit = LONG_MAX) const -> vector; + inline auto split(string_view key, long limit = LONG_MAX) const -> vector; + inline auto isplit(string_view key, long limit = LONG_MAX) const -> vector; + inline auto qsplit(string_view key, long limit = LONG_MAX) const -> vector; + inline auto iqsplit(string_view key, long limit = LONG_MAX) const -> vector; //trim.hpp - inline auto trim(view lhs, view rhs, long limit = LONG_MAX) -> type&; - inline auto trimLeft(view lhs, long limit = LONG_MAX) -> type&; - inline auto trimRight(view rhs, long limit = LONG_MAX) -> type&; + inline auto trim(string_view lhs, string_view rhs, long limit = LONG_MAX) -> type&; + inline auto trimLeft(string_view lhs, long limit = LONG_MAX) -> type&; + inline auto trimRight(string_view rhs, long limit = LONG_MAX) -> type&; - inline auto itrim(view lhs, view rhs, long limit = LONG_MAX) -> type&; - inline auto itrimLeft(view lhs, long limit = LONG_MAX) -> type&; - inline auto itrimRight(view rhs, long limit = LONG_MAX) -> type&; + inline auto itrim(string_view lhs, string_view rhs, long limit = LONG_MAX) -> type&; + inline auto itrimLeft(string_view lhs, long limit = LONG_MAX) -> type&; + inline auto itrimRight(string_view rhs, long limit = LONG_MAX) -> type&; inline auto strip() -> type&; inline auto stripLeft() -> type&; inline auto stripRight() -> type&; //utility.hpp - inline static auto read(view filename) -> string; - inline static auto repeat(view pattern, uint times) -> string; + inline static auto read(string_view filename) -> string; + inline static auto repeat(string_view pattern, uint times) -> string; inline auto fill(char fill = ' ') -> type&; inline auto hash() const -> uint; inline auto remove(uint offset, uint length) -> type&; @@ -294,14 +297,14 @@ template<> struct vector : vector_base { inline auto append() -> type&; inline auto isort() -> type&; - inline auto find(view source) const -> maybe; - inline auto ifind(view source) const -> maybe; - inline auto match(view pattern) const -> vector; - inline auto merge(view separator) const -> string; + inline auto find(string_view source) const -> maybe; + inline auto ifind(string_view source) const -> maybe; + inline auto match(string_view pattern) const -> vector; + inline auto merge(string_view separator) const -> string; inline auto strip() -> type&; //split.hpp - template inline auto _split(view, view, long) -> type&; + template inline auto _split(string_view, string_view, long) -> type&; }; struct string_format : vector { diff --git a/nall/string/cast.hpp b/nall/string/cast.hpp index 8b17cfaa..f90e741f 100644 --- a/nall/string/cast.hpp +++ b/nall/string/cast.hpp @@ -220,18 +220,18 @@ template<> struct stringify { const string& _text; }; -template<> struct stringify> { - stringify(const view& source) : _view(source) {} +template<> struct stringify { + stringify(const string_view& source) : _view(source) {} auto data() const -> const char* { return _view.data(); } auto size() const -> uint { return _view.size(); } - const view& _view; + const string_view& _view; }; -template<> struct stringify&> { - stringify(const view& source) : _view(source) {} +template<> struct stringify { + stringify(const string_view& source) : _view(source) {} auto data() const -> const char* { return _view.data(); } auto size() const -> uint { return _view.size(); } - const view& _view; + const string_view& _view; }; template<> struct stringify { diff --git a/nall/string/compare.hpp b/nall/string/compare.hpp index 65fecf8c..08479ba2 100644 --- a/nall/string/compare.hpp +++ b/nall/string/compare.hpp @@ -9,48 +9,48 @@ auto string::_compare(const char* target, uint capacity, const char* source, uin } //size() + 1 includes null-terminator; required to properly compare strings of differing lengths -auto string::compare(view x, view y) -> int { +auto string::compare(string_view x, string_view y) -> int { return memory::compare(x.data(), x.size() + 1, y.data(), y.size() + 1); } -auto string::icompare(view x, view y) -> int { +auto string::icompare(string_view x, string_view y) -> int { return memory::icompare(x.data(), x.size() + 1, y.data(), y.size() + 1); } -auto string::compare(view source) const -> int { +auto string::compare(string_view source) const -> int { return memory::compare(data(), size() + 1, source.data(), source.size() + 1); } -auto string::icompare(view source) const -> int { +auto string::icompare(string_view source) const -> int { return memory::icompare(data(), size() + 1, source.data(), source.size() + 1); } -auto string::equals(view source) const -> bool { +auto string::equals(string_view source) const -> bool { if(size() != source.size()) return false; return memory::compare(data(), source.data(), source.size()) == 0; } -auto string::iequals(view source) const -> bool { +auto string::iequals(string_view source) const -> bool { if(size() != source.size()) return false; return memory::icompare(data(), source.data(), source.size()) == 0; } -auto string::beginsWith(view source) const -> bool { +auto string::beginsWith(string_view source) const -> bool { if(source.size() > size()) return false; return memory::compare(data(), source.data(), source.size()) == 0; } -auto string::ibeginsWith(view source) const -> bool { +auto string::ibeginsWith(string_view source) const -> bool { if(source.size() > size()) return false; return memory::icompare(data(), source.data(), source.size()) == 0; } -auto string::endsWith(view source) const -> bool { +auto string::endsWith(string_view source) const -> bool { if(source.size() > size()) return false; return memory::compare(data() + size() - source.size(), source.data(), source.size()) == 0; } -auto string::iendsWith(view source) const -> bool { +auto string::iendsWith(string_view source) const -> bool { if(source.size() > size()) return false; return memory::icompare(data() + size() - source.size(), source.data(), source.size()) == 0; } diff --git a/nall/string/convert.hpp b/nall/string/convert.hpp index 406592dd..4c7d4abf 100644 --- a/nall/string/convert.hpp +++ b/nall/string/convert.hpp @@ -36,7 +36,7 @@ auto string::qupcase() -> string& { return *this; } -auto string::transform(view from, view to) -> string& { +auto string::transform(string_view from, string_view to) -> string& { if(from.size() != to.size() || from.size() == 0) return *this; //patterns must be the same length char* p = get(); for(uint n = 0; n < size(); n++) { diff --git a/nall/string/find.hpp b/nall/string/find.hpp index 4d665539..b926e212 100644 --- a/nall/string/find.hpp +++ b/nall/string/find.hpp @@ -2,7 +2,7 @@ namespace nall { -auto string::contains(view characters) const -> maybe { +auto string::contains(string_view characters) const -> maybe { for(uint x : range(size())) { for(char y : characters) { if(operator[](x) == y) return x; @@ -11,7 +11,7 @@ auto string::contains(view characters) const -> maybe { return nothing; } -template auto string::_find(int offset, view source) const -> maybe { +template auto string::_find(int offset, string_view source) const -> maybe { if(source.size() == 0) return nothing; auto p = data(); @@ -24,12 +24,12 @@ template auto string::_find(int offset, view source) const -> maybe { return _find<0, 0>(0, source); } -auto string::ifind(view source) const -> maybe { return _find<1, 0>(0, source); } -auto string::qfind(view source) const -> maybe { return _find<0, 1>(0, source); } -auto string::iqfind(view source) const -> maybe { return _find<1, 1>(0, source); } +auto string::find(string_view source) const -> maybe { return _find<0, 0>(0, source); } +auto string::ifind(string_view source) const -> maybe { return _find<1, 0>(0, source); } +auto string::qfind(string_view source) const -> maybe { return _find<0, 1>(0, source); } +auto string::iqfind(string_view source) const -> maybe { return _find<1, 1>(0, source); } -auto string::findFrom(int offset, view source) const -> maybe { return _find<0, 0>(offset, source); } -auto string::ifindFrom(int offset, view source) const -> maybe { return _find<1, 0>(offset, source); } +auto string::findFrom(int offset, string_view source) const -> maybe { return _find<0, 0>(offset, source); } +auto string::ifindFrom(int offset, string_view source) const -> maybe { return _find<1, 0>(offset, source); } } diff --git a/nall/string/markup/bml.hpp b/nall/string/markup/bml.hpp index 595afabd..84f08584 100644 --- a/nall/string/markup/bml.hpp +++ b/nall/string/markup/bml.hpp @@ -40,7 +40,7 @@ protected: p += length; } - auto parseData(const char*& p, view spacing) -> void { + auto parseData(const char*& p, string_view spacing) -> void { if(*p == '=' && *(p + 1) == '\"') { uint length = 2; while(p[length] && p[length] != '\n' && p[length] != '\"') length++; @@ -62,7 +62,7 @@ protected: } //read all attributes for a node - auto parseAttributes(const char*& p, view spacing) -> void { + auto parseAttributes(const char*& p, string_view spacing) -> void { while(*p && *p != '\n') { if(*p != ' ') throw "Invalid node name"; while(*p == ' ') p++; //skip excess spaces @@ -80,7 +80,7 @@ protected: } //read a node and all of its child nodes - auto parseNode(const vector& text, uint& y, view spacing) -> void { + auto parseNode(const vector& text, uint& y, string_view spacing) -> void { const char* p = text[y++]; _metadata = parseDepth(p); parseName(p); @@ -105,7 +105,7 @@ protected: } //read top-level nodes - auto parse(string document, view spacing) -> void { + auto parse(string document, string_view spacing) -> void { //in order to simplify the parsing logic; we do an initial pass to normalize the data //the below code will turn '\r\n' into '\n'; skip empty lines; and skip comment lines char* p = document.get(), *output = p; @@ -140,10 +140,10 @@ protected: } } - friend auto unserialize(const string&, view) -> Markup::Node; + friend auto unserialize(const string&, string_view) -> Markup::Node; }; -inline auto unserialize(const string& markup, view spacing = {}) -> Markup::Node { +inline auto unserialize(const string& markup, string_view spacing = {}) -> Markup::Node { SharedNode node(new ManagedNode); try { node->parse(markup, spacing); @@ -153,7 +153,7 @@ inline auto unserialize(const string& markup, view spacing = {}) -> Mark return (Markup::SharedNode&)node; } -inline auto serialize(const Markup::Node& node, view spacing = {}, uint depth = 0) -> string { +inline auto serialize(const Markup::Node& node, string_view spacing = {}, uint depth = 0) -> string { if(!node.name()) { string result; for(auto leaf : node) { diff --git a/nall/string/match.hpp b/nall/string/match.hpp index 0be483df..c1f79695 100644 --- a/nall/string/match.hpp +++ b/nall/string/match.hpp @@ -4,7 +4,7 @@ namespace nall { //todo: these functions are not binary-safe -auto string::match(view source) const -> bool { +auto string::match(string_view source) const -> bool { const char* s = data(); const char* p = source.data(); @@ -28,7 +28,7 @@ auto string::match(view source) const -> bool { return !*p; } -auto string::imatch(view source) const -> bool { +auto string::imatch(string_view source) const -> bool { static auto chrlower = [](char c) -> char { return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; }; diff --git a/nall/string/pascal.hpp b/nall/string/pascal.hpp index 31ca5d63..cadfe767 100644 --- a/nall/string/pascal.hpp +++ b/nall/string/pascal.hpp @@ -54,11 +54,11 @@ struct string_pascal { return *this; } - auto operator==(view source) const -> bool { + auto operator==(string_view source) const -> bool { return size() == source.size() && memory::compare(data(), source.data(), size()) == 0; } - auto operator!=(view source) const -> bool { + auto operator!=(string_view source) const -> bool { return size() != source.size() || memory::compare(data(), source.data(), size()) != 0; } diff --git a/nall/string/replace.hpp b/nall/string/replace.hpp index 078a7e0b..d8143a15 100644 --- a/nall/string/replace.hpp +++ b/nall/string/replace.hpp @@ -3,7 +3,7 @@ namespace nall { template -auto string::_replace(view from, view to, long limit) -> string& { +auto string::_replace(string_view from, string_view to, long limit) -> string& { if(limit <= 0 || from.size() == 0) return *this; int size = this->size(); @@ -86,9 +86,9 @@ auto string::_replace(view from, view to, long limit) -> string& return *this; } -auto string::replace(view from, view to, long limit) -> string& { return _replace<0, 0>(from, to, limit); } -auto string::ireplace(view from, view to, long limit) -> string& { return _replace<1, 0>(from, to, limit); } -auto string::qreplace(view from, view to, long limit) -> string& { return _replace<0, 1>(from, to, limit); } -auto string::iqreplace(view from, view to, long limit) -> string& { return _replace<1, 1>(from, to, limit); } +auto string::replace(string_view from, string_view to, long limit) -> string& { return _replace<0, 0>(from, to, limit); } +auto string::ireplace(string_view from, string_view to, long limit) -> string& { return _replace<1, 0>(from, to, limit); } +auto string::qreplace(string_view from, string_view to, long limit) -> string& { return _replace<0, 1>(from, to, limit); } +auto string::iqreplace(string_view from, string_view to, long limit) -> string& { return _replace<1, 1>(from, to, limit); } }; diff --git a/nall/string/split.hpp b/nall/string/split.hpp index f6a14cbf..0ceba68b 100644 --- a/nall/string/split.hpp +++ b/nall/string/split.hpp @@ -3,7 +3,7 @@ namespace nall { template -auto vector::_split(view source, view find, long limit) -> type& { +auto vector::_split(string_view source, string_view find, long limit) -> type& { reset(); if(limit <= 0 || find.size() == 0) return *this; @@ -33,9 +33,9 @@ auto vector::_split(view source, view find, long limit) return *this; } -auto string::split(view on, long limit) const -> vector { return vector()._split<0, 0>(*this, on, limit); } -auto string::isplit(view on, long limit) const -> vector { return vector()._split<1, 0>(*this, on, limit); } -auto string::qsplit(view on, long limit) const -> vector { return vector()._split<0, 1>(*this, on, limit); } -auto string::iqsplit(view on, long limit) const -> vector { return vector()._split<1, 1>(*this, on, limit); } +auto string::split(string_view on, long limit) const -> vector { return vector()._split<0, 0>(*this, on, limit); } +auto string::isplit(string_view on, long limit) const -> vector { return vector()._split<1, 0>(*this, on, limit); } +auto string::qsplit(string_view on, long limit) const -> vector { return vector()._split<0, 1>(*this, on, limit); } +auto string::iqsplit(string_view on, long limit) const -> vector { return vector()._split<1, 1>(*this, on, limit); } } diff --git a/nall/string/trim.hpp b/nall/string/trim.hpp index 952a12fa..17823a7d 100644 --- a/nall/string/trim.hpp +++ b/nall/string/trim.hpp @@ -2,13 +2,13 @@ namespace nall { -auto string::trim(view lhs, view rhs, long limit) -> string& { +auto string::trim(string_view lhs, string_view rhs, long limit) -> string& { trimRight(rhs, limit); trimLeft(lhs, limit); return *this; } -auto string::trimLeft(view lhs, long limit) -> string& { +auto string::trimLeft(string_view lhs, long limit) -> string& { if(lhs.size() == 0) return *this; long matches = 0; while(matches < limit) { @@ -22,7 +22,7 @@ auto string::trimLeft(view lhs, long limit) -> string& { return *this; } -auto string::trimRight(view rhs, long limit) -> string& { +auto string::trimRight(string_view rhs, long limit) -> string& { if(rhs.size() == 0) return *this; long matches = 0; while(matches < limit) { @@ -36,13 +36,13 @@ auto string::trimRight(view rhs, long limit) -> string& { return *this; } -auto string::itrim(view lhs, view rhs, long limit) -> string& { +auto string::itrim(string_view lhs, string_view rhs, long limit) -> string& { itrimRight(rhs, limit); itrimLeft(lhs, limit); return *this; } -auto string::itrimLeft(view lhs, long limit) -> string& { +auto string::itrimLeft(string_view lhs, long limit) -> string& { if(lhs.size() == 0) return *this; long matches = 0; while(matches < limit) { @@ -56,7 +56,7 @@ auto string::itrimLeft(view lhs, long limit) -> string& { return *this; } -auto string::itrimRight(view rhs, long limit) -> string& { +auto string::itrimRight(string_view rhs, long limit) -> string& { if(rhs.size() == 0) return *this; long matches = 0; while(matches < limit) { diff --git a/nall/string/utility.hpp b/nall/string/utility.hpp index d020dad6..000bdd18 100644 --- a/nall/string/utility.hpp +++ b/nall/string/utility.hpp @@ -2,7 +2,7 @@ namespace nall { -auto string::read(view filename) -> string { +auto string::read(string_view filename) -> string { #if !defined(_WIN32) FILE* fp = fopen(filename, "rb"); #else @@ -22,7 +22,7 @@ auto string::read(view filename) -> string { return fclose(fp), result; } -auto string::repeat(view pattern, uint times) -> string { +auto string::repeat(string_view pattern, uint times) -> string { string result; while(times--) result.append(pattern.data()); return result; @@ -82,7 +82,7 @@ auto string::size(int length, char fill) -> string& { return *this; } -auto slice(view self, int offset, int length) -> string { +auto slice(string_view self, int offset, int length) -> string { string result; if(offset < 0) offset = self.size() - abs(offset); if(offset >= 0 && offset < self.size()) { diff --git a/nall/string/vector.hpp b/nall/string/vector.hpp index 0182eeda..31f20635 100644 --- a/nall/string/vector.hpp +++ b/nall/string/vector.hpp @@ -19,21 +19,21 @@ auto vector::isort() -> type& { return *this; } -auto vector::find(view source) const -> maybe { +auto vector::find(string_view source) const -> maybe { for(uint n = 0; n < size(); n++) { if(operator[](n).equals(source)) return n; } return {}; } -auto vector::ifind(view source) const -> maybe { +auto vector::ifind(string_view source) const -> maybe { for(uint n = 0; n < size(); n++) { if(operator[](n).iequals(source)) return n; } return {}; } -auto vector::match(view pattern) const -> vector { +auto vector::match(string_view pattern) const -> vector { vector result; for(uint n = 0; n < size(); n++) { if(operator[](n).match(pattern)) result.append(operator[](n)); @@ -41,7 +41,7 @@ auto vector::match(view pattern) const -> vector { return result; } -auto vector::merge(view separator) const -> string { +auto vector::merge(string_view separator) const -> string { string output; for(uint n = 0; n < size(); n++) { output.append(operator[](n)); diff --git a/nall/string/view.hpp b/nall/string/view.hpp index fa6b5380..c60aa683 100644 --- a/nall/string/view.hpp +++ b/nall/string/view.hpp @@ -2,20 +2,20 @@ namespace nall { -view::view() { +string_view::string_view() { _string = nullptr; _data = ""; _size = 0; } -view::view(const view& source) { +string_view::string_view(const string_view& source) { if(this == &source) return; _string = nullptr; _data = source._data; _size = source._size; } -view::view(view&& source) { +string_view::string_view(string_view&& source) { if(this == &source) return; _string = source._string; _data = source._data; @@ -23,36 +23,36 @@ view::view(view&& source) { source._string = nullptr; } -view::view(const char* data) { +string_view::string_view(const char* data) { _string = nullptr; _data = data; _size = -1; //defer length calculation, as it is often unnecessary } -view::view(const char* data, uint size) { +string_view::string_view(const char* data, uint size) { _string = nullptr; _data = data; _size = size; } -view::view(const string& source) { +string_view::string_view(const string& source) { _string = nullptr; _data = source.data(); _size = source.size(); } template -view::view(P&&... p) { +string_view::string_view(P&&... p) { _string = new string{forward

(p)...}; _data = _string->data(); _size = _string->size(); } -view::~view() { +string_view::~string_view() { if(_string) delete _string; } -auto view::operator=(const view& source) -> view& { +auto string_view::operator=(const string_view& source) -> type& { if(this == &source) return *this; _string = nullptr; _data = source._data; @@ -60,7 +60,7 @@ auto view::operator=(const view& source) -> view& { return *this; }; -auto view::operator=(view&& source) -> view& { +auto string_view::operator=(string_view&& source) -> type& { if(this == &source) return *this; _string = source._string; _data = source._data; @@ -69,15 +69,15 @@ auto view::operator=(view&& source) -> view& { return *this; }; -view::operator const char*() const { +string_view::operator const char*() const { return _data; } -auto view::data() const -> const char* { +auto string_view::data() const -> const char* { return _data; } -auto view::size() const -> uint { +auto string_view::size() const -> uint { if(_size < 0) _size = strlen(_data); return _size; } diff --git a/nall/suffix-array.hpp b/nall/suffix-array.hpp index 4ff51503..afe3ce51 100644 --- a/nall/suffix-array.hpp +++ b/nall/suffix-array.hpp @@ -1,202 +1,341 @@ #pragma once +#include #include -#include +#include #include +#include namespace nall { /* input: - data = "acaaacatat" + data = "acaacatat" + 0 "acaacatat" + 1 "caacatat" + 2 "aacatat" + 3 "acatat" + 4 "catat" + 5 "atat" + 6 "tat" + 7 "at" + 8 "t" + 9 "" suffix_array: - suffixes = [2, 3, 0, 4, 8, 6, 1, 5, 9, 7] - 2 "aaaacatat" - 3 "aacatat" - 0 "acaaacatat" - 4 "acatat" - 8 "at" - 6 "atat" - 1 "caaacatat" - 5 "catat" - 9 "t" - 7 "tat" + suffixes = [9,2,0,3,7,5,1,4,8,6] => input + suffixes: + 9 "" + 2 "aacatat" + 0 "acaacatat" + 3 "acatat" + 7 "at" + 5 "atat" + 1 "caacatat" + 4 "catat" + 8 "t" + 6 "tat" -suffix_array_inv: - inverted = [2, 6, 0, 1, 3, 7, 5, 9, 4, 8] +[auxiliary data structures to represent information lost from suffix trees] + +suffix_array_invert: + inverted = [2,6,1,3,7,5,9,4,8,0] => input + suffixes[inverted]: + 2 "acaacatat" + 6 "caacatat" + 1 "aacatat" + 3 "acatat" + 7 "catat" + 5 "atat" + 9 "tat" + 4 "at" + 8 "t" + 0 "" suffix_array_lcp: - prefixes = [-, 2, 1, 3, 1, 2, 0, 2, 0, 1] - "aaaacatat" - - "aacatat" 2 "aa" - "acaaacatat" 1 "a" - "acatat" 3 "aca" - "at" 1 "a" - "atat" 2 "at" - "caaacatat" 0 - "catat" 2 "ca" - "t" 0 - "tat" 1 "t" + prefixes = [0,1,3,1,2,0,2,0,1] => lcp[n] == lcp(n, n-1) + "" - + "aacatat" 0 + "acaacatat" 1 "a" + "acatat" 3 "aca" + "at" 1 "a" + "atat" 2 "at" + "caacatat" 0 + "catat" 2 "ca" + "t" 0 + "tat" 1 "t" + +suffix_array_lrcp: + llcp = [0,0,0,3,1,0,0,0,0,1] => llcp[m] == lcp(l, m) + rlcp = [0,1,1,1,2,0,2,0,0,0] => rlcp[m] == lcp(m, r) suffix_array_phi: - phi = [3, 6, -, 2, 0, 1, 8, 9, 4, 5] + phi = [2,5,9,0,1,7,8,3,4] -suffix_array_lps: - lengths = [-, 0, 1, 2, 3, 2, 1, 0, 2, 1] - offsets = [-, -, 0, 2, 0, 1, 4, -, 6, 7] - "acaaacatat" (-,-) - "caaacatat" (0,-) - "aaacatat" (1,0) at 0, match "a" - "aacatat" (2,2) at 2, match "aa" - "acatat" (3,0) at 0, match "aca" - "catat" (2,1) at 1, match "ca" - "atat" (1,4) at 4, match "a" (not 0) - "tat" (0,-) - "at" (2,6) at 6, match "at" - "t" (1,7) at 7, match "a" (not 0) +suffix_array_lpf: + lengths = [0,0,1,3,2,1,0,2,1,0] + offsets = [0,0,0,0,1,3,4,5,6,2] + "acaacatat" (0,-) + "caacatat" (0,-) + "aacatat" (1,0) at 0, match "a" + "acatat" (3,0) at 0, match "aca" + "catat" (2,1) at 1, match "ca" + "atat" (1,3) at 3, match "a" + "tat" (0,-) + "at" (2,5) at 5, match "at" + "t" (1,6) at 6, match "t" + "" (0,-) */ -// O(n log n) -inline auto suffix_array(int* suffixes, const uint8_t* data, int size) -> void { - for(int n : range(size)) suffixes[n] = n; - #if 1 - div_suf_sort(suffixes, data, size); - #else - sort(suffixes, size, [&](int lhs, int rhs) -> bool { - return memory::compare(data + lhs, size - lhs, data + rhs, size - rhs) < 0; - }); - #endif +// via induced sorting +// O(n) +inline auto suffix_array(array_view input) -> vector { + return induced_sort(input.data(), input.size()); } // inverse // O(n) -inline auto suffix_array_inv(int* inverted, const int* suffixes, int size) -> void { - for(int i : range(size)) inverted[suffixes[i]] = i; +inline auto suffix_array_invert(array_view suffixes) -> vector { + vector inverted; + inverted.reset(), inverted.reallocate(suffixes.size()); + for(int n : range(suffixes.size())) inverted[suffixes[n]] = n; + return inverted; } -// longest common prefix +// longest common prefix: lcp[n] == lcp(n, n-1) +// algorithm: kasai // O(n) -inline auto suffix_array_lcp(int* prefixes, const int* inverted, const int* suffixes, const uint8_t* data, int size) -> void { - prefixes[0] = -1; +inline auto suffix_array_lcp(array_view suffixes, array_view inverted, array_view input) -> vector { + int size = input.size(); + vector prefixes; + prefixes.reset(), prefixes.reallocate(size); for(int i = 0, l = 0; i < size; i++) { - if(inverted[i] == size - 1) { l = 0; continue; } + if(inverted[i] == size) { l = 0; continue; } //the next substring is empty; ignore it int j = suffixes[inverted[i] + 1]; - while(i + l < size && j + l < size && data[i + l] == data[j + l]) l++; - prefixes[1 + inverted[i]] = l; + while(i + l < size && j + l < size && input[i + l] == input[j + l]) l++; + prefixes[inverted[i]] = l; if(l) l--; } + return prefixes; } +// longest common prefixes - left + right +// llcp[m] == lcp(l, m) +// rlcp[m] == lcp(m, r) // O(n) -inline auto suffix_array_phi(int* phi, const int* suffixes, int size) -> void { - phi[suffixes[0]] = -1; - for(int i : range(1, size)) phi[suffixes[i]] = suffixes[i - 1]; +inline auto suffix_array_lrcp(vector& llcp, vector& rlcp, array_view lcp, array_view suffixes, array_view input) -> void { + llcp.reset(), llcp.reallocate(lcp.size() + 1); + rlcp.reset(), rlcp.reallocate(lcp.size() + 1); + + function recurse = [&](int l, int r) -> int { + if(l == lcp.size()) return 0; + if(l == r - 1) return lcp[l]; + int m = l + r >> 1; + llcp[m] = recurse(l, m); + rlcp[m] = recurse(m, r); + return min(llcp[m], rlcp[m]); + }; + recurse(0, lcp.size() + 1); + llcp[0] = 0; + rlcp[0] = 0; } -// longest previous string (longest previous factor) +// auxiliary data for suffix_array_lpf // O(n) -inline auto suffix_array_lps(int* lengths, int* offsets, const int* phi, const uint8_t* data, int size) -> void { - function sop = [&](int i, int l, int j) -> void { +inline auto suffix_array_phi(array_view suffixes) -> vector { + vector phi; + phi.reset(), phi.reallocate(suffixes.size() - 1); + for(int i : range(1, suffixes.size())) { + phi[suffixes[i]] = suffixes[i - 1]; + } + return phi; +} + +// longest previous factor +// O(n) +inline auto suffix_array_lpf(vector& lengths, vector& offsets, array_view phi, array_view input) -> void { + int l = 0, size = input.size(); + lengths.reset(), lengths.resize(size + 1, -1); + offsets.reset(), offsets.resize(size + 1, -1); + + function recurse = [&](int i, int l, int j) -> void { if(lengths[i] < 0) { lengths[i] = l; offsets[i] = j; } else { if(lengths[i] < l) { if(offsets[i] > j) { - sop(offsets[i], lengths[i], j); + recurse(offsets[i], lengths[i], j); } else { - sop(j, lengths[i], offsets[i]); + recurse(j, lengths[i], offsets[i]); } lengths[i] = l; offsets[i] = j; } else { if(offsets[i] > j) { - sop(offsets[i], l, j); + recurse(offsets[i], l, j); } else { - sop(j, l, offsets[i]); + recurse(j, l, offsets[i]); } } } }; - int l = 0; - for(int i : range(size)) lengths[i] = -1; for(int i : range(size)) { int j = phi[i]; - while(i + l < size && j + l < size && data[i + l] == data[j + l]) l++; + while(i + l < size && j + l < size && input[i + l] == input[j + l]) l++; if(i > j) { - sop(i, l, j); + recurse(i, l, j); } else { - sop(j, l, i); + recurse(j, l, i); } if(l) l--; } - lengths[0] = -1; + + //there can be no previous factor for the start of input; clear these values from -1 to 0 + lengths[0] = 0; + offsets[0] = 0; } -//partial_suffix_array computes a suffix array in O(n) time by only sorting by SuffixBits into each prefix -//this is much faster than a proper suffix_array, but at the penalty of not being 100% sorted -//thus, least common prefixes cannot readily be used with this; deduplication is suggested for binary searching -//suffixes[] = (offsets) list of indexes into data[] in sorted order -//prefixes[] = (values) sorted list of data[] - -template -inline auto partial_suffix_array(uint* suffixes, uint* prefixes, const void* data, uint64_t size, uint offset = 0) -> void; - -template<> -inline auto partial_suffix_array<32, 24>(uint* suffixes, uint* prefixes, const void* data, uint64_t size, uint offset) -> void { - auto input = (const uint8_t*)data; - if(size == 0 || !data || !suffixes || !prefixes) return; - if(size == 1) return suffixes[0] = offset << 16, prefixes[0] = input[0], void(); - - auto elements = new uint64_t[2 * size], lhs = &elements[0], rhs = &elements[size]; - for(uint index : range(size - 2)) { - elements[index] = index | uint64_t(input[0] << 16 | input[1] << 8 | input[2] << 0) << 32, input++; +// longest common prefix: lcp(l, r) +// O(n) +inline auto suffix_array_lcp(int l, int r, array_view suffixes, array_view input) -> int { + int k = 0, size = input.size(); + l = suffixes[l], r = suffixes[r]; + while(l + k < size && r + k < size) { + if(input[l + k] != input[r + k]) break; + k++; } - elements[size - 2] = size - 2 | uint64_t(input[0] << 16 | input[1] << 8) << 32, input++; - elements[size - 1] = size - 1 | uint64_t(input[0] << 16) << 32; - - counting_sort<12, 32>(rhs, lhs, size); - counting_sort<12, 44>(lhs, rhs, size); - for(uint n : range(size)) { - suffixes[n] = (uint32_t)lhs[n] + offset; - prefixes[n] = lhs[n] >> 32; - } - - delete[] elements; + return k; } -template<> -inline auto partial_suffix_array<32, 32>(uint* suffixes, uint* prefixes, const void* data, uint64_t size, uint offset) -> void { - auto input = (const uint8_t*)data; - if(size == 0 || !data || !suffixes || !prefixes) return; - if(size == 1) return suffixes[0] = offset, prefixes[0] = input[0], void(); - if(size == 2) { - suffixes[0] = offset, suffixes[1] = 1 + offset; - prefixes[0] = input[0] << 24 | input[1] << 16, prefixes[1] = input[1] << 24; - if(input[0] >= input[1]) swap(suffixes[0], suffixes[1]), swap(prefixes[0], prefixes[1]); - return; +// O(n log m) +inline auto suffix_array_find(int& length, int& offset, array_view suffixes, array_view input, array_view match) -> bool { + length = 0, offset = 0; + int l = 0, r = input.size(); + + while(l < r - 1) { + int m = l + r >> 1; + int s = suffixes[m]; + + int k = 0; + while(k < match.size() && s + k < input.size()) { + if(match[k] != input[s + k]) break; + k++; + } + + if(k > length) { + length = k; + offset = s; + if(k == match.size()) return true; + } + + if(match[k] < input[s + k]) { + r = m; + } else { + l = m; + } } - auto elements = new uint64_t[2 * size], lhs = &elements[0], rhs = &elements[size]; - for(uint index : range(size - 3)) { - elements[index] = index | uint64_t(input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3] << 0) << 32, input++; - } - elements[size - 3] = size - 3 | uint64_t(input[0] << 24 | input[1] << 16 | input[2] << 8) << 32, input++; - elements[size - 2] = size - 2 | uint64_t(input[0] << 24 | input[1] << 16) << 32, input++; - elements[size - 1] = size - 1 | uint64_t(input[0] << 24) << 32; - - counting_sort<16, 32>(rhs, lhs, size); - counting_sort<16, 48>(lhs, rhs, size); - for(uint n : range(size)) { - suffixes[n] = (uint32_t)lhs[n] + offset; - prefixes[n] = lhs[n] >> 32; - } - - delete[] elements; + return false; } +// O(n + log m) +inline auto suffix_array_find(int& length, int& offset, array_view llcp, array_view rlcp, array_view suffixes, array_view input, array_view match) -> bool { + length = 0, offset = 0; + int l = 0, r = input.size(), k = 0; + + while(l < r - 1) { + int m = l + r >> 1; + int s = suffixes[m]; + + while(k < match.size() && s + k < input.size()) { + if(match[k] != input[s + k]) break; + k++; + } + + if(k > length) { + length = k; + offset = s; + if(k == match.size()) return true; + } + + if(match[k] < input[s + k]) { + r = m; + k = min(k, llcp[m]); + } else { + l = m; + k = min(k, rlcp[m]); + } + } + + return false; +} + +// + +struct SuffixArray { + using type = SuffixArray; + + //O(n) + inline SuffixArray(array_view input) : input(input) { + suffixes = suffix_array(input); + } + + //O(n) + inline auto lcp() -> type& { + inverted = suffix_array_invert(suffixes); + prefixes = suffix_array_lcp(suffixes, inverted, input); + return *this; + } + + //O(n) + inline auto lrcp() -> type& { + lcp(); + suffix_array_lrcp(prefixesL, prefixesR, prefixes, suffixes, input); + return *this; + } + + //O(n) + inline auto lpf() -> type& { + auto phi = suffix_array_phi(suffixes); + suffix_array_lpf(lengths, offsets, phi, input); + return *this; + } + + inline auto operator[](int offset) const -> int { + return suffixes[offset]; + } + + //O(n log m) + //inline auto find(int& length, int& offset, array_view match) -> bool { + // return suffix_array_find(length, offset, suffixes, input, match); + //} + + //requires: lrcp() + //O(n + log m) + inline auto find(int& length, int& offset, array_view match) -> bool { + return suffix_array_find(length, offset, prefixesL, prefixesR, suffixes, input, match); + } + + //requires: lpf() + //O(n) + inline auto previous(int& length, int& offset, int address) -> void { + length = lengths[address]; + offset = offsets[address]; + } + + //non-owning reference: SuffixArray is invalidated if memory is freed + array_view input; + + //suffix array and auxiliary data structures + vector suffixes; //suffix array + vector inverted; //inverted suffix array + vector prefixes; //longest common prefixes - lcp(n, n-1) + vector prefixesL; //longest common prefixes - lcp(l, m) + vector prefixesR; //longest common prefixes - lcp(m, r) + vector lengths; //longest previous factors + vector offsets; //longest previous factors +}; + } diff --git a/nall/vector.hpp b/nall/vector.hpp index 2f3ba982..73f4e6ed 100644 --- a/nall/vector.hpp +++ b/nall/vector.hpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -11,14 +12,10 @@ #include #include #include +#include namespace nall { -template struct vector_iterator; -template struct vector_iterator_const; -template struct vector_iterator_reverse; -template struct vector_iterator_reverse_const; - template struct vector_base { using type = vector_base; @@ -33,10 +30,11 @@ struct vector_base { ~vector_base(); explicit operator bool() const; + operator array_view() const; template auto capacity() const -> uint; template auto size() const -> uint; - template auto data(uint offset = 0) -> Cast*; - template auto data(uint offset = 0) const -> const Cast*; + template auto data() -> Cast*; + template auto data() const -> const Cast*; //assign.hpp auto operator=(const type& source) -> type&; @@ -48,12 +46,17 @@ struct vector_base { //memory.hpp auto reset() -> void; + auto acquire(const T* data, uint size, uint capacity = 0) -> void; auto release() -> T*; auto reserveLeft(uint capacity) -> bool; auto reserveRight(uint capacity) -> bool; auto reserve(uint capacity) -> bool { return reserveRight(capacity); } + auto reallocateLeft(uint size) -> bool; + auto reallocateRight(uint size) -> bool; + auto reallocate(uint size) -> bool { return reallocateRight(size); } + auto resizeLeft(uint size, const T& value = T()) -> bool; auto resizeRight(uint size, const T& value = T()) -> bool; auto resize(uint size, const T& value = T()) -> bool { return resizeRight(size, value); } @@ -66,15 +69,13 @@ struct vector_base { alwaysinline auto operator()(uint offset, const T& value) const -> const T&; alwaysinline auto left() -> T&; - alwaysinline auto left() const -> const T&; - - alwaysinline auto right() -> T&; - alwaysinline auto right() const -> const T&; - alwaysinline auto first() -> T& { return left(); } + alwaysinline auto left() const -> const T&; alwaysinline auto first() const -> const T& { return left(); } + alwaysinline auto right() -> T&; alwaysinline auto last() -> T& { return right(); } + alwaysinline auto right() const -> const T&; alwaysinline auto last() const -> const T& { return right(); } //modify.hpp @@ -91,11 +92,15 @@ struct vector_base { auto insert(uint offset, const T& value) -> void; auto removeLeft(uint length = 1) -> void; + auto removeFirst(uint length = 1) -> void { return removeLeft(length); } auto removeRight(uint length = 1) -> void; + auto removeLast(uint length = 1) -> void { return removeRight(length); } auto remove(uint offset, uint length = 1) -> void; auto takeLeft() -> T; + auto takeFirst() -> T { return move(takeLeft()); } auto takeRight() -> T; + auto takeLast() -> T { return move(takeRight()); } auto take(uint offset) -> T; //iterator.hpp @@ -112,6 +117,7 @@ struct vector_base { auto rend() const -> reverse_iterator_const { return {data(), (uint)-1}; } //utility.hpp + auto fill(const T& value = {}) -> void; auto sort(const function& comparator = [](auto& lhs, auto& rhs) { return lhs < rhs; }) -> void; auto find(const function& comparator) -> maybe; auto find(const T& value) const -> maybe; diff --git a/nall/vector/core.hpp b/nall/vector/core.hpp index fffcda03..9cfcc9e4 100644 --- a/nall/vector/core.hpp +++ b/nall/vector/core.hpp @@ -31,6 +31,10 @@ template vector::operator bool() const { return _size; } +template vector::operator array_view() const { + return {data(), size()}; +} + template template auto vector::capacity() const -> uint { return (_left + _size + _right) * sizeof(T) / sizeof(Cast); } @@ -39,12 +43,12 @@ template template auto vector::size() const -> uin return _size * sizeof(T) / sizeof(Cast); } -template template auto vector::data(uint offset) -> Cast* { - return (Cast*)_pool + offset; +template template auto vector::data() -> Cast* { + return (Cast*)_pool; } -template template auto vector::data(uint offset) const -> const Cast* { - return (const Cast*)_pool + offset; +template template auto vector::data() const -> const Cast* { + return (const Cast*)_pool; } } diff --git a/nall/vector/memory.hpp b/nall/vector/memory.hpp index a73532b3..0b62499c 100644 --- a/nall/vector/memory.hpp +++ b/nall/vector/memory.hpp @@ -2,6 +2,9 @@ namespace nall { +//nall::vector acts internally as a deque (double-ended queue) +//it does this because it's essentially free to do so, only costing an extra integer in sizeof(vector) + template auto vector::reset() -> void { if(!_pool) return; @@ -14,6 +17,18 @@ template auto vector::reset() -> void { _right = 0; } +//acquire ownership of allocated memory + +template auto vector::acquire(const T* data, uint size, uint capacity) -> void { + reset(); + _pool = data; + _size = size; + _left = 0; + _right = capacity ? capacity : size; +} + +//release ownership of allocated memory + template auto vector::release() -> T* { auto pool = _pool; _pool = nullptr; @@ -23,6 +38,11 @@ template auto vector::release() -> T* { return pool; } +//reserve allocates memory for objects, but does not initialize them +//when the vector desired size is known, this can be used to avoid growing the capacity dynamically +//reserve will not actually shrink the capacity, only expand it +//shrinking the capacity would destroy objects, and break amortized growth with reallocate and resize + template auto vector::reserveLeft(uint capacity) -> bool { if(_size + _left >= capacity) return false; @@ -51,6 +71,43 @@ template auto vector::reserveRight(uint capacity) -> bool { return true; } +//reallocation is meant for POD types, to avoid the overhead of initialization +//do not use with non-POD types, or they will not be properly constructed or destructed + +template auto vector::reallocateLeft(uint size) -> bool { + if(size < _size) { //shrink + _pool += _size - size; + _left += _size - size; + _size = size; + return true; + } + if(size > _size) { //grow + reserveLeft(size); + _pool -= size - _size; + _left -= size - _size; + _size = size; + return true; + } + return false; +} + +template auto vector::reallocateRight(uint size) -> bool { + if(size < _size) { //shrink + _right += _size - size; + _size = size; + return true; + } + if(size > _size) { //grow + reserveRight(size); + _right -= size - _size; + _size = size; + return true; + } + return false; +} + +//resize is meant for non-POD types, and will properly construct objects + template auto vector::resizeLeft(uint size, const T& value) -> bool { if(size < _size) { //shrink for(uint n : range(_size - size)) _pool[n].~T(); diff --git a/nall/vector/utility.hpp b/nall/vector/utility.hpp index 1a4c1214..e2ab3c4e 100644 --- a/nall/vector/utility.hpp +++ b/nall/vector/utility.hpp @@ -2,6 +2,10 @@ namespace nall { +template auto vector::fill(const T& value) -> void { + for(uint n : range(size())) _pool[n] = value; +} + template auto vector::sort(const function& comparator) -> void { nall::sort(_pool, _size, comparator); } diff --git a/ruby/audio/openal.cpp b/ruby/audio/openal.cpp index da6a50ec..0044c969 100644 --- a/ruby/audio/openal.cpp +++ b/ruby/audio/openal.cpp @@ -8,7 +8,7 @@ struct AudioOpenAL : AudioDriver { AudioOpenAL& self = *this; - AudioOpenAL(Audio& driver) : AudioDriver(super) {} + AudioOpenAL(Audio& super) : AudioDriver(super) {} ~AudioOpenAL() { terminate(); } auto create() -> bool override { diff --git a/ruby/input/quartz.cpp b/ruby/input/quartz.cpp index 67fa04c1..e20b73f0 100644 --- a/ruby/input/quartz.cpp +++ b/ruby/input/quartz.cpp @@ -30,7 +30,7 @@ private: auto initialize() -> bool { terminate(); if(!keyboard.initialize()) return false; - return _ready = true; + return isReady = true; } auto terminate() -> void { diff --git a/ruby/video/cgl.cpp b/ruby/video/cgl.cpp index 4a1f7a3f..5213cd89 100644 --- a/ruby/video/cgl.cpp +++ b/ruby/video/cgl.cpp @@ -36,7 +36,7 @@ struct VideoCGL : VideoDriver, OpenGL { if(!view) return true; @autoreleasepool { [[view openGLContext] makeCurrentContext]; - int blocking = _blocking; + int blocking = self.blocking; [[view openGLContext] setValues:&blocking forParameter:NSOpenGLCPSwapInterval]; } return true; diff --git a/ruby/video/direct3d.cpp b/ruby/video/direct3d.cpp index 2d22d236..83dbd926 100644 --- a/ruby/video/direct3d.cpp +++ b/ruby/video/direct3d.cpp @@ -30,7 +30,6 @@ struct VideoDirect3D : VideoDriver { auto setShader(string shader) -> bool override { return updateFilter(); } auto clear() -> void override { - if(!ready()) return; if(_lost && !recover()) return; D3DSURFACE_DESC surfaceDescription; @@ -38,7 +37,10 @@ struct VideoDirect3D : VideoDriver { _texture->GetSurfaceLevel(0, &_surface); if(_surface) { - _device->ColorFill(_surface, 0, D3DCOLOR_XRGB(0x00, 0x00, 0x00)); + D3DLOCKED_RECT lockedRectangle; + _surface->LockRect(&lockedRectangle, 0, D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD); + memory::fill(lockedRectangle.pBits, lockedRectangle.Pitch * surfaceDescription.Height); + _surface->UnlockRect(); _surface->Release(); _surface = nullptr; } diff --git a/ruby/video/xshm.cpp b/ruby/video/xshm.cpp index f7e85b66..3d0b7676 100644 --- a/ruby/video/xshm.cpp +++ b/ruby/video/xshm.cpp @@ -10,8 +10,8 @@ struct VideoXShm : VideoDriver { VideoXShm& self = *this; - VideoXShm(Video& super) : VideoDriver(super) {} - ~VideoXShm() { terminate(); } + VideoXShm(Video& super) : VideoDriver(super) { construct(); } + ~VideoXShm() { destruct(); } auto create() -> bool override { return initialize(); @@ -29,6 +29,8 @@ struct VideoXShm : VideoDriver { auto setShader(string shader) -> bool override { return true; } auto configure(uint width, uint height, double inputFrequency, double outputFrequency) -> bool override { + if(width == _outputWidth && height == _outputHeight) return true; + _outputWidth = width; _outputHeight = height; XResizeWindow(_display, _window, _outputWidth, _outputHeight); @@ -42,7 +44,7 @@ struct VideoXShm : VideoDriver { XShmAttach(_display, &_shmInfo); _outputBuffer = (uint32_t*)_shmInfo.shmaddr; _image = XShmCreateImage(_display, _visual, _depth, ZPixmap, _shmInfo.shmaddr, &_shmInfo, _outputWidth, _outputHeight); - return true; + return (bool)_image; } auto clear() -> void override { @@ -69,8 +71,6 @@ struct VideoXShm : VideoDriver { } auto output() -> void override { - size(); - float xratio = (float)_inputWidth / (float)_outputWidth; float yratio = (float)_inputHeight / (float)_outputHeight; @@ -106,7 +106,7 @@ struct VideoXShm : VideoDriver { XEvent event; XNextEvent(_display, &event); if(event.type == Expose) { - XWindowAttributes attributes; + XWindowAttributes attributes{}; XGetWindowAttributes(_display, _window, &attributes); super.doUpdate(attributes.width, attributes.height); } @@ -114,14 +114,21 @@ struct VideoXShm : VideoDriver { } private: + auto construct() -> void { + _display = XOpenDisplay(nullptr); + _screen = DefaultScreen(_display); + XSetErrorHandler(errorHandler); + } + + auto destruct() -> void { + XCloseDisplay(_display); + } + auto initialize() -> bool { terminate(); if(!self.context) return false; - _display = XOpenDisplay(nullptr); - _screen = DefaultScreen(_display); - - XWindowAttributes getAttributes; + XWindowAttributes getAttributes{}; XGetWindowAttributes(_display, (Window)self.context, &getAttributes); _depth = getAttributes.depth; _visual = getAttributes.visual; @@ -149,21 +156,11 @@ private: XNextEvent(_display, &event); } - if(!size()) return false; return _ready = true; } auto terminate() -> void { free(); - if(_display) { - XCloseDisplay(_display); - _display = nullptr; - } - } - - auto size() -> bool { - - return true; } auto free() -> void { @@ -185,6 +182,12 @@ private: return cr << 16 | cg << 8 | cb << 0; } + static auto errorHandler(Display* display, XErrorEvent* event) -> int { + //catch occasional BadAccess errors during window resize events + //currently, I'm unsure of the cause, but they're certainly not fatal + return 0; + } + bool _ready = false; uint32_t* _inputBuffer = nullptr;