Update to v106r59 release.

byuu says:

Changelog:

  - fixed bug in Emulator::Game::Memory::operator bool()
  - nall: renamed view<string> 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]
This commit is contained in:
Tim Allen 2018-08-26 16:49:54 +10:00
parent f9adb4d2c6
commit bd814f0358
89 changed files with 1079 additions and 2241 deletions

View File

@ -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/";

View File

@ -6,14 +6,14 @@ struct Game {
struct Memory;
struct Oscillator;
inline auto load(view<string>) -> void;
inline auto load(string_view) -> void;
inline auto memory(Markup::Node) -> maybe<Memory>;
inline auto oscillator(natural = 0) -> maybe<Oscillator>;
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<Oscillator> oscillatorList;
};
auto Game::load(view<string> text) -> void {
auto Game::load(string_view text) -> void {
document = BML::unserialize(text);
sha256 = document["game/sha256"].text();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -46,7 +46,8 @@ auto Interface::unload() -> void {
}
auto Interface::ports() -> vector<Port> { return {
{ID::Port::Hardware, "Hardware"}};
{ID::Port::Hardware, "Hardware"},
{ID::Port::Cartridge, "Cartridge"}};
}
auto Interface::devices(uint port) -> vector<Device> {
@ -54,6 +55,11 @@ auto Interface::devices(uint port) -> vector<Device> {
{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<Input> {
{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 {};

View File

@ -12,10 +12,13 @@ struct ID {
struct Port { enum : uint {
Hardware,
Cartridge,
};};
struct Device { enum : uint {
Controls,
MBC5,
MBC7,
};};
};

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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

View File

@ -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 {

View File

@ -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();

View File

@ -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<uint8_t>& 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);

View File

@ -108,14 +108,16 @@ auto Program::applyPatchBPS(vector<uint8_t>& 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;

View File

@ -117,7 +117,7 @@ public:
};
struct SuperFamicom : Game {
string label;
string title;
vector<uint8_t> program;
vector<uint8_t> data;
vector<uint8_t> expansion;

View File

@ -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<uint8_t> 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<uint8_t> saveState;

View File

@ -158,7 +158,7 @@ auto StateManager::updateSelection() -> void {
uint preview = memory::readl<sizeof(uint)>(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();

View File

@ -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 {

View File

@ -89,12 +89,11 @@ Program::Program(vector<string> 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<string> arguments) {
auto Program::main() -> void {
updateStatusText();
video->poll();
inputManager->poll();
inputManager->pollHotkeys();

View File

@ -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();
}

View File

@ -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;
}

View File

@ -76,7 +76,7 @@ auto pTableViewColumn::setWidth(signed width) -> void {
}
auto pTableViewColumn::_parent() -> maybe<pTableView&> {
if(auto parent = self().parentTableViewHeader()) {
if(auto parent = self().parentTableView()) {
if(auto self = parent->self()) return *self;
}
return {};

View File

@ -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;
}

View File

@ -20,10 +20,10 @@
if(parent) (*parent)->append(*this, std::forward<P>(p)...); \
} \
template<typename T> auto is() -> bool { \
return dynamic_cast<typename T::internalType*>(data()); \
return dynamic_cast<typename T::internalType*>(s##Name::data()); \
} \
template<typename T> auto cast() -> T { \
if(auto pointer = dynamic_cast<typename T::internalType*>(data())) { \
if(auto pointer = dynamic_cast<typename T::internalType*>(s##Name::data())) { \
if(auto shared = pointer->instance.acquire()) return T(shared); \
} \
return T(); \

View File

@ -193,6 +193,8 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
window.setDismissable();
window.setVisible();
view.setFocused();
Application::processEvents();
view->resizeColumns();
window.setModal();
window.setVisible(false);

View File

@ -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();
}

View File

@ -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");

View File

@ -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") {

View File

@ -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";
}

60
nall/array-view.hpp Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <nall/iterator.hpp>
namespace nall {
struct string;
template<typename T> struct vector;
template<typename T> struct array;
template<typename T> 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<typename U = T> inline auto data() const -> const U* { return (const U*)_data; }
template<typename U = T> inline auto size() const -> uint64_t { return _size * sizeof(T) / sizeof(U); }
inline auto begin() const -> iterator_const<T> { return {_data, (uint)0}; }
inline auto end() const -> iterator_const<T> { return {_data, (uint)_size}; }
inline auto rbegin() const -> reverse_iterator_const<T> { return {_data, (uint)_size - 1}; }
inline auto rend() const -> reverse_iterator_const<T> { return {_data, (uint)-1}; }
protected:
const T* _data;
int _size;
};
}

View File

@ -1,6 +1,8 @@
#pragma once
#include <nall/array-view.hpp>
#include <nall/range.hpp>
#include <nall/view.hpp>
namespace nall {
@ -17,6 +19,10 @@ template<typename T, uint Size> struct array<T[Size]> {
}
}
operator array_view<T>() const {
return {data(), size()};
}
alwaysinline auto operator[](uint index) -> T& {
#ifdef DEBUG
struct out_of_bounds {};

View File

@ -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<string&> manifest = {}, maybe<string&> result = {}
) -> maybe<vector<uint8_t>> {
inline auto apply(array_view<uint8_t> source, array_view<uint8_t> beat, maybe<string&> manifest = {}, maybe<string&> result = {}) -> maybe<vector<uint8_t>> {
#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<uint8_t> targetData;
vector<uint8_t> 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

View File

@ -1,18 +1,14 @@
#pragma once
#include <nall/encode/dictionary.hpp>
#include <nall/suffix-array.hpp>
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<uint8_t>> {
vector<uint8_t> beatData;
inline auto create(array_view<uint8_t> source, array_view<uint8_t> target, string_view manifest = {}) -> vector<uint8_t> {
vector<uint8_t> 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;
}
}}}

View File

@ -6,8 +6,7 @@
namespace nall { namespace Decode {
inline auto BWT(const void* data) -> vector<uint8_t> {
auto input = (const uint8_t*)data;
inline auto BWT(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> output;
uint size = 0;
@ -17,14 +16,11 @@ inline auto BWT(const void* data) -> vector<uint8_t> {
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<uint8_t> {
return output;
}
template<typename T>
inline auto BWT(const vector<T>& buffer) -> vector<uint8_t> {
return move(BWT(buffer.data()));
}
}}

View File

@ -2,8 +2,7 @@
namespace nall { namespace Decode {
inline auto Huffman(const void* data) -> vector<uint8_t> {
auto input = (const uint8_t*)data;
inline auto Huffman(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> output;
uint size = 0;
@ -34,9 +33,4 @@ inline auto Huffman(const void* data) -> vector<uint8_t> {
return output;
}
template<typename T>
inline auto Huffman(const vector<T>& buffer) -> vector<uint8_t> {
return move(Huffman(buffer.data()));
}
}}

View File

@ -25,7 +25,6 @@ inline auto LZSA(const void* data) -> vector<uint8_t> {
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());

View File

@ -1,44 +0,0 @@
#pragma once
namespace nall { namespace Decode {
inline auto LZSS(const void* data) -> vector<uint8_t> {
vector<uint8_t> 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;
}
}}

View File

@ -4,25 +4,19 @@
namespace nall { namespace Decode {
inline auto MTF(const void* data, uint size) -> vector<uint8_t> {
auto input = (const uint8_t*)data;
inline auto MTF(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> 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;

View File

@ -3,14 +3,11 @@
namespace nall { namespace Decode {
template<uint S = 1, uint M = 4 / S> //S = word size; M = match length
inline auto RLE(const void* data, uint remaining = ~0) -> vector<uint8_t> {
inline auto RLE(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> 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;

View File

@ -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];

File diff suppressed because it is too large Load Diff

View File

@ -6,44 +6,81 @@
namespace nall { namespace Encode {
inline auto BWT(const void* data, uint size) -> vector<uint8_t> {
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<uint8_t> input) -> vector<uint8_t> {
auto size = input.size();
vector<uint8_t> 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<uint8_t> 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<int> 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<typename T>
inline auto BWT(const vector<T>& buffer) -> vector<uint8_t> {
return move(BWT(buffer.data(), buffer.size() * sizeof(T)));
}
}}

View File

@ -1,73 +0,0 @@
#pragma once
#include <nall/suffix-array.hpp>
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;
}
}}

View File

@ -2,10 +2,9 @@
namespace nall { namespace Encode {
inline auto Huffman(const void* data, uint size) -> vector<uint8_t> {
auto input = (const uint8_t*)data;
inline auto Huffman(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> 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<uint8_t> {
uint rhs = 0;
};
array<Node[512]> 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<uint8_t> {
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<uint8_t> {
return output;
}
template<typename T>
inline auto Huffman(const vector<T>& buffer) -> vector<uint8_t> {
return move(Huffman(buffer.data(), buffer.size() * sizeof(T)));
}
}}

View File

@ -8,22 +8,12 @@
namespace nall { namespace Encode {
inline auto LZSA(const void* data, uint64_t size) -> vector<uint8_t> {
inline auto LZSA(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> 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<uint8_t> flags;
vector<uint8_t> literals;
vector<uint8_t> stringLengths;
@ -55,13 +45,13 @@ inline auto LZSA(const void* data, uint64_t size) -> vector<uint8_t> {
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<uint8_t> {
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;
}

View File

@ -1,76 +0,0 @@
#pragma once
#include <nall/encode/dictionary.hpp>
namespace nall { namespace Encode {
inline auto LZSS(const void* data, uint64_t size, uint windowBits = 16, uint lengthBits = 8) -> vector<uint8_t> {
vector<uint8_t> 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;
}
}}

View File

@ -4,17 +4,16 @@
namespace nall { namespace Encode {
inline auto MTF(const void* data, uint size) -> vector<uint8_t> {
auto input = (const uint8_t*)data;
inline auto MTF(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> 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<uint8_t> {
return output;
}
template<typename T>
inline auto MTF(const vector<T>& buffer) -> vector<uint8_t> {
return move(MTF(buffer.data(), buffer.size() * sizeof(T)));
}
}}

View File

@ -3,17 +3,15 @@
namespace nall { namespace Encode {
template<uint S = 1, uint M = 4 / S> //S = word size; M = match length
inline auto RLE(const void* data, uint64_t size) -> vector<uint8_t> {
inline auto RLE(array_view<uint8_t> input) -> vector<uint8_t> {
vector<uint8_t> 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<uint8_t> {
} 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<uint8_t> {
return output;
}
template<uint S = 1, uint M = 4 / S, typename T>
inline auto RLE(const vector<T>& buffer) -> vector<uint8_t> {
return move(RLE<S, M>(buffer.data(), buffer.size() * sizeof(T)));
}
}}

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -5,7 +5,12 @@
namespace nall { namespace Hash {
struct CRC16 : Hash {
nallHash(CRC16)
using Hash::input;
CRC16(array_view<uint8_t> buffer = {}) {
reset();
input(buffer);
}
auto reset() -> void override {
checksum = ~0;

View File

@ -5,7 +5,12 @@
namespace nall { namespace Hash {
struct CRC32 : Hash {
nallHash(CRC32)
using Hash::input;
CRC32(array_view<uint8_t> buffer = {}) {
reset();
input(buffer);
}
auto reset() -> void override {
checksum = ~0;

View File

@ -5,7 +5,12 @@
namespace nall { namespace Hash {
struct CRC64 : Hash {
nallHash(CRC64)
using Hash::input;
CRC64(array_view<uint8_t> buffer = {}) {
reset();
input(buffer);
}
auto reset() -> void override {
checksum = ~0;

View File

@ -20,6 +20,10 @@ struct Hash {
virtual auto input(uint8_t data) -> void = 0;
virtual auto output() const -> vector<uint8_t> = 0;
auto input(array_view<uint8_t> 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++);

View File

@ -5,7 +5,12 @@
namespace nall { namespace Hash {
struct SHA224 : Hash {
nallHash(SHA224)
using Hash::input;
SHA224(array_view<uint8_t> buffer = {}) {
reset();
input(buffer);
}
auto reset() -> void override {
for(auto& n : queue) n = 0;

View File

@ -5,7 +5,12 @@
namespace nall { namespace Hash {
struct SHA256 : Hash {
nallHash(SHA256)
using Hash::input;
SHA256(array_view<uint8_t> buffer = {}) {
reset();
input(buffer);
}
auto reset() -> void override {
for(auto& n : queue) n = 0;

View File

@ -5,7 +5,12 @@
namespace nall { namespace Hash {
struct SHA384 : Hash {
nallHash(SHA384)
using Hash::input;
SHA384(array_view<uint8_t> buffer = {}) {
reset();
input(buffer);
}
auto reset() -> void override {
for(auto& n : queue) n = 0;

View File

@ -5,7 +5,12 @@
namespace nall { namespace Hash {
struct SHA512 : Hash {
nallHash(SHA512)
using Hash::input;
SHA512(array_view<uint8_t> buffer = {}) {
reset();
input(buffer);
}
auto reset() -> void override {
for(auto& n : queue) n = 0;

175
nall/induced-sort.hpp Normal file
View File

@ -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<typename T>
inline auto induced_sort(const T* data, const uint size, const uint characters = 256) -> vector<int> {
if(size == 0) return vector<int>{0}; //required to avoid out-of-bounds accesses
if(size == 1) return vector<int>{1, 0}; //not strictly necessary; but more performant
vector<bool> 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<uint> counts;
counts.resize(characters);
for(uint n : range(size)) counts[data[n]]++;
//bucket sorting start offsets
vector<uint> 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<uint> 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<int> 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<int> 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<int> summaryOffsets;
vector<int> 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<int> 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;
}
}

View File

@ -4,7 +4,7 @@ namespace nall { namespace Location {
// (/parent/child.type/)
// (/parent/child.type/)name.type
inline auto path(view<string> 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<string> self) -> string {
// /parent/child.type/()
// /parent/child.type/(name.type)
inline auto file(view<string> 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<string> self) -> string {
// (/parent/)child.type/
// (/parent/child.type/)name.type
inline auto dir(view<string> 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<string> self) -> string {
// /parent/(child.type/)
// /parent/child.type/(name.type)
inline auto base(view<string> 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<string> self) -> string {
// /parent/(child).type/
// /parent/child.type/(name).type
inline auto prefix(view<string> 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<string> self) -> string {
// /parent/child(.type)/
// /parent/child.type/name(.type)
inline auto suffix(view<string> 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<string> self) -> string {
return ""; //no suffix found
}
inline auto notsuffix(view<string> self) -> string {
inline auto notsuffix(string_view self) -> string {
return {path(self), prefix(self)};
}

View File

@ -17,6 +17,7 @@
#include <nall/any.hpp>
#include <nall/arithmetic.hpp>
#include <nall/array.hpp>
#include <nall/array-view.hpp>
#include <nall/atoi.hpp>
#include <nall/bit.hpp>
#include <nall/bit-field.hpp>

View File

@ -14,7 +14,7 @@ inline auto active() -> string {
return result;
}
inline auto real(view<string> 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("\\", "/"));

View File

@ -11,7 +11,7 @@ template<typename T> struct shared_pointer;
struct shared_pointer_manager {
void* pointer = nullptr;
function<auto (void*) -> void> deleter;
function<void (void*)> deleter;
uint strong = 0;
uint weak = 0;
@ -41,7 +41,9 @@ struct shared_pointer {
shared_pointer(T* source, const function<void (T*)>& deleter) {
operator=(source);
manager->deleter = [=](void* p) { deleter((T*)p); };
manager->deleter = function<void (void*)>([=](void* p) {
deleter((T*)p);
});
}
shared_pointer(const shared_pointer& source) {

View File

@ -10,6 +10,7 @@
#include <memory>
#include <nall/platform.hpp>
#include <nall/array-view.hpp>
#include <nall/atoi.hpp>
#include <nall/function.hpp>
#include <nall/intrinsics.hpp>
@ -27,21 +28,21 @@ namespace nall {
struct string;
struct string_format;
template<> struct view<string> {
using type = view<string>;
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<typename... P> 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<typename... P> 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<string>& list, const char* s, const char* p) -> bool;
//utility.hpp
inline auto slice(view<string> self, int offset = 0, int length = -1) -> string;
inline auto slice(string_view self, int offset = 0, int length = -1) -> string;
template<typename T> inline auto fromInteger(char* result, T value) -> char*;
template<typename T> inline auto fromNatural(char* result, T value) -> char*;
template<typename T> 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<char>() const { return {(const char*)data(), size()}; }
operator array_view<uint8_t>() 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<string> source) const -> bool { return compare(source) == 0; }
auto operator!=(view<string> source) const -> bool { return compare(source) != 0; }
auto operator< (view<string> source) const -> bool { return compare(source) < 0; }
auto operator<=(view<string> source) const -> bool { return compare(source) <= 0; }
auto operator> (view<string> source) const -> bool { return compare(source) > 0; }
auto operator>=(view<string> 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<string> characters) const -> maybe<uint>;
inline auto contains(string_view characters) const -> maybe<uint>;
template<bool, bool> inline auto _find(int, view<string>) const -> maybe<uint>;
template<bool, bool> inline auto _find(int, string_view) const -> maybe<uint>;
inline auto find(view<string> source) const -> maybe<uint>;
inline auto ifind(view<string> source) const -> maybe<uint>;
inline auto qfind(view<string> source) const -> maybe<uint>;
inline auto iqfind(view<string> source) const -> maybe<uint>;
inline auto find(string_view source) const -> maybe<uint>;
inline auto ifind(string_view source) const -> maybe<uint>;
inline auto qfind(string_view source) const -> maybe<uint>;
inline auto iqfind(string_view source) const -> maybe<uint>;
inline auto findFrom(int offset, view<string> source) const -> maybe<uint>;
inline auto ifindFrom(int offset, view<string> source) const -> maybe<uint>;
inline auto findFrom(int offset, string_view source) const -> maybe<uint>;
inline auto ifindFrom(int offset, string_view source) const -> maybe<uint>;
//format.hpp
inline auto format(const nall::string_format& params) -> type&;
@ -211,20 +214,20 @@ public:
//compare.hpp
template<bool> inline static auto _compare(const char*, uint, const char*, uint) -> int;
inline static auto compare(view<string>, view<string>) -> int;
inline static auto icompare(view<string>, view<string>) -> int;
inline static auto compare(string_view, string_view) -> int;
inline static auto icompare(string_view, string_view) -> int;
inline auto compare(view<string> source) const -> int;
inline auto icompare(view<string> source) const -> int;
inline auto compare(string_view source) const -> int;
inline auto icompare(string_view source) const -> int;
inline auto equals(view<string> source) const -> bool;
inline auto iequals(view<string> source) const -> bool;
inline auto equals(string_view source) const -> bool;
inline auto iequals(string_view source) const -> bool;
inline auto beginsWith(view<string> source) const -> bool;
inline auto ibeginsWith(view<string> source) const -> bool;
inline auto beginsWith(string_view source) const -> bool;
inline auto ibeginsWith(string_view source) const -> bool;
inline auto endsWith(view<string> source) const -> bool;
inline auto iendsWith(view<string> 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<string> from, view<string>to) -> type&;
inline auto transform(string_view from, string_view to) -> type&;
//match.hpp
inline auto match(view<string> source) const -> bool;
inline auto imatch(view<string> source) const -> bool;
inline auto match(string_view source) const -> bool;
inline auto imatch(string_view source) const -> bool;
//replace.hpp
template<bool, bool> inline auto _replace(view<string>, view<string>, long) -> type&;
inline auto replace(view<string> from, view<string> to, long limit = LONG_MAX) -> type&;
inline auto ireplace(view<string> from, view<string> to, long limit = LONG_MAX) -> type&;
inline auto qreplace(view<string> from, view<string> to, long limit = LONG_MAX) -> type&;
inline auto iqreplace(view<string> from, view<string> to, long limit = LONG_MAX) -> type&;
template<bool, bool> 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<string> key, long limit = LONG_MAX) const -> vector<string>;
inline auto isplit(view<string> key, long limit = LONG_MAX) const -> vector<string>;
inline auto qsplit(view<string> key, long limit = LONG_MAX) const -> vector<string>;
inline auto iqsplit(view<string> key, long limit = LONG_MAX) const -> vector<string>;
inline auto split(string_view key, long limit = LONG_MAX) const -> vector<string>;
inline auto isplit(string_view key, long limit = LONG_MAX) const -> vector<string>;
inline auto qsplit(string_view key, long limit = LONG_MAX) const -> vector<string>;
inline auto iqsplit(string_view key, long limit = LONG_MAX) const -> vector<string>;
//trim.hpp
inline auto trim(view<string> lhs, view<string> rhs, long limit = LONG_MAX) -> type&;
inline auto trimLeft(view<string> lhs, long limit = LONG_MAX) -> type&;
inline auto trimRight(view<string> 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<string> lhs, view<string> rhs, long limit = LONG_MAX) -> type&;
inline auto itrimLeft(view<string> lhs, long limit = LONG_MAX) -> type&;
inline auto itrimRight(view<string> 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<string> filename) -> string;
inline static auto repeat(view<string> 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<string> : vector_base<string> {
inline auto append() -> type&;
inline auto isort() -> type&;
inline auto find(view<string> source) const -> maybe<uint>;
inline auto ifind(view<string> source) const -> maybe<uint>;
inline auto match(view<string> pattern) const -> vector<string>;
inline auto merge(view<string> separator) const -> string;
inline auto find(string_view source) const -> maybe<uint>;
inline auto ifind(string_view source) const -> maybe<uint>;
inline auto match(string_view pattern) const -> vector<string>;
inline auto merge(string_view separator) const -> string;
inline auto strip() -> type&;
//split.hpp
template<bool, bool> inline auto _split(view<string>, view<string>, long) -> type&;
template<bool, bool> inline auto _split(string_view, string_view, long) -> type&;
};
struct string_format : vector<string> {

View File

@ -220,18 +220,18 @@ template<> struct stringify<const string&> {
const string& _text;
};
template<> struct stringify<view<string>> {
stringify(const view<string>& source) : _view(source) {}
template<> struct stringify<string_view> {
stringify(const string_view& source) : _view(source) {}
auto data() const -> const char* { return _view.data(); }
auto size() const -> uint { return _view.size(); }
const view<string>& _view;
const string_view& _view;
};
template<> struct stringify<const view<string>&> {
stringify(const view<string>& source) : _view(source) {}
template<> struct stringify<const string_view&> {
stringify(const string_view& source) : _view(source) {}
auto data() const -> const char* { return _view.data(); }
auto size() const -> uint { return _view.size(); }
const view<string>& _view;
const string_view& _view;
};
template<> struct stringify<string_pascal> {

View File

@ -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<string> x, view<string> 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<string> x, view<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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;
}

View File

@ -36,7 +36,7 @@ auto string::qupcase() -> string& {
return *this;
}
auto string::transform(view<string> from, view<string> 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++) {

View File

@ -2,7 +2,7 @@
namespace nall {
auto string::contains(view<string> characters) const -> maybe<uint> {
auto string::contains(string_view characters) const -> maybe<uint> {
for(uint x : range(size())) {
for(char y : characters) {
if(operator[](x) == y) return x;
@ -11,7 +11,7 @@ auto string::contains(view<string> characters) const -> maybe<uint> {
return nothing;
}
template<bool Insensitive, bool Quoted> auto string::_find(int offset, view<string> source) const -> maybe<uint> {
template<bool Insensitive, bool Quoted> auto string::_find(int offset, string_view source) const -> maybe<uint> {
if(source.size() == 0) return nothing;
auto p = data();
@ -24,12 +24,12 @@ template<bool Insensitive, bool Quoted> auto string::_find(int offset, view<stri
return nothing;
}
auto string::find(view<string> source) const -> maybe<uint> { return _find<0, 0>(0, source); }
auto string::ifind(view<string> source) const -> maybe<uint> { return _find<1, 0>(0, source); }
auto string::qfind(view<string> source) const -> maybe<uint> { return _find<0, 1>(0, source); }
auto string::iqfind(view<string> source) const -> maybe<uint> { return _find<1, 1>(0, source); }
auto string::find(string_view source) const -> maybe<uint> { return _find<0, 0>(0, source); }
auto string::ifind(string_view source) const -> maybe<uint> { return _find<1, 0>(0, source); }
auto string::qfind(string_view source) const -> maybe<uint> { return _find<0, 1>(0, source); }
auto string::iqfind(string_view source) const -> maybe<uint> { return _find<1, 1>(0, source); }
auto string::findFrom(int offset, view<string> source) const -> maybe<uint> { return _find<0, 0>(offset, source); }
auto string::ifindFrom(int offset, view<string> source) const -> maybe<uint> { return _find<1, 0>(offset, source); }
auto string::findFrom(int offset, string_view source) const -> maybe<uint> { return _find<0, 0>(offset, source); }
auto string::ifindFrom(int offset, string_view source) const -> maybe<uint> { return _find<1, 0>(offset, source); }
}

View File

@ -40,7 +40,7 @@ protected:
p += length;
}
auto parseData(const char*& p, view<string> 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<string> 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<string>& text, uint& y, view<string> spacing) -> void {
auto parseNode(const vector<string>& 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<string> 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<string>) -> Markup::Node;
friend auto unserialize(const string&, string_view) -> Markup::Node;
};
inline auto unserialize(const string& markup, view<string> 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<string> spacing = {}) -> Mark
return (Markup::SharedNode&)node;
}
inline auto serialize(const Markup::Node& node, view<string> 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) {

View File

@ -4,7 +4,7 @@ namespace nall {
//todo: these functions are not binary-safe
auto string::match(view<string> 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<string> source) const -> bool {
return !*p;
}
auto string::imatch(view<string> 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;
};

View File

@ -54,11 +54,11 @@ struct string_pascal {
return *this;
}
auto operator==(view<string> source) const -> bool {
auto operator==(string_view source) const -> bool {
return size() == source.size() && memory::compare(data(), source.data(), size()) == 0;
}
auto operator!=(view<string> source) const -> bool {
auto operator!=(string_view source) const -> bool {
return size() != source.size() || memory::compare(data(), source.data(), size()) != 0;
}

View File

@ -3,7 +3,7 @@
namespace nall {
template<bool Insensitive, bool Quoted>
auto string::_replace(view<string> from, view<string> 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<string> from, view<string> to, long limit) -> string&
return *this;
}
auto string::replace(view<string> from, view<string> to, long limit) -> string& { return _replace<0, 0>(from, to, limit); }
auto string::ireplace(view<string> from, view<string> to, long limit) -> string& { return _replace<1, 0>(from, to, limit); }
auto string::qreplace(view<string> from, view<string> to, long limit) -> string& { return _replace<0, 1>(from, to, limit); }
auto string::iqreplace(view<string> from, view<string> 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); }
};

View File

@ -3,7 +3,7 @@
namespace nall {
template<bool Insensitive, bool Quoted>
auto vector<string>::_split(view<string> source, view<string> find, long limit) -> type& {
auto vector<string>::_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<string>::_split(view<string> source, view<string> find, long limit)
return *this;
}
auto string::split(view<string> on, long limit) const -> vector<string> { return vector<string>()._split<0, 0>(*this, on, limit); }
auto string::isplit(view<string> on, long limit) const -> vector<string> { return vector<string>()._split<1, 0>(*this, on, limit); }
auto string::qsplit(view<string> on, long limit) const -> vector<string> { return vector<string>()._split<0, 1>(*this, on, limit); }
auto string::iqsplit(view<string> on, long limit) const -> vector<string> { return vector<string>()._split<1, 1>(*this, on, limit); }
auto string::split(string_view on, long limit) const -> vector<string> { return vector<string>()._split<0, 0>(*this, on, limit); }
auto string::isplit(string_view on, long limit) const -> vector<string> { return vector<string>()._split<1, 0>(*this, on, limit); }
auto string::qsplit(string_view on, long limit) const -> vector<string> { return vector<string>()._split<0, 1>(*this, on, limit); }
auto string::iqsplit(string_view on, long limit) const -> vector<string> { return vector<string>()._split<1, 1>(*this, on, limit); }
}

View File

@ -2,13 +2,13 @@
namespace nall {
auto string::trim(view<string> lhs, view<string> 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<string> 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<string> lhs, long limit) -> string& {
return *this;
}
auto string::trimRight(view<string> 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<string> rhs, long limit) -> string& {
return *this;
}
auto string::itrim(view<string> lhs, view<string> 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<string> 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<string> lhs, long limit) -> string& {
return *this;
}
auto string::itrimRight(view<string> 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) {

View File

@ -2,7 +2,7 @@
namespace nall {
auto string::read(view<string> 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<string> filename) -> string {
return fclose(fp), result;
}
auto string::repeat(view<string> 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<string> 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()) {

View File

@ -19,21 +19,21 @@ auto vector<string>::isort() -> type& {
return *this;
}
auto vector<string>::find(view<string> source) const -> maybe<uint> {
auto vector<string>::find(string_view source) const -> maybe<uint> {
for(uint n = 0; n < size(); n++) {
if(operator[](n).equals(source)) return n;
}
return {};
}
auto vector<string>::ifind(view<string> source) const -> maybe<uint> {
auto vector<string>::ifind(string_view source) const -> maybe<uint> {
for(uint n = 0; n < size(); n++) {
if(operator[](n).iequals(source)) return n;
}
return {};
}
auto vector<string>::match(view<string> pattern) const -> vector<string> {
auto vector<string>::match(string_view pattern) const -> vector<string> {
vector<string> result;
for(uint n = 0; n < size(); n++) {
if(operator[](n).match(pattern)) result.append(operator[](n));
@ -41,7 +41,7 @@ auto vector<string>::match(view<string> pattern) const -> vector<string> {
return result;
}
auto vector<string>::merge(view<string> separator) const -> string {
auto vector<string>::merge(string_view separator) const -> string {
string output;
for(uint n = 0; n < size(); n++) {
output.append(operator[](n));

View File

@ -2,20 +2,20 @@
namespace nall {
view<string>::view() {
string_view::string_view() {
_string = nullptr;
_data = "";
_size = 0;
}
view<string>::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<string>::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<string>::view(view&& source) {
source._string = nullptr;
}
view<string>::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<string>::view(const char* data, uint size) {
string_view::string_view(const char* data, uint size) {
_string = nullptr;
_data = data;
_size = size;
}
view<string>::view(const string& source) {
string_view::string_view(const string& source) {
_string = nullptr;
_data = source.data();
_size = source.size();
}
template<typename... P>
view<string>::view(P&&... p) {
string_view::string_view(P&&... p) {
_string = new string{forward<P>(p)...};
_data = _string->data();
_size = _string->size();
}
view<string>::~view() {
string_view::~string_view() {
if(_string) delete _string;
}
auto view<string>::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<string>::operator=(const view& source) -> view& {
return *this;
};
auto view<string>::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<string>::operator=(view&& source) -> view& {
return *this;
};
view<string>::operator const char*() const {
string_view::operator const char*() const {
return _data;
}
auto view<string>::data() const -> const char* {
auto string_view::data() const -> const char* {
return _data;
}
auto view<string>::size() const -> uint {
auto string_view::size() const -> uint {
if(_size < 0) _size = strlen(_data);
return _size;
}

View File

@ -1,202 +1,341 @@
#pragma once
#include <nall/array.hpp>
#include <nall/counting-sort.hpp>
#include <nall/div-suf-sort.hpp>
#include <nall/induced-sort.hpp>
#include <nall/range.hpp>
#include <nall/view.hpp>
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<uint8_t> input) -> vector<int> {
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<int> suffixes) -> vector<int> {
vector<int> 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<int> suffixes, array_view<int> inverted, array_view<uint8_t> input) -> vector<int> {
int size = input.size();
vector<int> 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<int>& llcp, vector<int>& rlcp, array_view<int> lcp, array_view<int> suffixes, array_view<uint8_t> input) -> void {
llcp.reset(), llcp.reallocate(lcp.size() + 1);
rlcp.reset(), rlcp.reallocate(lcp.size() + 1);
function<int (int, int)> 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<void (int, int, int)> sop = [&](int i, int l, int j) -> void {
inline auto suffix_array_phi(array_view<int> suffixes) -> vector<int> {
vector<int> 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<int>& lengths, vector<int>& offsets, array_view<int> phi, array_view<uint8_t> input) -> void {
int l = 0, size = input.size();
lengths.reset(), lengths.resize(size + 1, -1);
offsets.reset(), offsets.resize(size + 1, -1);
function<void (int, int, int)> 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<uint SuffixBits, uint PrefixBits>
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<int> suffixes, array_view<uint8_t> 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<int> suffixes, array_view<uint8_t> input, array_view<uint8_t> 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<int> llcp, array_view<int> rlcp, array_view<int> suffixes, array_view<uint8_t> input, array_view<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> input;
//suffix array and auxiliary data structures
vector<int> suffixes; //suffix array
vector<int> inverted; //inverted suffix array
vector<int> prefixes; //longest common prefixes - lcp(n, n-1)
vector<int> prefixesL; //longest common prefixes - lcp(l, m)
vector<int> prefixesR; //longest common prefixes - lcp(m, r)
vector<int> lengths; //longest previous factors
vector<int> offsets; //longest previous factors
};
}

View File

@ -2,6 +2,7 @@
#include <new>
#include <nall/array-view.hpp>
#include <nall/bit.hpp>
#include <nall/function.hpp>
#include <nall/iterator.hpp>
@ -11,14 +12,10 @@
#include <nall/merge-sort.hpp>
#include <nall/range.hpp>
#include <nall/traits.hpp>
#include <nall/view.hpp>
namespace nall {
template<typename T> struct vector_iterator;
template<typename T> struct vector_iterator_const;
template<typename T> struct vector_iterator_reverse;
template<typename T> struct vector_iterator_reverse_const;
template<typename T>
struct vector_base {
using type = vector_base;
@ -33,10 +30,11 @@ struct vector_base {
~vector_base();
explicit operator bool() const;
operator array_view<T>() const;
template<typename Cast = T> auto capacity() const -> uint;
template<typename Cast = T> auto size() const -> uint;
template<typename Cast = T> auto data(uint offset = 0) -> Cast*;
template<typename Cast = T> auto data(uint offset = 0) const -> const Cast*;
template<typename Cast = T> auto data() -> Cast*;
template<typename Cast = T> 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<T> { return {data(), (uint)-1}; }
//utility.hpp
auto fill(const T& value = {}) -> void;
auto sort(const function<bool (const T& lhs, const T& rhs)>& comparator = [](auto& lhs, auto& rhs) { return lhs < rhs; }) -> void;
auto find(const function<bool (const T& lhs)>& comparator) -> maybe<uint>;
auto find(const T& value) const -> maybe<uint>;

View File

@ -31,6 +31,10 @@ template<typename T> vector<T>::operator bool() const {
return _size;
}
template<typename T> vector<T>::operator array_view<T>() const {
return {data(), size()};
}
template<typename T> template<typename Cast> auto vector<T>::capacity() const -> uint {
return (_left + _size + _right) * sizeof(T) / sizeof(Cast);
}
@ -39,12 +43,12 @@ template<typename T> template<typename Cast> auto vector<T>::size() const -> uin
return _size * sizeof(T) / sizeof(Cast);
}
template<typename T> template<typename Cast> auto vector<T>::data(uint offset) -> Cast* {
return (Cast*)_pool + offset;
template<typename T> template<typename Cast> auto vector<T>::data() -> Cast* {
return (Cast*)_pool;
}
template<typename T> template<typename Cast> auto vector<T>::data(uint offset) const -> const Cast* {
return (const Cast*)_pool + offset;
template<typename T> template<typename Cast> auto vector<T>::data() const -> const Cast* {
return (const Cast*)_pool;
}
}

View File

@ -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<typename T> auto vector<T>::reset() -> void {
if(!_pool) return;
@ -14,6 +17,18 @@ template<typename T> auto vector<T>::reset() -> void {
_right = 0;
}
//acquire ownership of allocated memory
template<typename T> auto vector<T>::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<typename T> auto vector<T>::release() -> T* {
auto pool = _pool;
_pool = nullptr;
@ -23,6 +38,11 @@ template<typename T> auto vector<T>::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<typename T> auto vector<T>::reserveLeft(uint capacity) -> bool {
if(_size + _left >= capacity) return false;
@ -51,6 +71,43 @@ template<typename T> auto vector<T>::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<typename T> auto vector<T>::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<typename T> auto vector<T>::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<typename T> auto vector<T>::resizeLeft(uint size, const T& value) -> bool {
if(size < _size) { //shrink
for(uint n : range(_size - size)) _pool[n].~T();

View File

@ -2,6 +2,10 @@
namespace nall {
template<typename T> auto vector<T>::fill(const T& value) -> void {
for(uint n : range(size())) _pool[n] = value;
}
template<typename T> auto vector<T>::sort(const function<bool (const T& lhs, const T& rhs)>& comparator) -> void {
nall::sort(_pool, _size, comparator);
}

View File

@ -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 {

View File

@ -30,7 +30,7 @@ private:
auto initialize() -> bool {
terminate();
if(!keyboard.initialize()) return false;
return _ready = true;
return isReady = true;
}
auto terminate() -> void {

View File

@ -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;

View File

@ -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;
}

View File

@ -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;