Serialize SDD1 decompressor
Major speedup to nall/serializer [Alcaro]
Removed fast PPU tile cache (major speedup for run-ahead mode)
This commit is contained in:
byuu 2019-10-16 16:12:28 +09:00
parent 53f8de6ac3
commit 6b7e6e01bb
30 changed files with 262 additions and 167 deletions

View File

@ -42,8 +42,6 @@ else ifneq ($(filter $(platform),linux bsd),)
flags += -fPIC
options += -shared
endif
else
$(error "unsupported platform")
endif
objects := libco emulator filter lzma

View File

@ -1,5 +1,7 @@
#pragma once
#include <libco/libco.h>
#include <nall/platform.hpp>
#include <nall/adaptive-array.hpp>
#include <nall/any.hpp>
@ -20,8 +22,6 @@
#include <nall/hash/sha256.hpp>
using namespace nall;
#include <libco/libco.h>
#include <emulator/types.hpp>
#include <emulator/memory/readable.hpp>
#include <emulator/memory/writable.hpp>
@ -29,7 +29,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "bsnes";
static const string Version = "111.8";
static const string Version = "111.9";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org";

View File

@ -3,6 +3,7 @@ struct Decompressor {
IM(SDD1::Decompressor& self) : self(self) {}
auto init(uint offset) -> void;
auto getCodeWord(uint8 codeLength) -> uint8;
auto serialize(serializer&) -> void;
private:
Decompressor& self;
@ -13,6 +14,7 @@ struct Decompressor {
struct GCD { //golomb-code decoder
GCD(SDD1::Decompressor& self) : self(self) {}
auto getRunCount(uint8 codeNumber, uint8& mpsCount, bool& lpsIndex) -> void;
auto serialize(serializer&) -> void;
private:
Decompressor& self;
@ -23,6 +25,7 @@ struct Decompressor {
BG(SDD1::Decompressor& self, uint8 codeNumber) : self(self), codeNumber(codeNumber) {}
auto init() -> void;
auto getBit(bool& endOfRun) -> uint8;
auto serialize(serializer&) -> void;
private:
Decompressor& self;
@ -35,6 +38,7 @@ struct Decompressor {
PEM(SDD1::Decompressor& self) : self(self) {}
auto init() -> void;
auto getBit(uint8 context) -> uint8;
auto serialize(serializer&) -> void;
private:
Decompressor& self;
@ -54,6 +58,7 @@ struct Decompressor {
CM(SDD1::Decompressor& self) : self(self) {}
auto init(uint offset) -> void;
auto getBit() -> uint8;
auto serialize(serializer&) -> void;
private:
Decompressor& self;
@ -68,6 +73,7 @@ struct Decompressor {
OL(SDD1::Decompressor& self) : self(self) {}
auto init(uint offset) -> void;
auto decompress() -> uint8;
auto serialize(serializer&) -> void;
private:
Decompressor& self;
@ -78,6 +84,7 @@ struct Decompressor {
Decompressor();
auto init(uint offset) -> void;
auto read() -> uint8;
auto serialize(serializer&) -> void;
IM im;
GCD gcd;

View File

@ -11,4 +11,56 @@ auto SDD1::serialize(serializer& s) -> void {
s.integer(dma[n].size);
}
s.integer(dmaReady);
decompressor.serialize(s);
}
auto SDD1::Decompressor::serialize(serializer& s) -> void {
im.serialize(s);
gcd.serialize(s);
bg0.serialize(s);
bg1.serialize(s);
bg2.serialize(s);
bg3.serialize(s);
bg4.serialize(s);
bg5.serialize(s);
bg6.serialize(s);
bg7.serialize(s);
cm.serialize(s);
ol.serialize(s);
}
auto SDD1::Decompressor::IM::serialize(serializer& s) -> void {
s.integer(offset);
s.integer(bitCount);
}
auto SDD1::Decompressor::GCD::serialize(serializer& s) -> void {
}
auto SDD1::Decompressor::BG::serialize(serializer& s) -> void {
s.integer(mpsCount);
s.integer(lpsIndex);
}
auto SDD1::Decompressor::PEM::serialize(serializer& s) -> void {
for(auto& info : contextInfo) {
s.integer(info.status);
s.integer(info.mps);
}
}
auto SDD1::Decompressor::CM::serialize(serializer& s) -> void {
s.integer(bitplanesInfo);
s.integer(contextBitsInfo);
s.integer(bitNumber);
s.integer(currentBitplane);
s.array(previousBitplaneBits);
}
auto SDD1::Decompressor::OL::serialize(serializer& s) -> void {
s.integer(bitplanesInfo);
s.integer(r0);
s.integer(r1);
s.integer(r2);
}

View File

@ -23,6 +23,7 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> voi
bool hires = io.bgMode == 5 || io.bgMode == 6;
bool offsetPerTileMode = io.bgMode == 2 || io.bgMode == 4 || io.bgMode == 6;
bool directColorMode = io.col.directColor && source == Source::BG1 && (io.bgMode == 3 || io.bgMode == 4);
uint colorShift = 3 + self.tileMode;
int width = 256 << hires;
uint tileHeight = 3 + self.tileSize;
@ -91,14 +92,36 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> voi
if(tileHeight == 4 && (bool(voffset & 8) ^ bool(mirrorY))) tileNumber += 16;
tileNumber = (tileNumber & 0x03ff) + tiledataIndex & tileMask;
auto tiledata = ppu.tilecache[self.tileMode] + (tileNumber << 6);
tiledata += (voffset & 7 ^ mirrorY) << 3;
uint16 address;
address = (tileNumber << colorShift) + (voffset & 7 ^ mirrorY);
uint64 data;
data = (uint64)ppu.vram[address + 0] << 0;
data |= (uint64)ppu.vram[address + 8] << 16;
data |= (uint64)ppu.vram[address + 16] << 32;
data |= (uint64)ppu.vram[address + 24] << 48;
for(uint tileX = 0; tileX < 8; tileX++, x++) {
if(x & width) continue; //x < 0 || x >= width
if(!self.mosaicEnable || --mosaicCounter == 0) {
uint color, shift = mirrorX ? tileX : 7 - tileX;
/*if(self.tileMode >= TileMode::BPP2)*/ {
color = data >> shift + 0 & 1;
color += data >> shift + 7 & 2;
}
if(self.tileMode >= TileMode::BPP4) {
color += data >> shift + 14 & 4;
color += data >> shift + 21 & 8;
}
if(self.tileMode >= TileMode::BPP8) {
color += data >> shift + 28 & 16;
color += data >> shift + 35 & 32;
color += data >> shift + 42 & 64;
color += data >> shift + 49 & 128;
}
mosaicCounter = 1 + io.mosaicSize;
mosaicPalette = tiledata[tileX ^ mirrorX];
mosaicPalette = color;
mosaicPriority = tilePriority;
if(directColorMode) {
mosaicColor = directColor(paletteNumber, mosaicPalette);

View File

@ -42,6 +42,7 @@ auto PPU::writeVRAM(uint8 data) -> void {
}
auto PPU::updateTiledata(uint address) -> void {
/*
auto word = vram[address & 0x7fff];
auto line2bpp = tilecache[TileMode::BPP2] + (address << 3 & 0x3fff8);
auto line4bpp = tilecache[TileMode::BPP4] + (address << 2 & 0x1ffc0) + (address << 3 & 0x38);
@ -53,6 +54,7 @@ auto PPU::updateTiledata(uint address) -> void {
line4bpp[7 - x] = line4bpp[7 - x] & ~(3 << plane4bpp) | (word >> x & 1) << plane4bpp | (word >> x + 7 & 2) << plane4bpp;
line8bpp[7 - x] = line8bpp[7 - x] & ~(3 << plane8bpp) | (word >> x & 1) << plane8bpp | (word >> x + 7 & 2) << plane8bpp;
}
*/
}
auto PPU::readOAM(uint10 address) -> uint8 {

View File

@ -83,7 +83,9 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
uint mirrorX = !object.hflip ? tileX : tileWidth - 1 - tileX;
uint address = tiledataAddress + ((characterY + (characterX + mirrorX & 15)) << 4);
tile.number = address >> 4 & 0x7ff;
address = (address & 0x7ff0) + (y & 7);
tile.data = ppu.vram[address + 0] << 0;
tile.data |= ppu.vram[address + 8] << 16;
if(tileCount++ >= ppu.TileLimit) break;
tiles[tileCount - 1] = tile;
@ -97,16 +99,19 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
uint8_t priority[256] = {};
for(uint n : range(ppu.TileLimit)) {
const auto& tile = tiles[n];
auto& tile = tiles[n];
if(!tile.valid) continue;
auto tiledata = ppu.tilecache[TileMode::BPP4] + (tile.number << 6) + ((tile.y & 7) << 3);
uint tileX = tile.x;
uint mirrorX = tile.hflip ? 7 : 0;
for(uint x : range(8)) {
tileX &= 511;
if(tileX < 256) {
if(uint color = tiledata[x ^ mirrorX]) {
uint color, shift = tile.hflip ? x : 7 - x;
color = tile.data >> shift + 0 & 1;
color += tile.data >> shift + 7 & 2;
color += tile.data >> shift + 14 & 4;
color += tile.data >> shift + 21 & 8;
if(color) {
palette[tileX] = tile.palette + color;
priority[tileX] = self.priority[tile.priority];
}

View File

@ -51,10 +51,6 @@ PPU::PPU() {
}
}
tilecache[TileMode::BPP2] = new uint8_t[4096 * 8 * 8]();
tilecache[TileMode::BPP4] = new uint8_t[2048 * 8 * 8]();
tilecache[TileMode::BPP8] = new uint8_t[1024 * 8 * 8]();
for(uint y : range(240)) {
lines[y].y = y;
}
@ -63,9 +59,6 @@ PPU::PPU() {
PPU::~PPU() {
delete[] output;
for(uint l : range(16)) delete[] lightTable[l];
delete[] tilecache[TileMode::BPP2];
delete[] tilecache[TileMode::BPP4];
delete[] tilecache[TileMode::BPP8];
}
auto PPU::synchronizeCPU() -> void {

View File

@ -235,7 +235,7 @@ public:
uint8 priority = 0;
uint8 palette = 0;
bool hflip = 0;
uint16 number = 0;
uint32 data = 0;
};
struct Pixel {
@ -276,7 +276,6 @@ public:
//[unserialized]
uint16* output = {};
uint16* lightTable[16] = {};
uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata
uint ItemLimit = 0;
uint TileLimit = 0;

View File

@ -8,8 +8,6 @@ auto PPU::serialize(serializer& s) -> void {
s.array(cgram);
for(auto& object : objects) object.serialize(s);
if (s.mode() == s.Load)
for(auto address : range(32768)) updateTiledata(address);
Line::start = 0;
Line::count = 0;
}

View File

@ -1,4 +1,11 @@
auto System::serialize(bool synchronize) -> serializer {
//deterministic serialization (synchronize=false) is only possible with select libco methods.
if(co_method() != "x86"
&& co_method() != "amd64"
&& co_method() != "arm"
&& co_method() != "aarch64"
) synchronize = true;
if(!information.serializeSize[synchronize]) return {}; //should never occur
if(synchronize) runToSave();

View File

@ -3,6 +3,14 @@ local := false
openmp := true
flags += -Wno-narrowing -Wno-multichar -g -fPIC
ifeq ($(platform), ios-arm64)
flags += -fPIC -miphoneos-version-min=11.0 -Wno-error=implicit-function-declaration -DHAVE_POSIX_MEMALIGN
options += -dynamiclib
else ifeq ($(platform), tvos-arm64)
flags += -fPIC -mtvos-version-min=11.0 -Wno-error=implicit-function-declaration -DHAVE_POSIX_MEMALIGN
options += -dynamiclib
endif
objects := libretro $(objects)
objects := $(patsubst %,obj/%.o,$(objects))
@ -10,9 +18,9 @@ obj/libretro.o: target-libretro/libretro.cpp
all: $(objects)
ifeq ($(platform),linux)
$(strip $(compiler) -o out/$(name).so -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -Wl,-Bdynamic $(options))
$(strip $(compiler) -o out/$(name).so -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -lgomp -Wl,-Bdynamic $(options))
else ifeq ($(platform),windows)
$(strip $(compiler) -o out/$(name).dll -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -Wl,-Bdynamic $(options))
$(strip $(compiler) -o out/$(name).dll -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -lgomp -Wl,-Bdynamic $(options))
else ifeq ($(platform),macos)
$(strip $(compiler) -o out/$(name).dylib -shared $(objects) $(options))
else ifeq ($(platform), ios-arm64)

View File

@ -490,38 +490,18 @@ RETRO_API void retro_run()
RETRO_API size_t retro_serialize_size()
{
// To avoid having to serialize twice to query the size -> serialize.
if (program->has_cached_serialize)
{
return program->cached_serialize.size();
}
else
{
program->cached_serialize = emulator->serialize();
program->has_cached_serialize = true;
return program->cached_serialize.size();
}
return emulator->serialize().size();
}
RETRO_API bool retro_serialize(void *data, size_t size)
{
if (!program->has_cached_serialize)
{
program->cached_serialize = emulator->serialize();
program->has_cached_serialize = true;
}
if (program->cached_serialize.size() != size)
return false;
memcpy(data, program->cached_serialize.data(), size);
memcpy(data, emulator->serialize().data(), size);
return true;
}
RETRO_API bool retro_unserialize(const void *data, size_t size)
{
serializer s(static_cast<const uint8_t *>(data), size);
program->has_cached_serialize = false;
return emulator->unserialize(s);
}

View File

@ -44,9 +44,6 @@ struct Program : Emulator::Platform
auto hackPatchMemory(vector<uint8_t>& data) -> void;
serializer cached_serialize;
bool has_cached_serialize = false;
string base_name;
bool overscan = false;

View File

@ -59,6 +59,10 @@ static void co_init() {
#endif
}
const char* co_method() {
return "aarch64";
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
@ -73,7 +77,8 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
if(!co_active_handle) co_active_handle = &co_active_buffer;
if(handle = (unsigned long*)memory) {
unsigned long* p = (unsigned long*)((unsigned char*)handle + size);
unsigned int offset = (size & ~15);
unsigned long* p = (unsigned long*)((unsigned char*)handle + offset);
handle[19] = (unsigned long)p; /* x29 (frame pointer) */
handle[20] = (unsigned long)p; /* x30 (stack pointer) */
handle[21] = (unsigned long)entrypoint; /* x31 (link register) */
@ -83,23 +88,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
unsigned long* handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
size += 256;
size &= ~15;
if(handle = (unsigned long*)malloc(size)) {
unsigned long* p = (unsigned long*)((unsigned char*)handle + size);
handle[19] = (unsigned long)p; /* x29 (frame pointer) */
handle[20] = (unsigned long)p; /* x30 (stack pointer) */
handle[21] = (unsigned long)entrypoint; /* x31 (link register) */
}
return handle;
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {

View File

@ -115,6 +115,10 @@ static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
const char* co_method() {
return "amd64";
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
@ -140,22 +144,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
if(handle = (cothread_t)malloc(size)) {
unsigned int offset = (size & ~15) - 32;
long long *p = (long long*)((char*)handle + offset); /* seek to top of stack */
*--p = (long long)crash; /* crash if entrypoint returns */
*--p = (long long)entrypoint; /* start of function */
*(long long*)handle = (long long)p; /* stack pointer */
}
return handle;
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {

View File

@ -35,6 +35,10 @@ static void co_init() {
#endif
}
const char* co_method() {
return "arm";
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
@ -49,7 +53,8 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
if(!co_active_handle) co_active_handle = &co_active_buffer;
if(handle = (unsigned long*)memory) {
unsigned long* p = (unsigned long*)((unsigned char*)handle + size);
unsigned int offset = (size & ~15);
unsigned long* p = (unsigned long*)((unsigned char*)handle + offset);
handle[8] = (unsigned long)p;
handle[9] = (unsigned long)entrypoint;
}
@ -58,22 +63,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
unsigned long* handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
size += 256;
size &= ~15;
if(handle = (unsigned long*)malloc(size)) {
unsigned long* p = (unsigned long*)((unsigned char*)handle + size);
handle[8] = (unsigned long)p;
handle[9] = (unsigned long)entrypoint;
}
return handle;
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {

View File

@ -16,6 +16,10 @@ static void __stdcall co_thunk(void* coentry) {
((void (*)(void))coentry)();
}
const char* co_method() {
return "fiber";
}
cothread_t co_active() {
if(!co_active_) {
ConvertThreadToFiber(0);

View File

@ -1,7 +1,7 @@
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wparentheses"
//placing code in section(text) does not mark it executable with Clang.
/* placing code in section(text) does not mark it executable with Clang. */
#undef LIBCO_MPROTECT
#define LIBCO_MPROTECT
#endif

View File

@ -1,5 +1,5 @@
/*
libco v20 (2019-10-14)
libco v20 (2019-10-16)
author: byuu
license: ISC
*/
@ -13,6 +13,7 @@ extern "C" {
typedef void* cothread_t;
const char* co_method();
cothread_t co_active();
cothread_t co_derive(void*, unsigned int, void (*)(void));
cothread_t co_create(unsigned int, void (*)(void));

View File

@ -413,6 +413,10 @@ static void co_init_(void) {
co_active_handle = co_create_(state_size, (uintptr_t)&co_switch);
}
const char* co_method() {
return "ppc";
}
cothread_t co_active() {
if(!co_active_handle) co_init_();

View File

@ -221,6 +221,10 @@ __asm__(
".size swap_context, .-swap_context\n"
);
const char* co_method() {
return "ppc64v2";
}
cothread_t co_active() {
if(!co_active_handle) {
co_active_handle = (struct ppc64_context*)malloc(MIN_STACK + sizeof(struct ppc64_context));
@ -255,11 +259,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*coentry)(void)) {
}
cothread_t co_create(unsigned int size, void (*coentry)(void)) {
size_t total = MAX(size, MIN_STACK) + sizeof(struct ppc64_context);
void* memory = malloc(total);
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, total, coentry);
return co_derive(memory, size, coentry);
}
void co_delete(cothread_t handle) {

View File

@ -33,6 +33,10 @@ static void springboard(int ignored) {
}
}
const char* co_method() {
return "sjlj";
}
cothread_t co_active() {
if(!co_running) co_running = &co_primary;
return (cothread_t)co_running;

View File

@ -26,6 +26,10 @@ extern "C" {
static thread_local ucontext_t co_primary;
static thread_local ucontext_t* co_running = 0;
const char* co_module() {
return "ucontext";
}
cothread_t co_active() {
if(!co_running) co_running = &co_primary;
return (cothread_t)co_running;

View File

@ -69,6 +69,10 @@ static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
const char* co_method() {
return "x86";
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
@ -94,22 +98,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
if(handle = (cothread_t)malloc(size)) {
unsigned int offset = (size & ~15) - 32;
long *p = (long*)((char*)handle + offset); /* seek to top of stack */
*--p = (long)crash; /* crash if entrypoint returns */
*--p = (long)entrypoint; /* start of function */
*(long*)handle = (long)p; /* stack pointer */
}
return handle;
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {

View File

@ -9,7 +9,7 @@ namespace nall::DSP::Resampler {
struct Cubic {
inline auto reset(double inputFrequency, double outputFrequency = 0, uint queueSize = 0) -> void;
inline auto setInputFrequency(double inputFrequency) -> void;
inline auto pending() const -> uint;
inline auto pending() const -> bool;
inline auto read() -> double;
inline auto write(double sample) -> void;
inline auto serialize(serializer&) -> void;
@ -39,7 +39,7 @@ auto Cubic::setInputFrequency(double inputFrequency) -> void {
ratio = inputFrequency / outputFrequency;
}
auto Cubic::pending() const -> uint {
auto Cubic::pending() const -> bool {
return samples.pending();
}

View File

@ -241,11 +241,26 @@ auto Response::setFile(const string& value) -> type& {
}
if(!valid) return *this;
//cache images for seven days
auto suffix = Location::suffix(value);
uint maxAge = 0;
if(suffix == ".svg"
|| suffix == ".ico"
|| suffix == ".png"
|| suffix == ".gif"
|| suffix == ".jpg"
|| suffix == ".jpeg") {
maxAge = 7 * 24 * 60 * 60;
}
_file = value;
string eTag = {"\"", chrono::utc::datetime(file::timestamp(value, file::time::modify)), "\""};
header.assign("Content-Length", file::size(value));
header.assign("Cache-Control", "public");
header.assign("ETag", eTag);
header.assign("ETag", {"\"", chrono::utc::datetime(file::timestamp(value, file::time::modify)), "\""});
if(maxAge == 0) {
header.assign("Cache-Control", {"public"});
} else {
header.assign("Cache-Control", {"public, max-age=", maxAge});
}
return *this;
}

View File

@ -41,7 +41,7 @@ struct queue {
template<typename U = T> auto capacity() const -> uint { return _capacity * sizeof(T) / sizeof(U); }
template<typename U = T> auto size() const -> uint { return _size * sizeof(T) / sizeof(U); }
auto empty() const -> bool { return _size == 0; }
auto pending() const -> uint { return _size; }
auto pending() const -> bool { return _size > 0; }
auto full() const -> bool { return _size >= (int)_capacity; }
auto underflow() const -> bool { return _size < 0; }
auto overflow() const -> bool { return _size > (int)_capacity; }

View File

@ -51,13 +51,9 @@ struct serializer {
return _capacity;
}
auto setMode(Mode mode) -> bool {
if(_mode == Mode::Save && mode == Mode::Load) {
_mode = mode;
_size = 0;
return true;
}
return false;
auto setMode(Mode mode) -> void {
_mode = mode;
_size = 0;
}
template<typename T> auto real(T& value) -> serializer& {
@ -110,35 +106,38 @@ struct serializer {
return *this;
}
template<int N> auto array(uint8_t (&array_)[N]) -> serializer& {
array(array_, N);
return *this;
}
auto array(uint8_t* array, uint size) -> serializer& {
if(_mode == Save) {
memcpy(_data+_size, array, size);
} else if(_mode == Load) {
memcpy(array, _data+_size, size);
}
_size += size;
return *this;
}
#ifdef ENDIAN_LSB
template<int N> auto array(uint16_t (&array_)[N]) -> serializer& {
array(array_, N);
return *this;
}
auto array(uint16_t* array_, uint size) -> serializer& {
array((uint8_t*)array_, size*2);
return *this;
}
#endif
template<typename T, uint Size> auto array(nall::array<T[Size]>& array) -> serializer& {
for(auto& value : array) operator()(value);
return *this;
}
//optimized specializations
auto array(uint8_t* data, uint size) -> serializer& {
if(_mode == Save) {
memory::copy(_data + _size, data, size);
} else if(_mode == Load) {
memory::copy(data, _data + _size, size);
} else {
}
_size += size;
return *this;
}
template<int N> auto array(uint8_t (&data)[N]) -> serializer& {
return array(data, N);
}
//nall/serializer saves data in little-endian ordering
#if defined(ENDIAN_LSB)
auto array(uint16_t* data, uint size) -> serializer& { return array((uint8_t*)data, size * sizeof(uint16_t)); }
auto array(uint32_t* data, uint size) -> serializer& { return array((uint8_t*)data, size * sizeof(uint32_t)); }
auto array(uint64_t* data, uint size) -> serializer& { return array((uint8_t*)data, size * sizeof(uint64_t)); }
template<int N> auto array(uint16_t (&data)[N]) -> serializer& { return array(data, N); }
template<int N> auto array(uint32_t (&data)[N]) -> serializer& { return array(data, N); }
template<int N> auto array(uint64_t (&data)[N]) -> serializer& { return array(data, N); }
#endif
template<typename T> auto operator()(T& value, typename std::enable_if<has_serialize<T>::value>::type* = 0) -> serializer& { value.serialize(*this); return *this; }
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_integral<T>::value>::type* = 0) -> serializer& { return integer(value); }
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return real(value); }

View File

@ -1,7 +1,7 @@
#pragma once
/* Document Markup Language (DML) v1.0 parser
* revision 0.04
* revision 0.05
*/
#include <nall/location.hpp>
@ -9,6 +9,11 @@
namespace nall {
struct DML {
inline auto title() const -> string { return state.title; }
inline auto subtitle() const -> string { return state.subtitle; }
inline auto description() const -> string { return state.description; }
inline auto content() const -> string { return state.output; }
auto& setAllowHTML(bool allowHTML) { settings.allowHTML = allowHTML; return *this; }
auto& setHost(const string& hostname) { settings.host = {hostname, "/"}; return *this; }
auto& setPath(const string& pathname) { settings.path = pathname; return *this; }
@ -28,6 +33,9 @@ private:
} settings;
struct State {
string title;
string subtitle;
string description;
string output;
uint sections = 0;
} state;
@ -41,12 +49,14 @@ private:
};
inline auto DML::parse(const string& filedata, const string& pathname) -> string {
state = {};
settings.path = pathname;
parseDocument(filedata, settings.path, 0);
return state.output;
}
inline auto DML::parse(const string& filename) -> string {
state = {};
if(!settings.path) settings.path = Location::path(filename);
string document = settings.reader ? settings.reader(filename) : string::read(filename);
parseDocument(document, settings.path, 0);
@ -83,8 +93,8 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
//title
else if(block.beginsWith("! ")) {
auto name = lines.takeLeft().trimLeft("! ", 1L);
state.output.append("<h1>", markup(name));
state.title = lines.takeLeft().trimLeft("! ", 1L);
state.output.append("<h1>", markup(state.title));
for(auto& line : lines) {
if(!line.beginsWith("! ")) continue;
state.output.append("<span>", markup(line.trimLeft("! ", 1L)), "</span>");
@ -92,6 +102,14 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
state.output.append("</h1>\n");
}
//description
else if(block.beginsWith("? ")) {
while(lines) {
state.description.append(lines.takeLeft().trimLeft("? ", 1L), " ");
}
state.description.strip();
}
//section
else if(block.beginsWith("# ")) {
if(settings.sectioned) {
@ -101,6 +119,7 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
auto content = lines.takeLeft().trimLeft("# ", 1L).split("::", 1L).strip();
auto data = markup(content[0]);
auto name = escape(content(1, data.hash()));
state.subtitle = content[0];
state.output.append("<h2 id=\"", name, "\">", data);
for(auto& line : lines) {
if(!line.beginsWith("# ")) continue;
@ -226,13 +245,14 @@ inline auto DML::markup(const string& s) -> string {
natural link, linkBase;
natural embed, embedBase;
natural photo, photoBase;
natural iframe, iframeBase;
for(uint n = 0; n < s.size();) {
char a = s[n];
char b = s[n + 1];
if(!link && !embed && !iframe) {
if(!link && !embed && !photo && !iframe) {
if(a == '*' && b == '*') { t.append(strong.flip() ? "<strong>" : "</strong>"); n += 2; continue; }
if(a == '/' && b == '/') { t.append(emphasis.flip() ? "<em>" : "</em>"); n += 2; continue; }
if(a == '_' && b == '_') { t.append(insertion.flip() ? "<ins>" : "</ins>"); n += 2; continue; }
@ -244,7 +264,15 @@ inline auto DML::markup(const string& s) -> string {
if(iframe == 0 && a == '<' && b == '<') { t.append("<iframe width='772' height='434' src=\""); iframe = 1; iframeBase = n += 2; continue; }
if(iframe != 0 && a == '>' && b == '>') { t.append("\" frameborder='0' allowfullscreen></iframe>"); iframe = 0; n += 2; continue; }
if(!embed) {
if(!embed && !link) {
if(photo == 0 && a == '[' && b == '{') { t.append("<a href=\""); photo = 1; photoBase = n += 2; continue; }
if(photo == 1 && a == '}' && b == ']') { t.append(slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\"><img src=\"", slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\" alt=\"\"></a>"); n += 2; photo = 0; continue; }
if(photo == 1 && a == ':' && b == ':') { t.append(slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\"><img src=\"", slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\" alt=\""); photo = 2; photoBase = n += 2; continue; }
if(photo == 2 && a == '}' && b == ']') { t.append(slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\"></a>"); n += 2; photo = 0; continue; }
if(photo != 0) { n++; continue; }
}
if(!photo && !embed) {
if(link == 0 && a == '[' && b == '[') { t.append("<a href=\""); link = 1; linkBase = n += 2; continue; }
if(link == 1 && a == ':' && b == ':') { t.append("\">"); link = 2; n += 2; continue; }
if(link == 1 && a == ']' && b == ']') { t.append("\">", slice(s, linkBase, n - linkBase), "</a>"); n += 2; link = 0; continue; }
@ -252,7 +280,7 @@ inline auto DML::markup(const string& s) -> string {
if(link == 1 && a == '@' && b == '/') { t.append(settings.host); n += 2; continue; }
}
if(!link) {
if(!photo && !link) {
if(embed == 0 && a == '{' && b == '{') { t.append("<img src=\""); embed = 1; embedBase = n += 2; continue; }
if(embed == 1 && a == ':' && b == ':') { t.append("\" alt=\""); embed = 2; n += 2; continue; }
if(embed != 0 && a == '}' && b == '}') { t.append("\">"); embed = 0; n += 2; continue; }