mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
f9adb4d2c6
commit
bd814f0358
|
@ -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/";
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -12,10 +12,13 @@ struct ID {
|
|||
|
||||
struct Port { enum : uint {
|
||||
Hardware,
|
||||
Cartridge,
|
||||
};};
|
||||
|
||||
struct Device { enum : uint {
|
||||
Controls,
|
||||
MBC5,
|
||||
MBC7,
|
||||
};};
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -117,7 +117,7 @@ public:
|
|||
};
|
||||
|
||||
struct SuperFamicom : Game {
|
||||
string label;
|
||||
string title;
|
||||
vector<uint8_t> program;
|
||||
vector<uint8_t> data;
|
||||
vector<uint8_t> expansion;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(); \
|
||||
|
|
|
@ -193,6 +193,8 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
|
|||
window.setDismissable();
|
||||
window.setVisible();
|
||||
view.setFocused();
|
||||
Application::processEvents();
|
||||
view->resizeColumns();
|
||||
window.setModal();
|
||||
window.setVisible(false);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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 {};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}}}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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)));
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}}
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}}
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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++);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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)};
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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("\\", "/"));
|
||||
|
|
|
@ -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) {
|
||||
|
|
127
nall/string.hpp
127
nall/string.hpp
|
@ -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> {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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); }
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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); }
|
||||
|
||||
};
|
||||
|
|
|
@ -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); }
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -30,7 +30,7 @@ private:
|
|||
auto initialize() -> bool {
|
||||
terminate();
|
||||
if(!keyboard.initialize()) return false;
|
||||
return _ready = true;
|
||||
return isReady = true;
|
||||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue