From 985e441d8041e93a15e25052d6428bce6da654d1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 23 Feb 2020 14:03:03 +0100 Subject: [PATCH] - transitioned all JSON-based savegame code to FSerializer and removed sjson. Now everything is in place to transition the rest of the savegame code as well. --- source/CMakeLists.txt | 1 - source/common/compositesavegame.cpp | 1 + source/common/menu/loadsavemenu.cpp | 1 - source/common/menu/savegamemanager.cpp | 73 +- source/common/music/music.cpp | 73 +- source/common/music/s_music.h | 3 - source/common/music/z_music.h | 2 + source/common/quotemgr.h | 6 +- source/common/quotes.cpp | 98 +- source/common/savegamehelp.cpp | 177 +- source/common/secrets.cpp | 52 +- source/common/secrets.h | 4 +- source/common/serializer.cpp | 2 + source/common/sound/backend/i_sound.cpp | 8 +- source/common/sound/backend/i_sound.h | 2 - source/common/sound/s_sound.cpp | 3 +- source/common/statistics.cpp | 122 +- source/common/statistics.h | 4 +- source/common/version.h | 20 +- source/thirdparty/include/sjson.h | 2376 ----------------------- source/thirdparty/src/sjson.cpp | 5 - 21 files changed, 212 insertions(+), 2821 deletions(-) delete mode 100644 source/thirdparty/include/sjson.h delete mode 100644 source/thirdparty/src/sjson.cpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index e8ec09a25..bd4ebe8da 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -653,7 +653,6 @@ set (PCH_SOURCES glbackend/hw_draw2d.cpp thirdparty/src/base64.cpp - thirdparty/src/sjson.cpp thirdparty/src/fix16.cpp thirdparty/src/fix16_str.cpp thirdparty/src/md4.cpp diff --git a/source/common/compositesavegame.cpp b/source/common/compositesavegame.cpp index 398b01f21..7a278ad0a 100644 --- a/source/common/compositesavegame.cpp +++ b/source/common/compositesavegame.cpp @@ -61,6 +61,7 @@ void CompositeSavegameWriter::AddCompressedElement(const char* filename, FCompre subbuffers.Push(buffer); buffer = {}; subfiles.Push(nullptr); + isCompressed.Push(true); } FCompressedBuffer CompositeSavegameWriter::CompressElement(BufferWriter *bw, bool compress) diff --git a/source/common/menu/loadsavemenu.cpp b/source/common/menu/loadsavemenu.cpp index 293531b80..13c245ce2 100644 --- a/source/common/menu/loadsavemenu.cpp +++ b/source/common/menu/loadsavemenu.cpp @@ -44,7 +44,6 @@ #include "v_draw.h" #include "files.h" #include "resourcefile.h" -#include "sjson.h" #include "savegamehelp.h" #include "i_specialpaths.h" #include "../../platform/win32/i_findfile.h" // This is a temporary direct path. Needs to be fixed when stuff gets cleaned up. diff --git a/source/common/menu/savegamemanager.cpp b/source/common/menu/savegamemanager.cpp index d6bc771a3..05c934a7e 100644 --- a/source/common/menu/savegamemanager.cpp +++ b/source/common/menu/savegamemanager.cpp @@ -44,7 +44,6 @@ #include "v_draw.h" #include "files.h" #include "resourcefile.h" -#include "sjson.h" #include "cmdlib.h" #include "files.h" #include "savegamehelp.h" @@ -52,6 +51,7 @@ #include "c_dispatch.h" #include "i_system.h" #include "build.h" +#include "serializer.h" FSavegameManager savegameManager; @@ -378,50 +378,49 @@ unsigned FSavegameManager::ExtractSaveData(int index) // this should not happen because the file has already been verified. return index; } - auto fr = info->NewReader(); - auto data = fr.ReadPadded(1); - fr.Close(); - sjson_context* ctx = sjson_create_context(0, 0, NULL); - if (ctx) + + void* data = info->Get(); + FSerializer arc; + if (!arc.OpenReader((const char*)data, info->LumpSize)) { - sjson_node* root = sjson_decode(ctx, (const char*)data.Data()); + return index; + } + FString comment, fcomment, ncomment, mtime; - FString comment = sjson_get_string(root, "Creation Time", ""); - FString fcomment = sjson_get_string(root, "Map Label", ""); - FString ncomment = sjson_get_string(root, "Map Name", ""); - FString mtime = sjson_get_string(root, "Map Time", ""); - comment.AppendFormat("\n%s - %s\n%s", fcomment.GetChars(), ncomment.GetChars(), mtime.GetChars()); - SaveCommentString = comment; + arc("Creation Time", comment) + ("Map Label", fcomment) + ("Map Name", ncomment) + ("Map Time", mtime); - // Extract pic (todo: let the renderer write a proper PNG file instead of a raw canvas dunp of the software renderer - and make it work for all games.) - FResourceLump *pic = resf->FindLump("savepic.png"); - if (pic != nullptr) + comment.AppendFormat("\n%s - %s\n%s", fcomment.GetChars(), ncomment.GetChars(), mtime.GetChars()); + SaveCommentString = comment; + + FResourceLump *pic = resf->FindLump("savepic.png"); + if (pic != nullptr) + { + FileReader picreader; + + picreader.OpenMemoryArray([=](TArray &array) { - FileReader picreader; - - picreader.OpenMemoryArray([=](TArray &array) + auto cache = pic->Lock(); + array.Resize(pic->LumpSize); + memcpy(&array[0], cache, pic->LumpSize); + pic->Unlock(); + return true; + }); + PNGHandle *png = M_VerifyPNG(picreader); + if (png != nullptr) + { + SavePic = PNGTexture_CreateFromFile(png, node->Filename); + delete png; + if (SavePic && SavePic->GetWidth() == 1 && SavePic->GetHeight() == 1) { - auto cache = pic->Lock(); - array.Resize(pic->LumpSize); - memcpy(&array[0], cache, pic->LumpSize); - pic->Unlock(); - return true; - }); - PNGHandle *png = M_VerifyPNG(picreader); - if (png != nullptr) - { - SavePic = PNGTexture_CreateFromFile(png, node->Filename); - delete png; - if (SavePic && SavePic->GetWidth() == 1 && SavePic->GetHeight() == 1) - { - delete SavePic; - SavePic = nullptr; - SavePicData.Clear(); - } + delete SavePic; + SavePic = nullptr; + SavePicData.Clear(); } } - sjson_destroy_context(ctx); } delete resf; } diff --git a/source/common/music/music.cpp b/source/common/music/music.cpp index e55cf0fd4..19ebe8985 100644 --- a/source/common/music/music.cpp +++ b/source/common/music/music.cpp @@ -50,10 +50,9 @@ #include "c_dispatch.h" #include "gamecontrol.h" #include "filereadermusicinterface.h" -#include "savegamehelp.h" -#include "sjson.h" #include "v_text.h" #include "mapinfo.h" +#include "serializer.h" MusPlayingInfo mus_playing; MusicAliasMap MusicAliases; @@ -707,61 +706,27 @@ void Mus_SetPaused(bool on) else S_ResumeMusic(); } -void MUS_Save() +void Mus_Serialize(FSerializer &arc) { - FString music = mus_playing.name; - if (music.IsEmpty()) music = mus_playing.LastSong; - - sjson_context* ctx = sjson_create_context(0, 0, NULL); - if (!ctx) + if (arc.BeginObject("music")) { - return; + if (arc.isWriting()) + { + FString music = mus_playing.name; + if (music.IsEmpty()) music = mus_playing.LastSong; + + arc.AddString("music", music); + } + else arc("music", mus_playing.LastSong); + + arc("baseorder", mus_playing.baseorder) + ("loop", mus_playing.loop) + .EndObject(); + + // this is to prevent scripts from resetting the music after it has been loaded from the savegame. + if (arc.isReading()) mus_blocked = true; + // Actual music resuming cannot be performed here, it must be done in the game code. } - sjson_node* root = sjson_mkobject(ctx); - sjson_put_string(ctx, root, "music", music); - sjson_put_int(ctx, root, "baseorder", mus_playing.baseorder); - sjson_put_bool(ctx, root, "loop", mus_playing.loop); - - char* encoded = sjson_stringify(ctx, root, " "); - - FileWriter* fil = WriteSavegameChunk("music.json"); - if (!fil) - { - sjson_destroy_context(ctx); - return; - } - - fil->Write(encoded, strlen(encoded)); - - sjson_free_string(ctx, encoded); - sjson_destroy_context(ctx); -} - -bool MUS_Restore() -{ - auto fil = ReadSavegameChunk("music.json"); - if (!fil.isOpen()) - { - return false; - } - - auto text = fil.ReadPadded(1); - fil.Close(); - - if (text.Size() == 0) - { - return false; - } - - sjson_context* ctx = sjson_create_context(0, 0, NULL); - sjson_node* root = sjson_decode(ctx, (const char*)text.Data()); - mus_playing.LastSong = sjson_get_string(root, "music", ""); - mus_playing.baseorder = sjson_get_int(root, "baseorder", 0); - mus_playing.loop = sjson_get_bool(root, "loop", true); - sjson_destroy_context(ctx); - mus_blocked = true; // this is to prevent scripts from resetting the music after it has been loaded from the savegame. - - return true; } void Mus_ResumeSaved() diff --git a/source/common/music/s_music.h b/source/common/music/s_music.h index 61d25fad0..497e4b640 100644 --- a/source/common/music/s_music.h +++ b/source/common/music/s_music.h @@ -71,9 +71,6 @@ extern MusPlayingInfo mus_playing; extern float relative_volume, saved_relative_volume; -void MUS_Save(); -bool MUS_Restore(); - // Note for later when the OPL player is ported. // DN3D and related games use "d3dtimbr.tmb" diff --git a/source/common/music/z_music.h b/source/common/music/z_music.h index d848de0b8..544b67a6d 100644 --- a/source/common/music/z_music.h +++ b/source/common/music/z_music.h @@ -12,3 +12,5 @@ void Mus_Fade(double seconds); void Mus_SetPaused(bool on); void Mus_ResumeSaved(); FString G_SetupFilenameBasedMusic(const char* fileName, const char *defaultfn); +class FSerializer; +void Mus_Serialize(FSerializer& arc); diff --git a/source/common/quotemgr.h b/source/common/quotemgr.h index 3de93401e..f6dc8af7a 100644 --- a/source/common/quotemgr.h +++ b/source/common/quotemgr.h @@ -12,6 +12,8 @@ enum MAXQUOTES = 16384, }; +class FSerializer; + class Quotes { FString quotes[MAXQUOTES]; @@ -55,11 +57,9 @@ public: } void AppendQuote(int dst, int src, int len = -1); - void AppendExQuote(int dst, int src, int len = -1); void FormatQuote(int dst, const char* fmt, ...); void Substitute(int num, const char* text, const char* replc); - void ReadFromSavegame(); - void WriteToSavegame(); + void Serialize(FSerializer &arc); }; extern Quotes quoteMgr; diff --git a/source/common/quotes.cpp b/source/common/quotes.cpp index 1b2099d1a..3a6d438e5 100644 --- a/source/common/quotes.cpp +++ b/source/common/quotes.cpp @@ -37,7 +37,8 @@ #include "quotemgr.h" #include "savegamehelp.h" -#include "sjson.h" +#include "serializer.h" +#include "printf.h" void Quotes::MakeStringLabel(FString "e) @@ -72,15 +73,6 @@ void Quotes::AppendQuote(int dst, int src, int len) else quotes[dst] += FString(GStrings.localize(quotes[src]), len); } -void Quotes::AppendExQuote(int dst, int src, int len) -{ - // This needs to apply the localization because the combined string is not localizable anymore. - if (quotes[dst][0] == '$') quotes[dst] = GStrings.localize(quotes[dst]); - if (len < 0) quotes[dst] << GStrings.localize(exquotes[src]); - else quotes[dst] += FString(GStrings.localize(exquotes[src]), len); -} - - void Quotes::FormatQuote(int dst, const char* fmt, ...) { va_list ap; @@ -95,88 +87,20 @@ void Quotes::Substitute(int dst, const char* text, const char* replc) } -void Quotes::ReadFromSavegame() +void Quotes::Serialize(FSerializer &arc) { - for (auto& q : quotes) q = ""; - for (auto& q : exquotes) q = ""; - - auto fil = ReadSavegameChunk("quotes.json"); - if (!fil.isOpen()) + // This only saves the regular quotes. The ExQuotes array is immutable once initialized. + if (arc.BeginObject("quotes")) { - return; - } - - auto text = fil.ReadPadded(1); - fil.Close(); - - if (text.Size() == 0) - { - return; - } - - sjson_context* ctx = sjson_create_context(0, 0, NULL); - sjson_node* root = sjson_decode(ctx, (const char*)text.Data()); - - auto qs = sjson_find_member(root, "quotes"); - auto xs = sjson_find_member(root, "exquotes"); - - sjson_node* q; - sjson_foreach(q, qs) - { - int index = (int)strtoll(q->key, nullptr, 10); - quotes[index] = q->string_; - } - sjson_foreach(q, xs) - { - int index = (int)strtoll(q->key, nullptr, 10); - exquotes[index] = q->string_; - } - sjson_destroy_context(ctx); -} - -void Quotes::WriteToSavegame() -{ - sjson_context* ctx = sjson_create_context(0, 0, NULL); - if (!ctx) - { - return; - } - sjson_node* root = sjson_mkobject(ctx); - sjson_node* qs = sjson_mkobject(ctx); - sjson_node* xs = sjson_mkobject(ctx); - - for (unsigned i = 0; i < MAXQUOTES; i++) - { - if (quotes[i].IsNotEmpty()) + for (int i = 0; i < MAXQUOTES; i++) { - char buff[10]; - snprintf(buff, 10, "%d", i); - sjson_append_member(ctx, qs, buff, sjson_mkstring(ctx, quotes[i])); - } - if (exquotes[i].IsNotEmpty()) - { - char buff[10]; - snprintf(buff, 10, "%d", i); - sjson_append_member(ctx, xs, buff, sjson_mkstring(ctx, exquotes[i])); + char buf[10]; + mysnprintf(buf, 10, "%d", i); + FString nulstr; + arc(buf, quotes[i], nulstr); } + arc.EndObject(); } - sjson_append_member(ctx, root, "quotes", qs); - sjson_append_member(ctx, root, "exquotes", xs); - - char* encoded = sjson_stringify(ctx, root, " "); - FileWriter* fil = WriteSavegameChunk("quotes.json"); - if (!fil) - { - sjson_destroy_context(ctx); - return; - } - - fil->Write(encoded, strlen(encoded)); - - sjson_free_string(ctx, encoded); - sjson_destroy_context(ctx); - return; - } Quotes quoteMgr; diff --git a/source/common/savegamehelp.cpp b/source/common/savegamehelp.cpp index f8694651f..bf4ca9141 100644 --- a/source/common/savegamehelp.cpp +++ b/source/common/savegamehelp.cpp @@ -35,7 +35,6 @@ #include "compositesaveame.h" #include "savegamehelp.h" -#include "sjson.h" #include "baselayer.h" #include "gstrings.h" #include "i_specialpaths.h" @@ -43,7 +42,6 @@ #include "filesystem/filesystem.h" #include "statistics.h" #include "secrets.h" -#include "s_music.h" #include "quotemgr.h" #include "mapinfo.h" #include "v_video.h" @@ -51,6 +49,7 @@ #include "m_argv.h" #include "serializer.h" #include "version.h" +#include "z_music.h" static CompositeSavegameWriter savewriter; static FResourceFile *savereader; @@ -59,6 +58,20 @@ void SaveEngineState(); CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +//============================================================================= +// +// +// +//============================================================================= + +static void SerializeSession(FSerializer& arc) +{ + SerializeStatistics(arc); + SECRET_Serialize(arc); + Mus_Serialize(arc); + quoteMgr.Serialize(arc); +} + //============================================================================= // // This is for keeping my sanity while working with the horrible mess @@ -80,12 +93,21 @@ bool OpenSaveGameForRead(const char *name) if (savereader != nullptr) { + FResourceLump* info = savereader->FindLump("session.json"); + if (info == nullptr) + { + return false; + } + + FSerializer arc; + void* data = info->Get(); + if (!arc.OpenReader((const char*)data, info->LumpSize)) + { + return false; + } // Load system-side data from savegames. - ReadStatistics(); - SECRET_Load(); - MUS_Restore(); - quoteMgr.ReadFromSavegame(); + SerializeSession(arc); LoadEngineState(); auto file = ReadSavegameChunk("info.json"); @@ -152,7 +174,6 @@ bool OpenSaveGameForWrite(const char* filename, const char *name) FSerializer savegameengine; // saved play state. savegameinfo.OpenWriter(true); - savegamesession.OpenWriter(save_formatted); savegameengine.OpenWriter(save_formatted); char buf[100]; @@ -192,10 +213,11 @@ bool OpenSaveGameForWrite(const char* filename, const char *name) // Handle system-side modules that need to persist data in savegames here, in a central place. - SaveStatistics(); - SECRET_Save(); - MUS_Save(); - quoteMgr.WriteToSavegame(); + savegamesession.OpenWriter(save_formatted); + SerializeSession(savegamesession); + buff = savegamesession.GetCompressedOutput(); + AddCompressedSavegameChunk("session.json", buff); + SaveEngineState(); auto picfile = WriteSavegameChunk("savepic.png"); screen->WriteSavePic(picfile, 240, 180); @@ -270,84 +292,75 @@ static bool G_CheckSaveGameWads (const char *gamegrp, const char *mapgrp, bool p int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu) { -#if 0 - FSerializer arc(nullptr); - if (!arc.OpenReader((const char*)data, info->LumpSize)) + auto data = fr.Read(); + FSerializer arc; + if (!arc.OpenReader((const char*)data.Data(), data.Size())) { - LoadGameError("TXT_FAILEDTOREADSG"); - return; + return -2; } -#endif - auto data = fr.ReadPadded(1); - - sjson_context* ctx = sjson_create_context(0, 0, NULL); - if (ctx) + int savever; + FString engine, gamegrp, mapgrp, title, filename; + + arc("Save Version", savever) + ("Engine", engine) + ("Game Resource", gamegrp) + ("Map Resource", mapgrp) + ("Title", title) + ("Map File", filename); + + auto savesig = gi->GetSaveSig(); + + if (savetitle) *savetitle = title; + if (engine.Compare(savesig.savesig) != 0 || savever > savesig.currentsavever) { - sjson_node* root = sjson_decode(ctx, (const char*)data.Data()); - - - int savever = sjson_get_int(root, "Save Version", -1); - FString engine = sjson_get_string(root, "Engine", ""); - FString gamegrp = sjson_get_string(root, "Game Resource", ""); - FString mapgrp = sjson_get_string(root, "Map Resource", ""); - FString title = sjson_get_string(root, "Title", ""); - FString filename = sjson_get_string(root, "Map File", ""); - auto savesig = gi->GetSaveSig(); - - sjson_destroy_context(ctx); - - if (savetitle) *savetitle = title; - if (engine.Compare(savesig.savesig) != 0 || savever > savesig.currentsavever) + // different engine or newer version: + // not our business. Leave it alone. + return 0; + } + + MapRecord *curLevel = nullptr; + + if (strncmp(filename, "file://", 7) != 0) + { + for (auto& mr : mapList) { - // different engine or newer version: - // not our business. Leave it alone. + if (mr.fileName.Compare(filename) == 0) + { + curLevel = &mr; + } + } + } + else + { + curLevel = &userMapRecord; + if (!formenu) + { + userMapRecord.name = ""; + userMapRecord.SetFileName(filename); + } + } + if (!curLevel) return 0; + if (!formenu) currentLevel = curLevel; + + + if (savever < savesig.minsavever) + { + // old, incompatible savegame. List as not usable. + return -1; + } + else + { + auto ggfn = ExtractFileBase(fileSystem.GetResourceFileName(1), true); + if (gamegrp.CompareNoCase(ggfn) == 0) + { + return G_CheckSaveGameWads(gamegrp, mapgrp, false) ? 1 : -2; + } + else + { + // different game. Skip this. return 0; } - - MapRecord *curLevel = nullptr; - - if (strncmp(filename, "file://", 7) != 0) - { - for (auto& mr : mapList) - { - if (mr.fileName.Compare(filename) == 0) - { - curLevel = &mr; - } - } - } - else - { - curLevel = &userMapRecord; - if (!formenu) - { - userMapRecord.name = ""; - userMapRecord.SetFileName(filename); - } - } - if (!curLevel) return 0; - if (!formenu) currentLevel = curLevel; - - - if (savever < savesig.minsavever) - { - // old, incompatible savegame. List as not usable. - return -1; - } - else - { - auto ggfn = ExtractFileBase(fileSystem.GetResourceFileName(1), true); - if (gamegrp.CompareNoCase(ggfn) == 0) - { - return G_CheckSaveGameWads(gamegrp, mapgrp, false) ? 1 : -2; - } - else - { - // different game. Skip this. - return 0; - } - } } return 0; } diff --git a/source/common/secrets.cpp b/source/common/secrets.cpp index 06347ca95..b12fc251e 100644 --- a/source/common/secrets.cpp +++ b/source/common/secrets.cpp @@ -6,8 +6,7 @@ #include "c_cvars.h" #include "v_font.h" #include "v_draw.h" -#include "sjson.h" -#include "savegamehelp.h" +#include "serializer.h" #include "mapinfo.h" // Unlike in GZDoom we have to maintain this list here, because we got different game frontents that all store this info differently. @@ -109,54 +108,13 @@ CCMD(secret) } } -void SECRET_Save() +void SECRET_Serialize(FSerializer &arc) { - sjson_context* ctx = sjson_create_context(0, 0, NULL); - if (!ctx) + if (arc.BeginObject("secrets")) { - return; + arc("secrets", discovered_secrets) + .EndObject(); } - sjson_node* root = sjson_mkobject(ctx); - sjson_put_ints(ctx, root, "secrets", discovered_secrets.Data(), discovered_secrets.Size()); - - char* encoded = sjson_stringify(ctx, root, " "); - - FileWriter* fil = WriteSavegameChunk("secrets.json"); - if (!fil) - { - sjson_destroy_context(ctx); - return; - } - - fil->Write(encoded, strlen(encoded)); - - sjson_free_string(ctx, encoded); - sjson_destroy_context(ctx); -} - -bool SECRET_Load() -{ - auto fil = ReadSavegameChunk("secrets.json"); - if (!fil.isOpen()) - { - return false; - } - - auto text = fil.ReadPadded(1); - fil.Close(); - - if (text.Size() == 0) - { - return false; - } - - sjson_context* ctx = sjson_create_context(0, 0, NULL); - sjson_node* root = sjson_decode(ctx, (const char*)text.Data()); - discovered_secrets.Resize(1000); // Retarted interface alert - int realsize = sjson_get_ints(discovered_secrets.Data(), 1000, root, "secrets"); - discovered_secrets.Resize(realsize); - sjson_destroy_context(ctx); - return true; } void SECRET_SetMapName(const char *filename, const char *_maptitle) diff --git a/source/common/secrets.h b/source/common/secrets.h index 2377075c4..5b78e7107 100644 --- a/source/common/secrets.h +++ b/source/common/secrets.h @@ -1,8 +1,8 @@ #pragma once #include "files.h" -void SECRET_Save(); -bool SECRET_Load(); +class FSerializer; +void SECRET_Serialize(FSerializer &arc); void SECRET_SetMapName(const char *filename, const char *maptitle); void SECRET_Trigger(int num); diff --git a/source/common/serializer.cpp b/source/common/serializer.cpp index 26d7bcac7..861cdd7d2 100644 --- a/source/common/serializer.cpp +++ b/source/common/serializer.cpp @@ -51,6 +51,8 @@ #include "printf.h" #include "s_soundinternal.h" +bool save_full = false; + //========================================================================== // // This will double-encode already existing UTF-8 content. diff --git a/source/common/sound/backend/i_sound.cpp b/source/common/sound/backend/i_sound.cpp index 02e5ba00f..a0647160c 100644 --- a/source/common/sound/backend/i_sound.cpp +++ b/source/common/sound/backend/i_sound.cpp @@ -50,6 +50,7 @@ #include "s_music.h" #include "z_music.h" #include "gamecvars.h" +#include "gamecontrol.h" #include EXTERN_CVAR (Float, snd_sfxvolume) @@ -70,8 +71,6 @@ CVAR(String, snd_backend, DEF_BACKEND, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR (Bool, snd_pitched, false, CVAR_ARCHIVE) SoundRenderer *GSnd; -bool nosound; -bool nosfx; void I_CloseSound (); @@ -248,12 +247,9 @@ public: void I_InitSound () { FModule_SetProgDir(progdir); - /* Get command line options: */ - nosound = !!Args->CheckParm ("-nosound"); - nosfx = !!Args->CheckParm ("-nosfx"); GSnd = NULL; - if (nosound) + if (userConfig.nosound) { GSnd = new NullSoundRenderer; return; diff --git a/source/common/sound/backend/i_sound.h b/source/common/sound/backend/i_sound.h index f1c03c599..7eaac6d04 100644 --- a/source/common/sound/backend/i_sound.h +++ b/source/common/sound/backend/i_sound.h @@ -159,8 +159,6 @@ public: }; extern SoundRenderer *GSnd; -extern bool nosfx; -extern bool nosound; void I_InitSound (); void I_CloseSound(); diff --git a/source/common/sound/s_sound.cpp b/source/common/sound/s_sound.cpp index d8274299e..44b1791ee 100644 --- a/source/common/sound/s_sound.cpp +++ b/source/common/sound/s_sound.cpp @@ -43,6 +43,7 @@ #include "name.h" #include "filesystem.h" #include "cmdlib.h" +#include "gamecontrol.h" enum @@ -382,7 +383,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source, FVector3 pos, vel; FRolloffInfo *rolloff; - if (sound_id <= 0 || volume <= 0 || nosfx || nosound ) + if (sound_id <= 0 || volume <= 0 || userConfig.nosound ) return NULL; // prevent crashes. diff --git a/source/common/statistics.cpp b/source/common/statistics.cpp index c3622443e..edcb709f6 100644 --- a/source/common/statistics.cpp +++ b/source/common/statistics.cpp @@ -45,8 +45,7 @@ #include "c_cvars.h" #include "sc_man.h" #include "baselayer.h" -#include "savegamehelp.h" -#include "sjson.h" +#include "serializer.h" #include "gstrings.h" #include "version.h" @@ -468,112 +467,31 @@ void STAT_Cancel() // //========================================================================== -void SaveOneLevel(sjson_context *ctx, sjson_node *lev, OneLevel& l) +FSerializer& Serialize(FSerializer& arc, const char* key, OneLevel& l, OneLevel* def) { - sjson_put_int(ctx, lev, "totalkills", l.totalkills); - sjson_put_int(ctx, lev, "kills", l.killcount); - sjson_put_int(ctx, lev, "totalsecrets", l.totalsecrets); - sjson_put_int(ctx, lev, "secrets", l.secretcount); - sjson_put_int(ctx, lev, "leveltime", l.leveltime); - sjson_put_string(ctx, lev, "levelname", l.Levelname); + if (arc.BeginObject(key)) + { + arc("totalkills", l.totalkills) + ("killcount", l.killcount) + ("totalsecrets", l.totalsecrets) + ("secretcount", l.secretcount) + ("leveltime", l.leveltime) + ("levelname", l.Levelname) + .EndObject(); + } + return arc; } -void ReadOneLevel(sjson_node *lev, OneLevel& l) +void SerializeStatistics(FSerializer &arc) { - - l.totalkills = sjson_get_int(lev, "totalkills", 0); - l.killcount = sjson_get_int(lev, "kills", 0); - l.totalsecrets = sjson_get_int(lev, "totalsecrets", 0); - l.secretcount = sjson_get_int(lev, "secrets", 0); - l.leveltime = sjson_get_int(lev, "leveltime", 0); - l.Levelname = sjson_get_string(lev, "levelname", ""); -} - -void SaveStatistics() -{ - sjson_context* ctx = sjson_create_context(0, 0, NULL); - if (!ctx) + if (arc.BeginObject("statistics")) { - return; + arc("levelname", LevelName) + ("episode", StartEpisode) + ("skill", StartSkill) + ("levels", LevelData) + .EndObject(); } - sjson_node* root = sjson_mkobject(ctx); - - sjson_put_string(ctx, root, "levelname", LevelName); - sjson_put_string(ctx, root, "episode", StartEpisode); - sjson_put_int(ctx, root, "skill", StartSkill); - - sjson_node* levels = sjson_mkarray(ctx); - for (auto& lev : LevelData) - { - sjson_node* levj = sjson_mkobject(ctx); - SaveOneLevel(ctx, levj, lev); - sjson_append_element(levels, levj); - } - sjson_append_member(ctx, root, "levels", levels); - - char errmsg[256]; - if (!sjson_check(root, errmsg)) - { - buildprint(errmsg, "\n"); - sjson_destroy_context(ctx); - return; - } - - char* encoded = sjson_stringify(ctx, root, " "); - - FileWriter* fil = WriteSavegameChunk("statistics.json"); - if (!fil) - { - sjson_destroy_context(ctx); - return; - } - - fil->Write(encoded, strlen(encoded)); - - sjson_free_string(ctx, encoded); - sjson_destroy_context(ctx); - return; -} - -bool ReadStatistics() -{ - auto fil = ReadSavegameChunk("statistics.json"); - if (!fil.isOpen()) - { - return false; - } - - auto text = fil.ReadPadded(1); - fil.Close(); - - if (text.Size() == 0) - { - return false; - } - - sjson_context* ctx = sjson_create_context(0, 0, NULL); - sjson_node* root = sjson_decode(ctx, (const char*)text.Data()); - - LevelName = sjson_get_string(root, "levelname", ""); - StartEpisode = sjson_get_string(root, "episode", ""); - StartSkill = sjson_get_int(root, "skill", -1); - sjson_node* levels = sjson_find_member(root, "levels"); - - if (LevelName.Len() == 0 || StartEpisode.Len() == 0 || StartSkill == -1 || levels == nullptr) - { - sjson_destroy_context(ctx); - return true; // do not error out on this. - } - - int numlevels = sjson_child_count(levels); - LevelData.Resize(numlevels); - int i = 0; - for (auto& lev : LevelData) - { - ReadOneLevel(sjson_find_element(levels, i++), lev); - } - sjson_destroy_context(ctx); - return true; } diff --git a/source/common/statistics.h b/source/common/statistics.h index 3193fa3b7..61ed2b864 100644 --- a/source/common/statistics.h +++ b/source/common/statistics.h @@ -6,6 +6,6 @@ void STAT_NewLevel(const char* mapname); void STAT_Update(bool endofgame); void STAT_Cancel(); +class FSerializer; void InitStatistics(); -void SaveStatistics(); -bool ReadStatistics(); +void SerializeStatistics(FSerializer &); diff --git a/source/common/version.h b/source/common/version.h index 89e9acef2..86d9df5f3 100644 --- a/source/common/version.h +++ b/source/common/version.h @@ -66,17 +66,17 @@ const char *GetVersionString(); #define SAVESIG_SW GAMENAME ".ShadowWarrior" #define SAVESIG_PS GAMENAME ".Exhumed" -#define MINSAVEVER_DN3D 3 -#define MINSAVEVER_BLD 3 -#define MINSAVEVER_RR 3 -#define MINSAVEVER_SW 2 -#define MINSAVEVER_PS 2 +#define MINSAVEVER_DN3D 4 +#define MINSAVEVER_BLD 4 +#define MINSAVEVER_RR 4 +#define MINSAVEVER_SW 3 +#define MINSAVEVER_PS 3 -#define SAVEVER_DN3D 3 -#define SAVEVER_BLD 3 -#define SAVEVER_RR 3 -#define SAVEVER_SW 2 -#define SAVEVER_PS 2 +#define SAVEVER_DN3D 4 +#define SAVEVER_BLD 4 +#define SAVEVER_RR 4 +#define SAVEVER_SW 3 +#define SAVEVER_PS 3 #if defined(__APPLE__) || defined(_WIN32) #define GAME_DIR GAMENAME diff --git a/source/thirdparty/include/sjson.h b/source/thirdparty/include/sjson.h deleted file mode 100644 index 0cc649604..000000000 --- a/source/thirdparty/include/sjson.h +++ /dev/null @@ -1,2376 +0,0 @@ -// -// Copyright 2018 Sepehr Taghdisian (septag@github). All rights reserved. -// License: https://github.com/septag/sjson#license-bsd-2-clause -// -// Original code by Joseph A. Adams (joeyadams3.14159@gmail.com) -// -// sjson.h - v1.1.1 - Fast single header json encoder/decoder -// This is actually a fork of Joseph's awesome Json encoder/decoder code from his repo: -// https://github.com/rustyrussell/ccan/tree/master/ccan/json -// The encoder/decoder code is almost the same. What I did was adding object pools and string pages (sjson_context) -// that eliminates many micro memory allocations, which improves encode/decode speed and data access performance -// I also added malloc/free and libc API overrides, and made the library single header -// -// FEATURES: -// - Single header C-file -// - UTF8 support -// - Fast with minimal allocations (Object pool, String pool, ..) -// - Overriable malloc/free/memcpy/.. -// - Have both Json Decoder/Encoder -// - Encoder supports pretify -// - No dependencies -// - Simple and easy to use C api -// -// USAGE: -// Data types: -// sjson_tag Json object tag type, string, number, object, etc.. -// sjson_node Data structure that represents json DOM node -// sjson_context Encoder/decoder context, almost all API functions need a context to allocate and manage memory -// It's not thread-safe, so don't use the same context in multiple threads -// API: -// --- CREATION -// sjson_create_context creates a json context: -// @PARAM pool_size number of initial items in object pool (default=512) -// this is actually the number of DOM nodes in json and can be grown -// for large files with lots of elements, you can set this paramter to higher values -// @PARAM str_buffer_size String buffer size in bytes (default=4096) -// Strings in json decoder, encoder uses a special allocator that allocates string from a pool -// String pool will be grown from the initial size to store more json string data -// Depending on the size of the json data, you can set this value higher or lower -// @PARAM alloc_user A user-defined pointer that is passed to allocations, in case you override memory alloc functions -// sjson_destroy_context Destroys a json context and frees all memory -// after the context destroys, all created json nodes will become invalid -// -// sjson_reset_context Resets the context. No memory will be freed, just resets the buffers so they can be reused -// but the DOM nodes and strings will be invalidated next time you parse or decode a json -// -// --- DECODE/ENCODE/VALIDATE -// sjson_decode Decodes the json text and returns DOM document root node -// sjson_encode Encodes the json root node to json string, does not prettify -// Generated string can be freed by calling 'sjson_free_string' on the returned pointer -// sjson_stringify Encodes the json root node to pretty json string -// @PARAM space sets number of spaces for tab, example two-space for a tab is " " -// sjson_free_string String pointer returned by stringify and encode should be freed by calling this function -// sjson_validate Validates the json string, returns false if json string is invalid -// -// --- LOOKUP/TRAVERSE -// sjson_find_element Finds an element by index inside array -// sjson_find_member Finds an element by name inside object -// sjson_find_member_nocase Finds an element by name inside object, ignores case -// sjson_first_child Gets first child of Object or Array types -// sjson_foreach Iterates through an Object or array child elements, first parameter should be a pre-defined json_node* -// -// --- HIGHER LEVEL LOOKUP -// sjson_get_int Gets an integer value from a child of the specified parent node, sets to 'default_val' if node is not found -// sjson_get_float Gets a float value from a child of the specified parent node, sets to 'default_val' if node is not found -// sjson_get_double Gets a double value from a child of the specified parent node, sets to 'default_val' if node is not found -// sjson_get_string Gets a string value from a child of the specified parent node, sets to 'default_val' if node is not found -// sjson_get_bool Gets a boolean value from a child of the specified parent node, sets to 'default_val' if node is not found -// sjson_get_floats Returns float array from a child of the specified parent node, returns false if node is not found or does not have enough elements to fill the variable -// sjson_get_ints Returns integer array from a child of the specified parent node, returns false if node is not found or does not have enough elements to fill the variable -// -// --- CONSTRUCTION -// sjson_mknull Creates a NULL type json node -// sjson_mknumber Creates and sets a number type json node -// sjson_mkstring Creates and sets a string type json node -// sjson_mkobject Creates an object type json node -// sjson_mkarray Creates an array type json node -// sjson_mkbool Creates and sets boolean type json node -// sjson_append_element Appends the node to the end of an array -// sjson_prepend_element Prepends the node to the beginning of an array -// sjson_append_member Appends the node to the members of an object, should provide the key name to the element -// sjson_prepend_member Prepends the node to the members of an object, should provide the key name to the element -// sjson_remove_from_parent Removes the node from it's parrent, if there is any -// sjson_delete_node Deletes the node and all of it's children (object/array) -// -// --- HIGHER LEVEL CONSTRUCTION these functions use a combination of above to facilitate some operations -// sjson_put_obj Add an object with a key(name) to the child of the parent node -// sjson_put_array Add an array with a key(name) to the child of the parent node -// sjson_put_int Add an integer value with a key(name) to the child of the specified parent node -// sjson_put_float Add float value with a key(name) to the child of the specified parent node -// sjson_put_double Add double value with a key(name) to the child of the specified parent node -// sjson_put_bool Add boolean value with a key(name) to the child of the specified parent node -// sjson_put_string Add string value with a key(name) to the child of the specified parent node -// sjson_put_floats Add float array with a key(name) to the child of the specified parent node -// sjson_put_ints Add integer array with a key(name) to the child of the specified parent node -// sjson_put_string Add string array with a key(name) to the child of the specified parent node -// -// --- DEBUG -// sjson_check Checks and validates the json DOM node recursively -// Returns false and writes a report to 'errmsg' parameter if DOM document has any encoding problems -// -// IMPLEMENTATION: -// -// To include and implement sjson in your project, you should define SJSON_IMPLEMENTATION before including sjson.h -// You can also override memory allocation or any libc functions that the library uses to your own -// Here's a list of stuff that can be overriden: -// ALLOCATIONS -// - sjson_malloc(user, size) 'user' is the pointer that is passed in sjson_create_context -// - sjson_free(user, ptr) -// - sjson_realloc(user, ptr, size) -// DEBUG/ASSERT -// - sjson_assert -// STRINGS -// - sjson_stricmp -// - sjson_strcpy -// - sjson_strcmp -// - sjson_snprintf -// MEMORY -// - sjson_memcpy -// - sjson_memset -// - sjson_out_of_memory happens when sjson cannot allocate memory internally -// Default behaviour is that it asserts and exits the program -// Example: -// #define SJSON_IMPLEMENTATION -// #define sjson_malloc(user, size) MyMalloc(user, size) -// #define sjson_free(user, ptr) MyFree(user, ptr) -// #define sjson_realloc(user, ptr, size) MyRealloc(user, ptr, size) -// #include "sjson.h" -// ... -// -// NOTE: on sjson_reset_context -// what reset_context does is that is resets the internal buffers without freeing them -// this makes context re-usable for the next json document, so don't have to regrow buffers or create another context -// Example: -// sjson_context* ctx = sjson_create_context(0, 0, NULL); // initial creation -// sjson_decode(ctx, json1); // decode json1 -// ... // do some work on data -// sjson_reset_context(ctx); // reset the buffers, make sure you don't need json1 data -// sjson_decode(ctx, json2); // decode another json -// ... -// -#pragma once - -#ifndef SJSON_H_ -#define SJSON_H_ - -#ifdef _MSC_VER -# ifndef __cplusplus - -# define false 0 -# define true 1 -# define bool _Bool - -/* For compilers that don't have the builtin _Bool type. */ -# if ((defined(_MSC_VER) && _MSC_VER < 1800) || \ - (defined __GNUC__&& __STDC_VERSION__ < 199901L && __GNUC__ < 3)) && !defined(_lint) -typedef unsigned char _Bool; -# endif - -# endif /* !__cplusplus */ - -#define __bool_true_false_are_defined 1 -#else -# include -#endif -#include - -// Json DOM object type -typedef enum sjson_tag { - SJSON_NULL, - SJSON_BOOL, - SJSON_STRING, - SJSON_NUMBER, - SJSON_ARRAY, - SJSON_OBJECT, -} sjson_tag; - -// Json DOM node struct -typedef struct sjson_node -{ - struct sjson_node* parent; // only if parent is an object or array (NULL otherwise) - struct sjson_node* prev; - struct sjson_node* next; - - char* key; // only if parent is an object (NULL otherwise). Must be valid UTF-8. - - sjson_tag tag; - union { - bool bool_; // SJSON_BOOL - char* string_; // SJSON_STRING: Must be valid UTF-8. - double number_; // SJSON_NUMBER - struct { - struct sjson_node* head; - struct sjson_node* tail; - } children; // SJSON_ARRAY, SJSON_OBJECT - }; -} sjson_node; - -// Json context, handles memory, pools, etc. -// Almost every API needs a valid context to work -// Not multi-threaded. Do not use the same context in multiple threads -typedef struct sjson_context sjson_context; - -sjson_context* sjson_create_context(int pool_size, int str_buffer_size, void* alloc_user); -void sjson_destroy_context(sjson_context* ctx); -void sjson_reset_context(sjson_context* ctx); - -// Encoding, decoding, and validation -sjson_node* sjson_decode(sjson_context* ctx, const char* json); -char* sjson_encode(sjson_context* ctx, const sjson_node* node); -char* sjson_encode_string(sjson_context* ctx, const char* str); -char* sjson_stringify(sjson_context* ctx, const sjson_node* node, const char* space); -void sjson_free_string(sjson_context* ctx, char* str); - -bool sjson_validate(sjson_context* ctx, const char* json); - -// Lookup and traversal -sjson_node* sjson_find_element(sjson_node* array, int index); -sjson_node* sjson_find_member(sjson_node* object, const char* key); -sjson_node* sjson_find_member_nocase(sjson_node* object, const char *name); -sjson_node* sjson_first_child(const sjson_node* node); -int sjson_child_count(const sjson_node* node); - -// Higher level lookup/get functions -int sjson_get_int(sjson_node* parent, const char* key, int default_val); -float sjson_get_float(sjson_node* parent, const char* key, float default_val); -double sjson_get_double(sjson_node* parent, const char* key, double default_val); -const char* sjson_get_string(sjson_node* parent, const char* key, const char* default_val); -bool sjson_get_bool(sjson_node* parent, const char* key, bool default_val); -bool sjson_get_floats(float* out, int count, sjson_node* parent, const char* key); -int sjson_get_ints(int* out, int count, sjson_node* parent, const char* key); -bool sjson_get_uints(uint32_t* out, int count, sjson_node* parent, const char* key); -bool sjson_get_int16s(int16_t* out, int count, sjson_node* parent, const char* key); -bool sjson_get_uint16s(uint16_t* out, int count, sjson_node* parent, const char* key); - -#define sjson_foreach(i, object_or_array) \ - for ((i) = sjson_first_child(object_or_array); \ - (i) != NULL; \ - (i) = (i)->next) - -// Construction and manipulation -sjson_node* sjson_mknull(sjson_context* ctx); -sjson_node* sjson_mkbool(sjson_context* ctx, bool b); -sjson_node* sjson_mkstring(sjson_context* ctx, const char *s); -sjson_node* sjson_mknumber(sjson_context* ctx, double n); -sjson_node* sjson_mkarray(sjson_context* ctx); -sjson_node* sjson_mkobject(sjson_context* ctx); - -void sjson_append_element(sjson_node* array, sjson_node* element); -void sjson_prepend_element(sjson_node* array, sjson_node* element); -void sjson_append_member(sjson_context* ctx, sjson_node* object, const char* key, sjson_node* value); -void sjson_prepend_member(sjson_context* ctx, sjson_node* object, const char* key, sjson_node* value); - -void sjson_remove_from_parent(sjson_node* node); - -void sjson_delete_node(sjson_context* ctx, sjson_node* node); - -// Higher level construction -sjson_node* sjson_put_obj(sjson_context* ctx, sjson_node* parent, const char* key); -sjson_node* sjson_put_array(sjson_context* ctx, sjson_node* parent, const char* key); -sjson_node* sjson_put_int(sjson_context* ctx, sjson_node* parent, const char* key, int val); -sjson_node* sjson_put_float(sjson_context* ctx, sjson_node* parent, const char* key, float val); -sjson_node* sjson_put_double(sjson_context* ctx, sjson_node* parent, const char* key, double val); -sjson_node* sjson_put_bool(sjson_context* ctx, sjson_node* parent, const char* key, bool val); -sjson_node* sjson_put_string(sjson_context* ctx, sjson_node* parent, const char* key, const char* val); -sjson_node* sjson_put_floats(sjson_context* ctx, sjson_node* parent, const char* key, const float* vals, int count); -sjson_node* sjson_put_ints(sjson_context* ctx, sjson_node* parent, const char* key, const int* vals, int count); -sjson_node* sjson_put_strings(sjson_context* ctx, sjson_node* parent, const char* key, const char** vals, int count); -sjson_node* sjson_put_uints(sjson_context* ctx, sjson_node* parent, const char* key, const uint32_t* vals, int count); -sjson_node* sjson_put_int16s(sjson_context* ctx, sjson_node* parent, const char* key, const int16_t* vals, int count); -sjson_node* sjson_put_uint16s(sjson_context* ctx, sjson_node* parent, const char* key, const uint16_t* vals, int count); - -// Debugging - -/* - * Look for structure and encoding problems in a sjson_node or its descendents. - * - * If a problem is detected, return false, writing a description of the problem - * to errmsg (unless errmsg is NULL). - */ -bool sjson_check(const sjson_node* node, char errmsg[256]); - -#endif // SJSON_H_ - -#if defined(SJSON_IMPLEMENT) - -/* - Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) - All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef sjson_strcpy -# define __STDC_WANT_LIB_EXT1__ 1 -#endif - -#include - -#ifndef sjson_malloc -# include -# include -# define sjson_malloc(user, size) ((UNREFERENCED_PARAMETER(user)), malloc(size)) -# define sjson_free(user, ptr) ((UNREFERENCED_PARAMETER(user)), free(ptr)) -# define sjson_realloc(user, ptr, size) ((UNREFERENCED_PARAMETER(user)), realloc(ptr, size)) -#endif - -#ifndef sjson_assert -# include -# define sjson_assert(_e) assert(_e) -#endif - -#ifndef sjson_out_of_memory -# define sjson_out_of_memory() do { sjson_assert(0 && "Out of memory"); exit(EXIT_FAILURE); } while(0) -#endif - -#ifndef sjson_snprintf -# include -# define sjson_snprintf snprintf -#endif - -#ifndef sjson_strcmp -# include -# define sjson_strcmp(_a, _b) strcmp(_a, _b) -#endif - -#ifndef sjson_memset -# include -# define sjson_memset(_p, _b, _n) memset(_p, _b, _n) -#endif - -#ifndef sjson_memcpy -# include -# define sjson_memcpy(_a, _b, _n) memcpy(_a, _b, _n) -#endif - -#ifndef sjson_stricmp -# ifdef _WIN32 -# include -# define sjson_stricmp(_a, _b) _stricmp(_a, _b) -# else -# include -# define sjson_stricmp(_a, _b) strcasecmp(_a, _b) -# endif -#endif - -#ifndef sjson_strlen -# include -# define sjson_strlen(_str) strlen(_str) -#endif - -#ifndef sjson_strcpy -# include -# ifdef _MSC_VER -# define sjson_strcpy(_a, _s, _b) strcpy_s(_a, _s, _b) -# else -# define sjson_strcpy(_a, _s, _b) strcpy(_a, _b) -# endif -#endif - -#define sjson__align_mask(_value, _mask) ( ( (_value)+(_mask) ) & ( (~0)&(~(_mask) ) ) ) - -typedef struct sjson__str_page -{ - int offset; - int size; - struct sjson__str_page* next; - struct sjson__str_page* prev; -} sjson__str_page; - -typedef struct sjson__node_page -{ - int iter; - int capacity; - sjson_node** ptrs; - sjson_node* buff; - struct sjson__node_page* next; - struct sjson__node_page* prev; -} sjson__node_page; - -typedef struct sjson_context -{ - void* alloc_user; - int pool_size; - int str_buffer_size; - sjson__node_page* node_pages; - sjson__str_page* str_pages; - sjson__str_page* cur_str_page; - char* cur_str; -} sjson_context; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Growable string buffer -typedef struct sjson__sb -{ - char *cur; - char *end; - char *start; -} sjson__sb; - -static inline void sjson__sb_init(sjson_context* ctx, sjson__sb* sb, int reserve_sz) -{ - int ssize = reserve_sz >= ctx->str_buffer_size ? reserve_sz : ctx->str_buffer_size; - sb->start = (char*) sjson_malloc(ctx->alloc_user, ssize); - if (sb->start == NULL) - sjson_out_of_memory(); - sb->cur = sb->start; - sb->end = sb->start + ssize - 1; -} - -/* sb and need may be evaluated multiple times. */ -#define sjson__sb_need(ctx, sb, need) do { \ - if ((sb)->end - (sb)->cur < (need)) \ - sjson__sb_grow(ctx, sb, need); \ - } while (0) - -static inline void sjson__sb_grow(sjson_context* ctx, sjson__sb* sb, int need) -{ - size_t length = sb->cur - sb->start; - size_t alloc = sb->end - sb->start + 1; - - do { - alloc <<= 1; - } while (alloc <= length + need); - - sb->start = (char*)sjson_realloc(ctx->alloc_user, sb->start, alloc); - if (sb->start == NULL) { - sjson_out_of_memory(); - } - sb->cur = sb->start + length; - sb->end = sb->start + alloc - 1; - - UNREFERENCED_PARAMETER(ctx); -} - -static inline void sjson__sb_put(sjson_context* ctx, sjson__sb* sb, const char *bytes, int count) -{ - sjson__sb_need(ctx, sb, count); - sjson_memcpy(sb->cur, bytes, count); - sb->cur += count; -} - -#define sjson__sb_putc(ctx, sb, c) do { \ - if ((sb)->cur >= (sb)->end) \ - sjson__sb_grow(ctx, sb, 1); \ - *(sb)->cur++ = (c); \ - } while (0) - -static inline void sjson__sb_puts(sjson_context* ctx, sjson__sb* sb, const char *str) -{ - sjson__sb_put(ctx, sb, str, (int)sjson_strlen(str)); -} - -static inline char* sjson__sb_finish(sjson__sb* sb) -{ - *sb->cur = 0; - sjson_assert(sb->start <= sb->cur && sjson_strlen(sb->start) == (size_t)(sb->cur - sb->start)); - return sb->start; -} - -static inline void sjson__sb_free(sjson_context* ctx, sjson__sb* sb) -{ - sjson_free(ctx->alloc_user, sb->start); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Node page -static inline sjson__node_page* sjson__page_create(void* alloc_user, int capacity) -{ - capacity = sjson__align_mask(capacity, 15); - uint8_t* buff = (uint8_t*)sjson_malloc(alloc_user, - sizeof(sjson__node_page) + (sizeof(sjson_node) + sizeof(sjson_node*))*capacity); - if (!buff) { - sjson_out_of_memory(); - return NULL; - } - - sjson__node_page* page = (sjson__node_page*)buff; - page->iter = capacity; - page->capacity = capacity; - page->next = page->prev = NULL; - - buff += sizeof(sjson__node_page); - page->ptrs = (sjson_node**)buff; - buff += sizeof(sjson_node*)*capacity; - page->buff = (sjson_node*)buff; - - for (int i = 0; i < capacity; i++) - page->ptrs[capacity - i - 1] = &page->buff[i]; - - return page; -} - -static inline void sjson__page_destroy(sjson__node_page* page, void* alloc_user) -{ - sjson_assert(page); - page->capacity = page->iter = 0; - sjson_free(alloc_user, page); -} - -static inline sjson_node* sjson__page_new(sjson__node_page* page) -{ - if (page->iter > 0) { - return page->ptrs[--page->iter]; - } else { - sjson_assert(0 && "Node page capacity is full"); - return NULL; - } -} - -static inline bool sjson__page_full(const sjson__node_page* page) -{ - return page->iter == 0; -} - -static inline bool sjson__page_valid(const sjson__node_page* page, sjson_node* ptr) -{ - uintptr_t uptr = (uintptr_t)ptr; - bool inbuf = uptr >= (uintptr_t)page->buff && - uptr < (uintptr_t)(page->buff + page->capacity*sizeof(sjson_node)); - bool valid = (uintptr_t)((uint8_t*)ptr - (uint8_t*)page->buff) % sizeof(sjson_node) == 0; - return inbuf & valid; -} - -static inline void sjson__page_del(sjson__node_page* page, sjson_node* ptr) -{ - sjson_assert(page->iter != page->capacity && "Cannot delete more objects"); - sjson_assert(sjson__page_valid(page, ptr) && "Pointer does not belong to page"); - page->ptrs[page->iter++] = ptr; -} - -static inline void sjson__page_add_list(sjson__node_page** pfirst, sjson__node_page* node) -{ - if (*pfirst) { - (*pfirst)->prev = node; - node->next = *pfirst; - } - - *pfirst = node; -} - -static inline void sjson__page_remove_list(sjson__node_page** pfirst, sjson__node_page* node) -{ - if (node->prev) - node->prev->next = node->next; - if (node->next) - node->next->prev = node->prev; - if (*pfirst == node) - *pfirst = node->next; - node->prev = node->next = NULL; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// json string buffer page -static inline sjson__str_page* sjson__str_page_create(void* alloc_user, int size) -{ - size = sjson__align_mask(size, 7); - - // allocate the page and reserve memory for string-buffer after the struct - sjson__str_page* page = (sjson__str_page*)sjson_malloc(alloc_user, size + sizeof(sjson__str_page)); - if (!page) { - sjson_out_of_memory(); - return NULL; - } - - page->offset = 0; - page->size = size; - page->next = page->prev = NULL; - - return page; -} - -static inline void sjson__str_page_destroy(sjson__str_page* page, void* alloc_user) -{ - sjson_assert(page); - sjson_free(alloc_user, page); -} - -static inline char* sjson__str_page_ptr(sjson__str_page* page) -{ - char* buff = (char*)(page + 1); - return buff + page->offset; -} - -static inline char* sjson__str_page_startptr(sjson__str_page* page) -{ - return (char*)(page + 1); -} - -static inline bool sjson__str_page_grow(sjson__str_page* page, int size) -{ - if (page->offset + size <= page->size) { - page->offset += size; - return true; - } else { - return false; - } -} - -static inline void sjson__str_page_add_list(sjson__str_page** pfirst, sjson__str_page* node) -{ - if (*pfirst) { - (*pfirst)->prev = node; - node->next = *pfirst; - } - *pfirst = node; -} - -static inline void sjson__str_page_remove_list(sjson__str_page** pfirst, sjson__str_page* node) -{ - if (node->prev) - node->prev->next = node->next; - if (node->next) - node->next->prev = node->prev; - if (*pfirst == node) - *pfirst = node->next; - node->prev = node->next = NULL; -} - -sjson_context* sjson_create_context(int pool_size, int str_buffer_size, void* alloc_user) -{ - sjson_context* ctx = (sjson_context*)sjson_malloc(alloc_user, sizeof(sjson_context)); - if (!ctx) { - sjson_out_of_memory(); - return NULL; - } - sjson_memset(ctx, 0x00, sizeof(sjson_context)); - - if (pool_size <= 0) - pool_size = 512; - if (str_buffer_size <= 0) - str_buffer_size = 4096; - - ctx->alloc_user = alloc_user; - ctx->pool_size = pool_size; - ctx->str_buffer_size = str_buffer_size; - - // Create first pool page - ctx->node_pages = sjson__page_create(alloc_user, pool_size); - if (!ctx->node_pages) - return NULL; - - ctx->str_pages = NULL; - ctx->cur_str_page = NULL; - return ctx; -} - -void sjson_destroy_context(sjson_context* ctx) -{ - sjson_assert(ctx); - - // destroy node pages - sjson__node_page* npage = ctx->node_pages; - while (npage) { - sjson__node_page* next = npage->next; - sjson__page_destroy(npage, ctx->alloc_user); - npage = next; - } - - sjson__str_page* spage = ctx->str_pages; - while (spage) { - sjson__str_page* next = spage->next; - sjson__str_page_destroy(spage, ctx->alloc_user); - spage = next; - } - - sjson_free(ctx->alloc_user, ctx); -} - -void sjson_reset_context(sjson_context* ctx) -{ - // - for (sjson__node_page* npage = ctx->node_pages; npage; npage = npage->next) { - int capacity = npage->capacity; - npage->iter = capacity; - for (int i = 0; i < capacity; i++) - npage->ptrs[capacity - i - 1] = &npage->buff[i]; - } - - // - for (sjson__str_page* spage = ctx->str_pages; spage; spage = spage->next) { - spage->offset = 0; - } - - // TODO: maybe we can reverse the linked-lists to get better cache coherency -} - -static inline sjson_node* sjson__new_node(sjson_context* ctx, sjson_tag tag) -{ - sjson__node_page* npage = ctx->node_pages; - while (npage && sjson__page_full(npage)) - npage = npage->next; - - if (npage == NULL) { - npage = sjson__page_create(ctx->alloc_user, ctx->pool_size); - sjson_assert(npage); - sjson__page_add_list(&ctx->node_pages, npage); - } - - sjson_node* node = sjson__page_new(npage); - sjson_memset(node, 0x0, sizeof(sjson_node)); - node->tag = tag; - return node; -} - -static inline void sjson__del_node(sjson_context* ctx, sjson_node* node) -{ - sjson__node_page* npage = ctx->node_pages; - while (npage && !sjson__page_valid(npage, node)) - npage = npage->next; - - sjson_assert(npage && "sjson_node doesn't belong to any page - check the pointer"); - sjson__page_del(npage, node); -} - -// Usage: -static inline char* sjson__str_begin(sjson_context* ctx, int init_sz) -{ - sjson_assert (ctx->cur_str_page == NULL && "Should call sjson__str_end before begin"); - - // find a page that can grow to requested size - sjson__str_page* spage = ctx->str_pages; - while (spage && (spage->offset + init_sz) <= spage->size) - spage = spage->next; - - // create a new string page - if (spage == NULL) { - int page_sz = ctx->str_buffer_size; - if (init_sz > (page_sz >> 1)) { - page_sz = page_sz << 1; - page_sz = sjson__align_mask(init_sz, page_sz-1); - } - spage = sjson__str_page_create(ctx->alloc_user, page_sz); - sjson_assert(spage); - sjson__str_page_add_list(&ctx->str_pages, spage); - } - - sjson_assert(spage->offset + init_sz <= spage->size); - char* ptr = sjson__str_page_ptr(spage); - spage->offset += init_sz; - - ctx->cur_str_page = spage; - ctx->cur_str = ptr; - - return ptr; -} - -// Returns the pointer to the begining of the string, pointer may be reallocated and changed from sjson__str_begin -static inline char* sjson__str_grow(sjson_context* ctx, int grow_sz) -{ - sjson_assert (ctx->cur_str_page && "Should call sjson__str_begin before grow"); - - sjson__str_page* spage = ctx->cur_str_page; - if (sjson__str_page_grow(spage, grow_sz)) { - return ctx->cur_str; - } else { - int page_sz = spage->size; - int cur_str_sz = (int)(uintptr_t)(sjson__str_page_ptr(spage) - ctx->cur_str); - int total_sz = cur_str_sz + grow_sz; - if (total_sz > (page_sz >> 1)) { - page_sz <<= 1; - page_sz = total_sz > page_sz ? total_sz : page_sz; - } - - sjson__str_page* newspage = sjson__str_page_create(ctx->alloc_user, page_sz); - sjson_assert(newspage); - sjson_assert(total_sz <= newspage->size); - char* ptr = sjson__str_page_startptr(newspage); - newspage->offset += total_sz; - - // copy previous buffer into the new one - if (cur_str_sz > 0) - sjson_memcpy(ptr, ctx->cur_str, cur_str_sz); - - // check and see if we can remove the old page completely - if (ctx->cur_str == sjson__str_page_startptr(spage)) { - sjson__str_page_remove_list(&ctx->str_pages, spage); - sjson__str_page_destroy(spage, ctx->alloc_user); - } - - ctx->cur_str_page = newspage; - ctx->cur_str = ptr; - return ptr; - } -} - -static inline char* sjson__str_end(sjson_context* ctx) -{ - sjson_assert (ctx->cur_str_page && "Should call sjson__str_begin before end"); - char* ptr = ctx->cur_str; - ctx->cur_str_page = NULL; - ctx->cur_str = NULL; - return ptr; -} - -static char* sjson__strdup(sjson_context* ctx, const char *str) -{ - int len = (int)sjson_strlen(str); - char* ret = sjson__str_begin(ctx, len + 1); - sjson_memcpy(ret, str, len); - ret[len] = '\0'; - sjson__str_end(ctx); - return ret; -} - -/* - * Unicode helper functions - * - * These are taken from the ccan/charset module and customized a bit. - * Putting them here means the compiler can (choose to) inline them, - * and it keeps ccan/json from having a dependency. - */ - -/* - * Type for Unicode codepoints. - * We need our own because wchar_t might be 16 bits. - */ -typedef uint32_t json__uchar_t; - -/* - * Validate a single UTF-8 character starting at @s. - * The string must be null-terminated. - * - * If it's valid, return its length (1 thru 4). - * If it's invalid or clipped, return 0. - * - * This function implements the syntax given in RFC3629, which is - * the same as that given in The Unicode Standard, Version 6.0. - * - * It has the following properties: - * - * * All codepoints U+0000..U+10FFFF may be encoded, - * except for U+D800..U+DFFF, which are reserved - * for UTF-16 surrogate pair encoding. - * * UTF-8 byte sequences longer than 4 bytes are not permitted, - * as they exceed the range of Unicode. - * * The sixty-six Unicode "non-characters" are permitted - * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF). - */ -static int sjson__utf8_validate_cz(const char *s) -{ - unsigned char c = *s++; - - if (c <= 0x7F) { /* 00..7F */ - return 1; - } else if (c <= 0xC1) { /* 80..C1 */ - /* Disallow overlong 2-byte sequence. */ - return 0; - } else if (c <= 0xDF) { /* C2..DF */ - /* Make sure subsequent byte is in the range 0x80..0xBF. */ - if (((unsigned char)*s++ & 0xC0) != 0x80) - return 0; - - return 2; - } else if (c <= 0xEF) { /* E0..EF */ - /* Disallow overlong 3-byte sequence. */ - if (c == 0xE0 && (unsigned char)*s < 0xA0) - return 0; - - /* Disallow U+D800..U+DFFF. */ - if (c == 0xED && (unsigned char)*s > 0x9F) - return 0; - - /* Make sure subsequent bytes are in the range 0x80..0xBF. */ - if (((unsigned char)*s++ & 0xC0) != 0x80) - return 0; - if (((unsigned char)*s++ & 0xC0) != 0x80) - return 0; - - return 3; - } else if (c <= 0xF4) { /* F0..F4 */ - /* Disallow overlong 4-byte sequence. */ - if (c == 0xF0 && (unsigned char)*s < 0x90) - return 0; - - /* Disallow codepoints beyond U+10FFFF. */ - if (c == 0xF4 && (unsigned char)*s > 0x8F) - return 0; - - /* Make sure subsequent bytes are in the range 0x80..0xBF. */ - if (((unsigned char)*s++ & 0xC0) != 0x80) - return 0; - if (((unsigned char)*s++ & 0xC0) != 0x80) - return 0; - if (((unsigned char)*s++ & 0xC0) != 0x80) - return 0; - - return 4; - } else { /* F5..FF */ - return 0; - } -} - -/* Validate a null-terminated UTF-8 string. */ -static bool sjson__utf8_validate(const char *s) -{ - int len; - - for (; *s != 0; s += len) { - len = sjson__utf8_validate_cz(s); - if (len == 0) - return false; - } - - return true; -} - -/* - * Read a single UTF-8 character starting at @s, - * returning the length, in bytes, of the character read. - * - * This function assumes input is valid UTF-8, - * and that there are enough characters in front of @s. - */ -static int sjson__utf8_read_char(const char *s, json__uchar_t *out) -{ - const unsigned char *c = (const unsigned char*) s; - - sjson_assert(sjson__utf8_validate_cz(s)); - - if (c[0] <= 0x7F) { - /* 00..7F */ - *out = c[0]; - return 1; - } else if (c[0] <= 0xDF) { - /* C2..DF (unless input is invalid) */ - *out = ((json__uchar_t)c[0] & 0x1F) << 6 | - ((json__uchar_t)c[1] & 0x3F); - return 2; - } else if (c[0] <= 0xEF) { - /* E0..EF */ - *out = ((json__uchar_t)c[0] & 0xF) << 12 | - ((json__uchar_t)c[1] & 0x3F) << 6 | - ((json__uchar_t)c[2] & 0x3F); - return 3; - } else { - /* F0..F4 (unless input is invalid) */ - *out = ((json__uchar_t)c[0] & 0x7) << 18 | - ((json__uchar_t)c[1] & 0x3F) << 12 | - ((json__uchar_t)c[2] & 0x3F) << 6 | - ((json__uchar_t)c[3] & 0x3F); - return 4; - } -} - -/* - * Write a single UTF-8 character to @s, - * returning the length, in bytes, of the character written. - * - * @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF. - * - * This function will write up to 4 bytes to @out. - */ -static int sjson__utf8_write_char(json__uchar_t unicode, char *out) -{ - unsigned char *o = (unsigned char*) out; - - sjson_assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF)); - - if (unicode <= 0x7F) { - /* U+0000..U+007F */ - *o++ = unicode; - return 1; - } else if (unicode <= 0x7FF) { - /* U+0080..U+07FF */ - *o++ = 0xC0 | unicode >> 6; - *o++ = 0x80 | (unicode & 0x3F); - return 2; - } else if (unicode <= 0xFFFF) { - /* U+0800..U+FFFF */ - *o++ = 0xE0 | unicode >> 12; - *o++ = 0x80 | (unicode >> 6 & 0x3F); - *o++ = 0x80 | (unicode & 0x3F); - return 3; - } else { - /* U+10000..U+10FFFF */ - *o++ = 0xF0 | unicode >> 18; - *o++ = 0x80 | (unicode >> 12 & 0x3F); - *o++ = 0x80 | (unicode >> 6 & 0x3F); - *o++ = 0x80 | (unicode & 0x3F); - return 4; - } -} - -/* - * Compute the Unicode codepoint of a UTF-16 surrogate pair. - * - * @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF. - * If they aren't, this function returns false. - */ -static bool sjson__from_surrogate_pair(uint16_t uc, uint16_t lc, json__uchar_t *unicode) -{ - if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) { - *unicode = 0x10000 + ((((json__uchar_t)uc & 0x3FF) << 10) | (lc & 0x3FF)); - return true; - } else { - return false; - } -} - -/* - * Construct a UTF-16 surrogate pair given a Unicode codepoint. - * - * @unicode must be U+10000..U+10FFFF. - */ -static void sjson__to_surrogate_pair(json__uchar_t unicode, uint16_t *uc, uint16_t *lc) -{ - json__uchar_t n; - - sjson_assert(unicode >= 0x10000 && unicode <= 0x10FFFF); - - n = unicode - 0x10000; - *uc = ((n >> 10) & 0x3FF) | 0xD800; - *lc = (n & 0x3FF) | 0xDC00; -} - -#define sjson__is_space(c) ((c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == ' ') -#define sjson__is_digit(c) ((c) >= '0' && (c) <= '9') - -static bool sjson__parse_value(sjson_context* ctx, const char **sp, sjson_node **out); -static bool sjson__parse_string(sjson_context* ctx, const char **sp, char **out); -static bool sjson__parse_number(const char **sp, double *out); -static bool sjson__parse_array(sjson_context* ctx, const char **sp, sjson_node **out); -static bool sjson__parse_object(sjson_context* ctx, const char **sp, sjson_node **out); -static bool sjson__parse_hex16(const char **sp, uint16_t *out); - -static bool sjson__expect_literal(const char **sp, const char *str); -static void sjson__skip_space(const char **sp); - -static void sjson__emit_value(sjson_context* ctx, sjson__sb* out, const sjson_node *node); -static void sjson__emit_value_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *node, const char *space, int indent_level); -static void sjson__emit_string(sjson_context* ctx, sjson__sb* out, const char *str); -static void sjson__emit_number(sjson_context* ctx, sjson__sb* out, double num); -static void sjson__emit_array(sjson_context* ctx, sjson__sb* out, const sjson_node* array); -static void sjson__emit_array_indented(sjson_context* ctx, sjson__sb* out, const sjson_node* array, const char *space, int indent_level); -static void sjson__emit_object(sjson_context* ctx, sjson__sb* out, const sjson_node *object); -static void sjson__emit_object_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *object, const char *space, int indent_level); - -static int sjson__write_hex16(char *out, uint16_t val); - -static void sjson__append_node(sjson_node *parent, sjson_node *child); -static void sjson__prepend_node(sjson_node *parent, sjson_node *child); -static void sjson__append_member(sjson_node *object, char *key, sjson_node *value); - -/* Assertion-friendly validity checks */ -static bool sjson__tag_is_valid(unsigned int tag); -static bool sjson__number_is_valid(const char *num); - -sjson_node *sjson_decode(sjson_context* ctx, const char *json) -{ - const char *s = json; - sjson_node *ret; - - sjson__skip_space(&s); - if (!sjson__parse_value(ctx, &s, &ret)) - return NULL; - - sjson__skip_space(&s); - if (*s != 0) { - sjson_delete_node(ctx, ret); - return NULL; - } - - return ret; -} - -char* sjson_encode(sjson_context* ctx, const sjson_node *node) -{ - return sjson_stringify(ctx, node, NULL); -} - -char* sjson_encode_string(sjson_context* ctx, const char *str) -{ - sjson__sb sb; - sjson__sb_init(ctx, &sb, ctx->str_buffer_size); - - sjson__emit_string(ctx, &sb, str); - - return sjson__sb_finish(&sb); -} - -char* sjson_stringify(sjson_context* ctx, const sjson_node *node, const char *space) -{ - sjson__sb sb; - sjson__sb_init(ctx, &sb, ctx->str_buffer_size); - - if (space != NULL) - sjson__emit_value_indented(ctx, &sb, node, space, 0); - else - sjson__emit_value(ctx, &sb, node); - - return sjson__sb_finish(&sb); -} - -void sjson_free_string(sjson_context* ctx, char* str) -{ - sjson_free(ctx->alloc_user, str); -} - -void sjson_delete_node(sjson_context* ctx, sjson_node *node) -{ - if (node != NULL) { - sjson_remove_from_parent(node); - - switch (node->tag) { - case SJSON_ARRAY: - case SJSON_OBJECT: - { - sjson_node *child, *next; - for (child = node->children.head; child != NULL; child = next) { - next = child->next; - sjson_delete_node(ctx, child); - } - break; - } - default:; - } - - sjson__del_node(ctx, node); - } -} - -bool sjson_validate(sjson_context* ctx, const char *json) -{ - const char *s = json; - - sjson__skip_space(&s); - if (!sjson__parse_value(ctx, &s, NULL)) - return false; - - sjson__skip_space(&s); - if (*s != 0) - return false; - - return true; -} - -sjson_node *sjson_find_element(sjson_node *array, int index) -{ - sjson_node *element; - int i = 0; - - if (array == NULL || array->tag != SJSON_ARRAY) - return NULL; - - sjson_foreach(element, array) { - if (i == index) - return element; - i++; - } - - return NULL; -} - -sjson_node *sjson_find_member(sjson_node *object, const char *name) -{ - sjson_node *member; - - if (object == NULL || object->tag != SJSON_OBJECT) - return NULL; - - sjson_foreach(member, object) - if (sjson_strcmp(member->key, name) == 0) - return member; - - return NULL; -} - -sjson_node* sjson_find_member_nocase(sjson_node* object, const char *name) -{ - sjson_node *member; - - if (object == NULL || object->tag != SJSON_OBJECT) - return NULL; - - sjson_foreach(member, object) - if (sjson_stricmp(member->key, name) == 0) - return member; - - return NULL; -} - -sjson_node *sjson_first_child(const sjson_node *node) -{ - if (node != NULL && (node->tag == SJSON_ARRAY || node->tag == SJSON_OBJECT)) - return node->children.head; - return NULL; -} - -int sjson_child_count(const sjson_node* node) -{ - int count = 0; - sjson_node* item; - sjson_foreach(item, node) ++count; - return count; -} - - -int sjson_get_int(sjson_node* parent, const char* key, int default_val) -{ - sjson_node* p = sjson_find_member(parent, key); - if (p) { - sjson_assert(p->tag == SJSON_NUMBER); - return (int)p->number_; - } else { - return default_val; - } -} - -float sjson_get_float(sjson_node* parent, const char* key, float default_val) -{ - sjson_node* p = sjson_find_member(parent, key); - if (p) { - sjson_assert(p->tag == SJSON_NUMBER); - return (float)p->number_; - } else { - return default_val; - } -} - -double sjson_get_double(sjson_node* parent, const char* key, double default_val) -{ - sjson_node* p = sjson_find_member(parent, key); - if (p) { - sjson_assert(p->tag == SJSON_NUMBER); - return p->number_; - } else { - return default_val; - } -} - -const char* sjson_get_string(sjson_node* parent, const char* key, const char* default_val) -{ - sjson_node* p = sjson_find_member(parent, key); - if (p) { - sjson_assert(p->tag == SJSON_STRING); - return p->string_; - } else { - return default_val; - } -} - -bool sjson_get_bool(sjson_node* parent, const char* key, bool default_val) -{ - sjson_node* p = sjson_find_member(parent, key); - if (p) { - sjson_assert(p->tag == SJSON_BOOL); - return p->bool_; - } else { - return default_val; - } -} - -bool sjson_get_floats(float* out, int count, sjson_node* parent, const char* key) -{ - sjson_node* p = key ? sjson_find_member(parent, key) : parent; - if (p) { - sjson_assert(p->tag == SJSON_ARRAY); - int index = 0; - for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { - sjson_assert(elem->tag == SJSON_NUMBER); - out[index++] = (float)elem->number_; - } - return index == count; - } else { - return false; - } -} - -int sjson_get_ints(int* out, int count, sjson_node* parent, const char* key) -{ - sjson_node* p = key ? sjson_find_member(parent, key) : parent; - if (p) { - sjson_assert(p->tag == SJSON_ARRAY); - int index = 0; - for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { - sjson_assert(elem->tag == SJSON_NUMBER); - out[index++] = (int)elem->number_; - } - return index; - } else { - return 0; - } -} - -bool sjson_get_uints(uint32_t* out, int count, sjson_node* parent, const char* key) -{ - sjson_node* p = key ? sjson_find_member(parent, key) : parent; - if (p) { - sjson_assert(p->tag == SJSON_ARRAY); - int index = 0; - for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { - sjson_assert(elem->tag == SJSON_NUMBER); - out[index++] = (uint32_t)elem->number_; - } - return index == count; - } else { - return false; - } -} - -bool sjson_get_int16s(int16_t* out, int count, sjson_node* parent, const char* key) -{ - sjson_node* p = key ? sjson_find_member(parent, key) : parent; - if (p) { - sjson_assert(p->tag == SJSON_ARRAY); - int index = 0; - for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { - sjson_assert(elem->tag == SJSON_NUMBER); - out[index++] = (int16_t)elem->number_; - } - return index == count; - } else { - return false; - } -} - -bool sjson_get_uint16s(uint16_t* out, int count, sjson_node* parent, const char* key) -{ - sjson_node* p = key ? sjson_find_member(parent, key) : parent; - if (p) { - sjson_assert(p->tag == SJSON_ARRAY); - int index = 0; - for (sjson_node* elem = p->children.head; elem && index < count; elem = elem->next) { - sjson_assert(elem->tag == SJSON_NUMBER); - out[index++] = (uint16_t)elem->number_; - } - return index == count; - } else { - return false; - } -} - -sjson_node *sjson_mknull(sjson_context* ctx) -{ - return sjson__new_node(ctx, SJSON_NULL); -} - -sjson_node *sjson_mkbool(sjson_context* ctx, bool b) -{ - sjson_node *ret = sjson__new_node(ctx, SJSON_BOOL); - ret->bool_ = b; - return ret; -} - -static sjson_node *sjson__mkstring(sjson_context* ctx, char *s) -{ - sjson_node *ret = sjson__new_node(ctx, SJSON_STRING); - ret->string_ = s; - return ret; -} - -sjson_node *sjson_mkstring(sjson_context* ctx, const char *s) -{ - return sjson__mkstring(ctx, sjson__strdup(ctx, s)); -} - -sjson_node *sjson_mknumber(sjson_context* ctx, double n) -{ - sjson_node *node = sjson__new_node(ctx, SJSON_NUMBER); - node->number_ = n; - return node; -} - -sjson_node *sjson_mkarray(sjson_context* ctx) -{ - return sjson__new_node(ctx, SJSON_ARRAY); -} - -sjson_node *sjson_mkobject(sjson_context* ctx) -{ - return sjson__new_node(ctx, SJSON_OBJECT); -} - -static void sjson__append_node(sjson_node *parent, sjson_node *child) -{ - child->parent = parent; - child->prev = parent->children.tail; - child->next = NULL; - - if (parent->children.tail != NULL) - parent->children.tail->next = child; - else - parent->children.head = child; - parent->children.tail = child; -} - -static void sjson__prepend_node(sjson_node *parent, sjson_node *child) -{ - child->parent = parent; - child->prev = NULL; - child->next = parent->children.head; - - if (parent->children.head != NULL) - parent->children.head->prev = child; - else - parent->children.tail = child; - parent->children.head = child; -} - -static void sjson__append_member(sjson_node *object, char *key, sjson_node *value) -{ - value->key = key; - sjson__append_node(object, value); -} - -void sjson_append_element(sjson_node *array, sjson_node *element) -{ - sjson_assert(array->tag == SJSON_ARRAY); - sjson_assert(element->parent == NULL); - - sjson__append_node(array, element); -} - -void sjson_prepend_element(sjson_node *array, sjson_node *element) -{ - sjson_assert(array->tag == SJSON_ARRAY); - sjson_assert(element->parent == NULL); - - sjson__prepend_node(array, element); -} - -void sjson_append_member(sjson_context* ctx, sjson_node *object, const char *key, sjson_node *value) -{ - sjson_assert(object->tag == SJSON_OBJECT); - sjson_assert(value->parent == NULL); - - sjson__append_member(object, sjson__strdup(ctx, key), value); -} - -void sjson_prepend_member(sjson_context* ctx, sjson_node *object, const char *key, sjson_node *value) -{ - sjson_assert(object->tag == SJSON_OBJECT); - sjson_assert(value->parent == NULL); - - value->key = sjson__strdup(ctx, key); - sjson__prepend_node(object, value); -} - -void sjson_remove_from_parent(sjson_node *node) -{ - sjson_node *parent = node->parent; - - if (parent != NULL) { - if (node->prev != NULL) - node->prev->next = node->next; - else - parent->children.head = node->next; - if (node->next != NULL) - node->next->prev = node->prev; - else - parent->children.tail = node->prev; - - node->parent = NULL; - node->prev = node->next = NULL; - node->key = NULL; - } -} - -sjson_node* sjson_put_obj(sjson_context* ctx, sjson_node* parent, const char* key) -{ - sjson_node* n = sjson_mkobject(ctx); - sjson_append_member(ctx, parent, key, n); - return n; -} - -sjson_node* sjson_put_array(sjson_context* ctx, sjson_node* parent, const char* key) -{ - sjson_node* n = sjson_mkarray(ctx); - sjson_append_member(ctx, parent, key, n); - return n; -} - -sjson_node* sjson_put_int(sjson_context* ctx, sjson_node* parent, const char* key, int val) -{ - sjson_node* n = sjson_mknumber(ctx, (double)val); - sjson_assert(n); - sjson_append_member(ctx, parent, key, n); - return n; -} - -sjson_node* sjson_put_float(sjson_context* ctx, sjson_node* parent, const char* key, float val) -{ - sjson_node* n = sjson_mknumber(ctx, (double)val); - sjson_assert(n); - sjson_append_member(ctx, parent, key, n); - return n; -} - -sjson_node* sjson_put_double(sjson_context* ctx, sjson_node* parent, const char* key, double val) -{ - sjson_node* n = sjson_mknumber(ctx, val); - sjson_assert(n); - sjson_append_member(ctx, parent, key, n); - return n; -} - -sjson_node* sjson_put_bool(sjson_context* ctx, sjson_node* parent, const char* key, bool val) -{ - sjson_node* n = sjson_mkbool(ctx, val); - sjson_assert(n); - sjson_append_member(ctx, parent, key, n); - return n; -} - -sjson_node* sjson_put_string(sjson_context* ctx, sjson_node* parent, const char* key, const char* val) -{ - sjson_node* n = sjson_mkstring(ctx, val); - sjson_assert(n); - sjson_append_member(ctx, parent, key, n); - return n; -} - -sjson_node* sjson_put_floats(sjson_context* ctx, sjson_node* parent, const char* key, const float* vals, int count) -{ - sjson_node* a = sjson_mkarray(ctx); - sjson_assert(a); - for (int i = 0; i < count; i++) { - sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); - sjson_assert(n); - sjson_append_element(a, n); - } - sjson_append_member(ctx, parent, key, a); - return a; -} - -sjson_node* sjson_put_ints(sjson_context* ctx, sjson_node* parent, const char* key, const int* vals, int count) -{ - sjson_node* a = sjson_mkarray(ctx); - sjson_assert(a); - for (int i = 0; i < count; i++) { - sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); - sjson_assert(n); - sjson_append_element(a, n); - } - sjson_append_member(ctx, parent, key, a); - return a; -} - -sjson_node* sjson_put_uints(sjson_context* ctx, sjson_node* parent, const char* key, const uint32_t* vals, int count) -{ - sjson_node* a = sjson_mkarray(ctx); - sjson_assert(a); - for (int i = 0; i < count; i++) { - sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); - sjson_assert(n); - sjson_append_element(a, n); - } - sjson_append_member(ctx, parent, key, a); - return a; -} - -sjson_node* sjson_put_int16s(sjson_context* ctx, sjson_node* parent, const char* key, const int16_t* vals, int count) -{ - sjson_node* a = sjson_mkarray(ctx); - sjson_assert(a); - for (int i = 0; i < count; i++) { - sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); - sjson_assert(n); - sjson_append_element(a, n); - } - sjson_append_member(ctx, parent, key, a); - return a; -} - -sjson_node* sjson_put_uint16s(sjson_context* ctx, sjson_node* parent, const char* key, const uint16_t* vals, int count) -{ - sjson_node* a = sjson_mkarray(ctx); - sjson_assert(a); - for (int i = 0; i < count; i++) { - sjson_node* n = sjson_mknumber(ctx, (double)vals[i]); - sjson_assert(n); - sjson_append_element(a, n); - } - sjson_append_member(ctx, parent, key, a); - return a; -} - -sjson_node* sjson_put_strings(sjson_context* ctx, sjson_node* parent, const char* key, const char** vals, int count) -{ - sjson_node* a = sjson_mkarray(ctx); - sjson_assert(a); - for (int i = 0; i < count; i++) { - sjson_node* n = sjson_mkstring(ctx, vals[i]); - sjson_assert(n); - sjson_append_element(a, n); - } - sjson_append_member(ctx, parent, key, a); - return a; -} - -static bool sjson__parse_value(sjson_context* ctx, const char **sp, sjson_node **out) -{ - const char *s = *sp; - - switch (*s) { - case 'n': - if (sjson__expect_literal(&s, "null")) { - if (out) - *out = sjson_mknull(ctx); - *sp = s; - return true; - } - return false; - - case 'f': - if (sjson__expect_literal(&s, "false")) { - if (out) - *out = sjson_mkbool(ctx, false); - *sp = s; - return true; - } - return false; - - case 't': - if (sjson__expect_literal(&s, "true")) { - if (out) - *out = sjson_mkbool(ctx, true); - *sp = s; - return true; - } - return false; - - case '"': { - char *str; - if (sjson__parse_string(ctx, &s, out ? &str : NULL)) { - if (out) - *out = sjson__mkstring(ctx, str); - *sp = s; - return true; - } - return false; - } - - case '[': - if (sjson__parse_array(ctx, &s, out)) { - *sp = s; - return true; - } - return false; - - case '{': - if (sjson__parse_object(ctx, &s, out)) { - *sp = s; - return true; - } - return false; - - default: { - double num; - if (sjson__parse_number(&s, out ? &num : NULL)) { - if (out) - *out = sjson_mknumber(ctx, num); - *sp = s; - return true; - } - return false; - } - } -} - -static bool sjson__parse_array(sjson_context* ctx, const char **sp, sjson_node **out) -{ - const char *s = *sp; - sjson_node *ret = out ? sjson_mkarray(ctx) : NULL; - sjson_node *element; - - if (*s++ != '[') - goto failure; - sjson__skip_space(&s); - - if (*s == ']') { - s++; - goto success; - } - - for (;;) { - if (!sjson__parse_value(ctx, &s, out ? &element : NULL)) - goto failure; - sjson__skip_space(&s); - - if (out) - sjson_append_element(ret, element); - - if (*s == ']') { - s++; - goto success; - } - - if (*s++ != ',') - goto failure; - sjson__skip_space(&s); - } - -success: - *sp = s; - if (out) - *out = ret; - return true; - -failure: - sjson_delete_node(ctx, ret); - return false; -} - -static bool sjson__parse_object(sjson_context* ctx, const char **sp, sjson_node **out) -{ - const char *s = *sp; - sjson_node *ret = out ? sjson_mkobject(ctx) : NULL; - char *key; - sjson_node *value; - - if (*s++ != '{') - goto failure; - sjson__skip_space(&s); - - if (*s == '}') { - s++; - goto success; - } - - for (;;) { - if (!sjson__parse_string(ctx, &s, out ? &key : NULL)) - goto failure; - sjson__skip_space(&s); - - if (*s++ != ':') - goto failure_free_key; - sjson__skip_space(&s); - - if (!sjson__parse_value(ctx, &s, out ? &value : NULL)) - goto failure_free_key; - sjson__skip_space(&s); - - if (out) - sjson__append_member(ret, key, value); - - if (*s == '}') { - s++; - goto success; - } - - if (*s++ != ',') - goto failure; - sjson__skip_space(&s); - } - -success: - *sp = s; - if (out) - *out = ret; - return true; - -failure_free_key: -failure: - sjson_delete_node(ctx, ret); - return false; -} - -static bool sjson__parse_string(sjson_context* ctx, const char **sp, char **out) -{ - const char* s = *sp; - char throwaway_buffer[4]; /* enough space for a UTF-8 character */ - char* b; - char* src = NULL; - - if (*s++ != '"') - return false; - - if (out) { - b = sjson__str_begin(ctx, 4); - src = b; - } else { - b = throwaway_buffer; - } - - while (*s != '"') { - unsigned char c = *s++; - - /* Parse next character, and write it to b. */ - if (c == '\\') { - c = *s++; - switch (c) { - case '"': - case '\\': - case '/': - *b++ = c; - break; - case 'b': - *b++ = '\b'; - break; - case 'f': - *b++ = '\f'; - break; - case 'n': - *b++ = '\n'; - break; - case 'r': - *b++ = '\r'; - break; - case 't': - *b++ = '\t'; - break; - case 'u': - { - uint16_t uc, lc; - json__uchar_t unicode; - - if (!sjson__parse_hex16(&s, &uc)) - goto failed; - - if (uc >= 0xD800 && uc <= 0xDFFF) { - /* Handle UTF-16 surrogate pair. */ - if (*s++ != '\\' || *s++ != 'u' || !sjson__parse_hex16(&s, &lc)) - goto failed; /* Incomplete surrogate pair. */ - if (!sjson__from_surrogate_pair(uc, lc, &unicode)) - goto failed; /* Invalid surrogate pair. */ - } else if (uc == 0) { - /* Disallow "\u0000". */ - goto failed; - } else { - unicode = uc; - } - - b += sjson__utf8_write_char(unicode, b); - break; - } - default: - /* Invalid escape */ - goto failed; - } - } else if (c <= 0x1F) { - /* Control characters are not allowed in string literals. */ - goto failed; - } else { - /* Validate and echo a UTF-8 character. */ - int len; - - s--; - len = sjson__utf8_validate_cz(s); - if (len == 0) - goto failed; /* Invalid UTF-8 character. */ - - while (len--) - *b++ = *s++; - } - - /* - * Update sb to know about the new bytes, - * and set up b to write another character. - */ - if (out) { - int offset = (int)(uintptr_t)(b - src); - src = sjson__str_grow(ctx, 4); - b = src + offset; - } else { - b = throwaway_buffer; - } - } - s++; - - if (out) { - *b = '\0'; - *out = sjson__str_end(ctx); - } - *sp = s; - return true; - -failed: - if (out) - sjson__str_end(ctx); - return false; -} - -/* - * The JSON spec says that a number shall follow this precise pattern - * (spaces and quotes added for readability): - * '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)? - * - * However, some JSON parsers are more liberal. For instance, PHP accepts - * '.5' and '1.'. JSON.parse accepts '+3'. - * - * This function takes the strict approach. - */ -bool sjson__parse_number(const char **sp, double *out) -{ - const char *s = *sp; - - /* '-'? */ - if (*s == '-') - s++; - - /* (0 | [1-9][0-9]*) */ - if (*s == '0') { - s++; - } else { - if (!sjson__is_digit(*s)) - return false; - do { - s++; - } while (sjson__is_digit(*s)); - } - - /* ('.' [0-9]+)? */ - if (*s == '.') { - s++; - if (!sjson__is_digit(*s)) - return false; - do { - s++; - } while (sjson__is_digit(*s)); - } - - /* ([Ee] [+-]? [0-9]+)? */ - if (*s == 'E' || *s == 'e') { - s++; - if (*s == '+' || *s == '-') - s++; - if (!sjson__is_digit(*s)) - return false; - do { - s++; - } while (sjson__is_digit(*s)); - } - - if (out) - *out = strtod(*sp, NULL); - - *sp = s; - return true; -} - -static void sjson__skip_space(const char **sp) -{ - const char *s = *sp; - while (sjson__is_space(*s)) - s++; - *sp = s; -} - -static void sjson__emit_value(sjson_context* ctx, sjson__sb* out, const sjson_node *node) -{ - sjson_assert(sjson__tag_is_valid(node->tag)); - switch (node->tag) { - case SJSON_NULL: - sjson__sb_puts(ctx, out, "null"); - break; - case SJSON_BOOL: - sjson__sb_puts(ctx, out, node->bool_ ? "true" : "false"); - break; - case SJSON_STRING: - sjson__emit_string(ctx, out, node->string_); - break; - case SJSON_NUMBER: - sjson__emit_number(ctx, out, node->number_); - break; - case SJSON_ARRAY: - sjson__emit_array(ctx, out, node); - break; - case SJSON_OBJECT: - sjson__emit_object(ctx, out, node); - break; - default: - sjson_assert(false); - } -} - -void sjson__emit_value_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *node, const char *space, int indent_level) -{ - sjson_assert(sjson__tag_is_valid(node->tag)); - switch (node->tag) { - case SJSON_BOOL: - sjson__sb_puts(ctx, out, node->bool_ ? "true" : "false"); - break; - case SJSON_STRING: - sjson__emit_string(ctx, out, node->string_); - break; - case SJSON_NUMBER: - sjson__emit_number(ctx, out, node->number_); - break; - case SJSON_ARRAY: - sjson__emit_array_indented(ctx, out, node, space, indent_level); - break; - case SJSON_OBJECT: - sjson__emit_object_indented(ctx, out, node, space, indent_level); - break; - case SJSON_NULL: - sjson__sb_puts(ctx, out, "null"); - break; - default: - sjson_assert(false); - } -} - -static void sjson__emit_array(sjson_context* ctx, sjson__sb* out, const sjson_node *array) -{ - const sjson_node *element; - - sjson__sb_putc(ctx, out, '['); - sjson_foreach(element, array) { - sjson__emit_value(ctx, out, element); - if (element->next != NULL) - sjson__sb_putc(ctx, out, ','); - } - sjson__sb_putc(ctx, out, ']'); -} - -static void sjson__emit_array_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *array, const char *space, int indent_level) -{ - const sjson_node *element = array->children.head; - int i; - - if (element == NULL) { - sjson__sb_puts(ctx, out, "[]"); - return; - } - - sjson__sb_puts(ctx, out, "[\n"); - while (element != NULL) { - for (i = 0; i < indent_level + 1; i++) - sjson__sb_puts(ctx, out, space); - sjson__emit_value_indented(ctx, out, element, space, indent_level + 1); - - element = element->next; - sjson__sb_puts(ctx, out, element != NULL ? ",\n" : "\n"); - } - for (i = 0; i < indent_level; i++) - sjson__sb_puts(ctx, out, space); - sjson__sb_putc(ctx, out, ']'); -} - -static void sjson__emit_object(sjson_context* ctx, sjson__sb* out, const sjson_node *object) -{ - const sjson_node *member; - - sjson__sb_putc(ctx, out, '{'); - sjson_foreach(member, object) { - sjson__emit_string(ctx, out, member->key); - sjson__sb_putc(ctx, out, ':'); - sjson__emit_value(ctx, out, member); - if (member->next != NULL) - sjson__sb_putc(ctx, out, ','); - } - sjson__sb_putc(ctx, out, '}'); -} - -static void sjson__emit_object_indented(sjson_context* ctx, sjson__sb* out, const sjson_node *object, const char *space, int indent_level) -{ - const sjson_node *member = object->children.head; - int i; - - if (member == NULL) { - sjson__sb_puts(ctx, out, "{}"); - return; - } - - sjson__sb_puts(ctx, out, "{\n"); - while (member != NULL) { - for (i = 0; i < indent_level + 1; i++) - sjson__sb_puts(ctx, out, space); - sjson__emit_string(ctx, out, member->key); - sjson__sb_puts(ctx, out, ": "); - sjson__emit_value_indented(ctx, out, member, space, indent_level + 1); - - member = member->next; - sjson__sb_puts(ctx, out, member != NULL ? ",\n" : "\n"); - } - for (i = 0; i < indent_level; i++) - sjson__sb_puts(ctx, out, space); - sjson__sb_putc(ctx, out, '}'); -} - -void sjson__emit_string(sjson_context* ctx, sjson__sb* out, const char *str) -{ - bool escape_unicode = false; - const char *s = str; - char *b; - - sjson_assert(sjson__utf8_validate(str)); - - /* - * 14 bytes is enough space to write up to two - * \uXXXX escapes and two quotation marks. - */ - sjson__sb_need(ctx, out, 14); - b = out->cur; - - *b++ = '"'; - while (*s != 0) { - unsigned char c = *s++; - - /* Encode the next character, and write it to b. */ - switch (c) { - case '"': - *b++ = '\\'; - *b++ = '"'; - break; - case '\\': - *b++ = '\\'; - *b++ = '\\'; - break; - case '\b': - *b++ = '\\'; - *b++ = 'b'; - break; - case '\f': - *b++ = '\\'; - *b++ = 'f'; - break; - case '\n': - *b++ = '\\'; - *b++ = 'n'; - break; - case '\r': - *b++ = '\\'; - *b++ = 'r'; - break; - case '\t': - *b++ = '\\'; - *b++ = 't'; - break; - default: { - int len; - - s--; - len = sjson__utf8_validate_cz(s); - - if (len == 0) { - /* - * Handle invalid UTF-8 character gracefully in production - * by writing a replacement character (U+FFFD) - * and skipping a single byte. - * - * This should never happen when assertions are enabled - * due to the assertion at the beginning of this function. - */ - sjson_assert(false); - if (escape_unicode) { - sjson_strcpy(b, 7, "\\uFFFD"); - b += 6; - } else { - *b++ = (char)0xEF; - *b++ = (char)0xBF; - *b++ = (char)0xBD; - } - s++; - } else if (c < 0x1F || (c >= 0x80 && escape_unicode)) { - /* Encode using \u.... */ - uint32_t unicode; - - s += sjson__utf8_read_char(s, &unicode); - - if (unicode <= 0xFFFF) { - *b++ = '\\'; - *b++ = 'u'; - b += sjson__write_hex16(b, unicode); - } else { - /* Produce a surrogate pair. */ - uint16_t uc, lc; - sjson_assert(unicode <= 0x10FFFF); - sjson__to_surrogate_pair(unicode, &uc, &lc); - *b++ = '\\'; - *b++ = 'u'; - b += sjson__write_hex16(b, uc); - *b++ = '\\'; - *b++ = 'u'; - b += sjson__write_hex16(b, lc); - } - } else { - /* Write the character directly. */ - while (len--) - *b++ = *s++; - } - - break; - } - } - - /* - * Update *out to know about the new bytes, - * and set up b to write another encoded character. - */ - out->cur = b; - sjson__sb_need(ctx, out, 14); - b = out->cur; - } - *b++ = '"'; - - out->cur = b; -} - -static void sjson__emit_number(sjson_context* ctx, sjson__sb* out, double num) -{ - /* - * This isn't exactly how JavaScript renders numbers, - * but it should produce valid JSON for reasonable numbers - * preserve precision well enough, and avoid some oddities - * like 0.3 -> 0.299999999999999988898 . - */ - char buf[64]; - sjson_snprintf(buf, sizeof(buf), "%.16g", num); - - if (sjson__number_is_valid(buf)) - sjson__sb_puts(ctx, out, buf); - else - sjson__sb_puts(ctx, out, "null"); -} - -static bool sjson__tag_is_valid(unsigned int tag) -{ - return (/* tag >= SJSON_NULL && */ tag <= SJSON_OBJECT); -} - -static bool sjson__number_is_valid(const char *num) -{ - return (sjson__parse_number(&num, NULL) && *num == '\0'); -} - -static bool sjson__expect_literal(const char **sp, const char *str) -{ - const char *s = *sp; - - while (*str != '\0') - if (*s++ != *str++) - return false; - - *sp = s; - return true; -} - -/* - * Parses exactly 4 hex characters (capital or lowercase). - * Fails if any input chars are not [0-9A-Fa-f]. - */ -static bool sjson__parse_hex16(const char **sp, uint16_t *out) -{ - const char *s = *sp; - uint16_t ret = 0; - uint16_t i; - uint16_t tmp; - char c; - - for (i = 0; i < 4; i++) { - c = *s++; - if (c >= '0' && c <= '9') - tmp = c - '0'; - else if (c >= 'A' && c <= 'F') - tmp = c - 'A' + 10; - else if (c >= 'a' && c <= 'f') - tmp = c - 'a' + 10; - else - return false; - - ret <<= 4; - ret += tmp; - } - - if (out) - *out = ret; - *sp = s; - return true; -} - -/* - * Encodes a 16-bit number into hexadecimal, - * writing exactly 4 hex chars. - */ -static int sjson__write_hex16(char *out, uint16_t val) -{ - const char *hex = "0123456789ABCDEF"; - - *out++ = hex[(val >> 12) & 0xF]; - *out++ = hex[(val >> 8) & 0xF]; - *out++ = hex[(val >> 4) & 0xF]; - *out++ = hex[ val & 0xF]; - - return 4; -} - -bool sjson_check(const sjson_node* node, char errmsg[256]) -{ - #define problem(...) do { \ - if (errmsg != NULL) \ - sjson_snprintf(errmsg, 256, __VA_ARGS__); \ - return false; \ - } while (0) - - if (node->key != NULL && !sjson__utf8_validate(node->key)) - problem("key contains invalid UTF-8"); - - if (!sjson__tag_is_valid(node->tag)) - problem("tag is invalid (%u)", node->tag); - - if (node->tag == SJSON_BOOL) { -#if 0 - if (node->bool_ != false && node->bool_ != true) - problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true); -#endif - } else if (node->tag == SJSON_STRING) { - if (node->string_ == NULL) - problem("string_ is NULL"); - if (!sjson__utf8_validate(node->string_)) - problem("string_ contains invalid UTF-8"); - } else if (node->tag == SJSON_ARRAY || node->tag == SJSON_OBJECT) { - sjson_node *head = node->children.head; - sjson_node *tail = node->children.tail; - - if (head == NULL || tail == NULL) { - if (head != NULL) - problem("tail is NULL, but head is not"); - if (tail != NULL) - problem("head is NULL, but tail is not"); - } else { - sjson_node *child; - sjson_node *last = NULL; - - if (head->prev != NULL) - problem("First child's prev pointer is not NULL"); - - for (child = head; child != NULL; last = child, child = child->next) { - if (child == node) - problem("node is its own child"); - if (child->next == child) - problem("child->next == child (cycle)"); - if (child->next == head) - problem("child->next == head (cycle)"); - - if (child->parent != node) - problem("child does not point back to parent"); - if (child->next != NULL && child->next->prev != child) - problem("child->next does not point back to child"); - - if (node->tag == SJSON_ARRAY && child->key != NULL) - problem("Array element's key is not NULL"); - if (node->tag == SJSON_OBJECT && child->key == NULL) - problem("Object member's key is NULL"); - - if (!sjson_check(child, errmsg)) - return false; - } - - if (last != tail) - problem("tail does not match pointer found by starting at head and following next links"); - } - } - - return true; - - #undef problem -} - -#endif // SJSON_IMPLEMENT - -// -// Version History: -// 1.0.0 Initial release -// 1.1.0 Added higher level json get/put functions -// Implemented sjson_reset_context -// 1.1.1 Fixed some macro defines (sjson_strcpy, sjson_snprintf) -// diff --git a/source/thirdparty/src/sjson.cpp b/source/thirdparty/src/sjson.cpp deleted file mode 100644 index aee8cce40..000000000 --- a/source/thirdparty/src/sjson.cpp +++ /dev/null @@ -1,5 +0,0 @@ - -#include "compat.h" - -#define SJSON_IMPLEMENT -#include "sjson.h"