From 55b64ffa45464dea4a4ef385e85e39d7612170e9 Mon Sep 17 00:00:00 2001 From: falsycat Date: Fri, 27 Aug 2021 16:42:58 +0900 Subject: [PATCH] Enhances audio effects.Adds TitleScene. --- GlyphsJuke.vcxproj | 4 + GlyphsJuke.vcxproj.filters | 12 + src/AudioDevice.cc | 16 +- src/AudioDevice.h | 14 +- src/Font.h | 2 +- src/Game.cc | 9 +- src/GlitchPosteffect.h | 48 ++ src/GlyphElementFactory.h | 2 +- src/HiraganaMatcher.cc | 9 +- src/Lua.cc | 3 + src/MusicElement.h | 33 +- src/MusicElementFactory.h | 13 +- src/PlayScene.cc | 19 +- src/PlayScene.h | 17 +- src/ResultScene.cc | 12 +- src/ResultScene.h | 8 +- src/Texture.cc | 2 +- src/TitleScene.cc | 129 ++++ src/TitleScene.h | 53 ++ src/common.h | 8 + src/iAudioDevice.h | 5 +- src/iScene.h | 8 + thirdparty/picojson.h | 1200 ++++++++++++++++++++++++++++++++++++ 23 files changed, 1573 insertions(+), 53 deletions(-) create mode 100644 src/GlitchPosteffect.h create mode 100644 src/TitleScene.cc create mode 100644 src/TitleScene.h create mode 100644 thirdparty/picojson.h diff --git a/GlyphsJuke.vcxproj b/GlyphsJuke.vcxproj index 68b0abc..5ed0e04 100644 --- a/GlyphsJuke.vcxproj +++ b/GlyphsJuke.vcxproj @@ -155,6 +155,7 @@ + @@ -183,6 +184,7 @@ + @@ -197,6 +199,7 @@ + @@ -204,6 +207,7 @@ + diff --git a/GlyphsJuke.vcxproj.filters b/GlyphsJuke.vcxproj.filters index 0e30ff5..9e195cc 100644 --- a/GlyphsJuke.vcxproj.filters +++ b/GlyphsJuke.vcxproj.filters @@ -48,6 +48,9 @@ Source Files + + Source Files + @@ -197,6 +200,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/src/AudioDevice.cc b/src/AudioDevice.cc index 6b475dc..bfade3b 100644 --- a/src/AudioDevice.cc +++ b/src/AudioDevice.cc @@ -29,7 +29,7 @@ gj::AudioDevice::~AudioDevice() { } } -void gj::AudioDevice::PlayMusic(const std::string& path) { +void gj::AudioDevice::PlayMusic(const std::string& path, double offset) { std::lock_guard _(mtx_); if (playing_) { @@ -39,6 +39,11 @@ void gj::AudioDevice::PlayMusic(const std::string& path) { if (ma_decoder_init_file(path.c_str(), &config, &dec_) != MA_SUCCESS) { Abort("AudioDevice decoder error: "+path); } + + const uint64_t offset_frame = static_cast(kSampleRate * offset); + if (ma_decoder_seek_to_pcm_frame(&dec_, offset_frame) != MA_SUCCESS) { + Abort("decoder seek failure"); + } playing_ = true; } @@ -54,6 +59,9 @@ void gj::AudioDevice::Callback_(ma_device* ma, void* out, const void* in, ma_uin AudioDevice* dev = reinterpret_cast(ma->pUserData); std::lock_guard _(dev->mtx_); + const double amp = dev->amp_.load(); + const double lpf = dev->lpf_coe_.load(); + float* dst = reinterpret_cast(out); size_t wrote = 0; @@ -65,5 +73,11 @@ void gj::AudioDevice::Callback_(ma_device* ma, void* out, const void* in, ma_uin } wrote += n; } + + for (size_t i = 0; i < static_cast(framecnt)*kChannel; ++i) { + dst[i] = static_cast(dst[i]*amp); + dst[i] = static_cast(dev->lpf_prev_*lpf + dst[i]*(1-lpf)); + dev->lpf_prev_ = dst[i]; + } dev->time_.fetch_add(framecnt); } \ No newline at end of file diff --git a/src/AudioDevice.h b/src/AudioDevice.h index 3b8f02f..138612a 100644 --- a/src/AudioDevice.h +++ b/src/AudioDevice.h @@ -32,9 +32,16 @@ class AudioDevice : public iAudioDevice, public iClock { AudioDevice(); ~AudioDevice(); - void PlayMusic(const std::string& path) override; + void PlayMusic(const std::string& path, double offset) override; void StopMusic() override; + void SetVolume(double amp) override { + amp_.store(amp); + } + void SetLpfIntensity(double v) override { + lpf_coe_.store(v); + } + uint64_t now() const override { return time_.load() * 1000 / kSampleRate; } @@ -47,6 +54,11 @@ class AudioDevice : public iAudioDevice, public iClock { bool playing_ = false; ma_decoder dec_{0}; + std::atomic amp_ = 1; + + std::atomic lpf_coe_ = 0; + float lpf_prev_ = 0; + std::atomic time_; static void Callback_(ma_device* ma, void* out, const void* in, ma_uint32 framecnt); diff --git a/src/Font.h b/src/Font.h index 175dbfd..005d8f6 100644 --- a/src/Font.h +++ b/src/Font.h @@ -14,7 +14,7 @@ namespace gj { - class Font { +class Font { public: Font() = delete; Font(Font&&) = delete; diff --git a/src/Game.cc b/src/Game.cc index 7a5c03f..69b95c2 100644 --- a/src/Game.cc +++ b/src/Game.cc @@ -1,5 +1,7 @@ #include "Game.h" -#include "PlayScene.h" + +#include "iScene.h" +#include "TitleScene.h" gj::Game::Game(gj::Game::Param&& p) : @@ -8,10 +10,9 @@ gj::Game::Game(gj::Game::Param&& p) : logger_(p.h), w_(p.w), h_(p.h), frame_(p.w, p.h, kReserveDrawable, kReserveWritable) { - gj::PlayScene::Param param; + gj::iScene::Param param; param.alloc = alloc_; param.clock = &clock_; param.audio = p.audio; - param.score = "test"; /* TODO test */ - scene_ = alloc_->MakeUniq(std::move(param)); + scene_ = alloc_->MakeUniq(param); } \ No newline at end of file diff --git a/src/GlitchPosteffect.h b/src/GlitchPosteffect.h new file mode 100644 index 0000000..e62c487 --- /dev/null +++ b/src/GlitchPosteffect.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include "iDrawable.h" + + +namespace gj { + + +class GlitchPosteffect : public iDrawable { + public: + GlitchPosteffect(GlitchPosteffect&&) = delete; + GlitchPosteffect(const GlitchPosteffect&) = delete; + + GlitchPosteffect& operator=(GlitchPosteffect&&) = delete; + GlitchPosteffect& operator=(const GlitchPosteffect&) = delete; + + GlitchPosteffect() { + } + + void Draw(Colorbuffer& fb) const override { + const int32_t w = static_cast(fb.width()); + const int32_t h = static_cast(fb.height()); + + float* ptr = fb.ptr(); + + for (int32_t y = 0; y < h; ++y) { + const double shift = (XorShift(seed+y)%100/100.*2-1)*maxShift; + const int32_t s = static_cast(w*shift); + if (std::abs(shift) > 1) continue; + + const size_t offset = static_cast(y) * w; + float* src = ptr + offset; + float* dst = ptr + offset + s; + if (src > dst) std::swap(src, dst); + + std::memmove(dst, src, static_cast(w) - std::abs(s)); + } + } + + uint64_t seed = 1; + double maxShift = 0; +}; + + +} \ No newline at end of file diff --git a/src/GlyphElementFactory.h b/src/GlyphElementFactory.h index 9ddf32e..1b07633 100644 --- a/src/GlyphElementFactory.h +++ b/src/GlyphElementFactory.h @@ -42,7 +42,7 @@ class GlyphElementFactory : public iElementFactory { if (found != fonts_.end()) { return *found->second; } - auto f = alloc_->MakeUniq(alloc_, "res/font/"+name); + auto f = alloc_->MakeUniq(alloc_, name); auto ptr = f.get(); fonts_[name] = std::move(f); return *ptr; diff --git a/src/HiraganaMatcher.cc b/src/HiraganaMatcher.cc index 5e396ee..99b9129 100644 --- a/src/HiraganaMatcher.cc +++ b/src/HiraganaMatcher.cc @@ -189,10 +189,9 @@ bool gj::HiraganaMatcher::Input_(wchar_t c, bool force_cut) { if (itr.size() == newbuf.size()) { comp_match = i; if (force_cut) { - buffer_ = {c}; + buffer_ = L""; state_.match += i; - UpdateExpects_(); - return true; + return Input_(c, false); } } else { part_match = i; @@ -248,6 +247,8 @@ void gj::HiraganaMatcher::UpdateExpects_() { } remain = remain.substr(len); } - if (prev == remain.size()) Abort("invalid pattern for InputWin"); + if (prev == remain.size()) { + Abort("invalid pattern for InputWin"); + } } } \ No newline at end of file diff --git a/src/Lua.cc b/src/Lua.cc index 63d3382..dad16ab 100644 --- a/src/Lua.cc +++ b/src/Lua.cc @@ -1,5 +1,7 @@ #include "Lua.h" +#include "thirdparty/lualib.h" + struct LuaPusher { LuaPusher() = delete; @@ -142,6 +144,7 @@ gj::Lua::Lua(iAllocator* alloc, ElementStore* store, const FactoryMap& factory, if (L == nullptr) { Abort("lua_newstate failure"); } + luaopen_math(L); for (const auto& f : factory) { lua_pushstring(L, f.first.c_str()); diff --git a/src/MusicElement.h b/src/MusicElement.h index 38acbc6..c187318 100644 --- a/src/MusicElement.h +++ b/src/MusicElement.h @@ -12,6 +12,17 @@ namespace gj { class MusicElement : public iElement { public: + struct Param { + iAudioDevice* audio; + + Period period; + + std::string path; + double offset; + + UniqPtr driver; + }; + MusicElement() = delete; MusicElement(MusicElement&&) = delete; MusicElement(const MusicElement&) = delete; @@ -19,13 +30,24 @@ public: MusicElement& operator=(MusicElement&&) = delete; MusicElement& operator=(const MusicElement&) = delete; - MusicElement(const Period& p, iAudioDevice* audio, const std::string& name) : - iElement(p), audio_(audio), path_("res/music/"+name) { + MusicElement(Param&& p) : + iElement(p.period), audio_(p.audio), path_(p.path), offset_(p.offset), + drv_(std::move(p.driver)) { + param_["volume"] = 0.; + param_["lpf"] = 0.; } void Update(Frame& frame, double t) override { + drv_->Update(param_, t); + + const double volume = std::get(param_["volume"]); + const double lpf = std::get(param_["lpf"]); + + audio_->SetVolume(volume); + audio_->SetLpfIntensity(lpf); + if (first_) { - audio_->PlayMusic(path_); + audio_->PlayMusic(path_, offset_); first_ = false; } } @@ -41,6 +63,11 @@ public: std::string path_; bool first_ = true; + + double offset_; + + UniqPtr drv_; + iElementDriver::Param param_; }; diff --git a/src/MusicElementFactory.h b/src/MusicElementFactory.h index fe7037c..67a667e 100644 --- a/src/MusicElementFactory.h +++ b/src/MusicElementFactory.h @@ -22,11 +22,18 @@ class MusicElementFactory : public iElementFactory { } UniqPtr Create(Param&& param) override { - if (param.custom.size() != 1) return nullptr; + if (param.custom.size() != 2) return nullptr; - const std::string name = std::get(param.custom[0]); + const std::string path = std::get(param.custom[0]); + const double offset = std::get(param.custom[1]); - return alloc_->MakeUniq(param.period, audio_, name); + MusicElement::Param p; + p.audio = audio_; + p.period = param.period; + p.path = path; + p.offset = offset; + p.driver = std::move(param.driver); + return alloc_->MakeUniq(std::move(p)); } private: diff --git a/src/PlayScene.cc b/src/PlayScene.cc index 51270e5..dd927aa 100644 --- a/src/PlayScene.cc +++ b/src/PlayScene.cc @@ -7,29 +7,28 @@ #include "ResultScene.h" -gj::PlayScene::PlayScene(Param&& p) : - alloc_(p.alloc), audio_(p.audio), - clock_(p.clock), store_(&clock_, 256) { +gj::PlayScene::PlayScene(const Param& p, const std::string& title, const std::string& path) : + param_(p), clock_(p.clock), store_(&clock_, 256) { - GlyphElementFactory glyph(alloc_); - InputWindowElementFactory inputWin(alloc_, &sb_); - MusicElementFactory music(alloc_, audio_); + GlyphElementFactory glyph(p.alloc); + InputWindowElementFactory inputWin(p.alloc, &sb_); + MusicElementFactory music(p.alloc, p.audio); - sb_.title = ConvertStrToWstr(p.score); + sb_.title = ConvertStrToWstr(title); Lua::FactoryMap map = { { "Glyph", &glyph }, { "InputWin", &inputWin }, { "Music", &music }, }; - lua_ = alloc_->MakeUniq( - alloc_, &store_, map, "res/score/" + p.score + ".lua"); + lua_ = p.alloc->MakeUniq( + p.alloc, &store_, map, path); } gj::UniqPtr gj::PlayScene::Update(Frame& f) { if (store_.IsEmpty()) { - return alloc_->MakeUniq(alloc_, clock_.parent(), sb_); + return param_.alloc->MakeUniq(param_, sb_); } store_.Update(f); diff --git a/src/PlayScene.h b/src/PlayScene.h index 6ef21b3..9332a9f 100644 --- a/src/PlayScene.h +++ b/src/PlayScene.h @@ -2,10 +2,6 @@ #include "ElementStore.h" -#include "Frame.h" -#include "iAllocator.h" -#include "iAudioDevice.h" -#include "iLogger.h" #include "iScene.h" #include "Lua.h" #include "OffsetClock.h" @@ -17,14 +13,6 @@ namespace gj { class PlayScene : public iScene { public: - struct Param { - iAllocator* alloc; - iAudioDevice* audio; - const iClock* clock; - - std::string score; - }; - PlayScene() = delete; PlayScene(PlayScene&&) = delete; PlayScene(const PlayScene&) = delete; @@ -32,13 +20,12 @@ class PlayScene : public iScene { PlayScene& operator=(PlayScene&&) = delete; PlayScene& operator=(const PlayScene&) = delete; - PlayScene(Param&& p); + PlayScene(const Param& p, const std::string& title, const std::string& path); UniqPtr Update(Frame& f) override; private: - iAllocator* alloc_; - iAudioDevice* audio_; + Param param_; OffsetClock clock_; diff --git a/src/ResultScene.cc b/src/ResultScene.cc index bbc9951..fca5a6e 100644 --- a/src/ResultScene.cc +++ b/src/ResultScene.cc @@ -1,8 +1,10 @@ #include "ResultScene.h" +#include "TitleScene.h" -gj::ResultScene::ResultScene(iAllocator* alloc, const iClock* clock, const Scoreboard& sb) : - alloc_(alloc), clock_(clock), sb_(sb), + +gj::ResultScene::ResultScene(const Param& p, const Scoreboard& sb) : + param_(p), clock_(p.clock), sb_(sb), title_(sb.title), correct_label_(L"CORRECT TYPES"), correct_num_(std::to_wstring(sb.correct)), @@ -10,7 +12,7 @@ gj::ResultScene::ResultScene(iAllocator* alloc, const iClock* clock, const Score line_label_(L"COMPLETE LINES"), line_num_(std::to_wstring(sb.completeLines)), line_den_(std::to_wstring(sb.lines)), - guide_(L"~ PRESS ENTER ~") { + guide_(L"~ PRESS SPACE ~") { } gj::UniqPtr gj::ResultScene::Update(Frame& f) { @@ -51,5 +53,9 @@ gj::UniqPtr gj::ResultScene::Update(Frame& f) { guide_.SetPosition((w-guide_.width())/2, static_cast(h*.8)); f.Add(&guide_); + if (f.input.find(' ') != std::string::npos) { + return param_.alloc->MakeUniq(param_); + } + return nullptr; } \ No newline at end of file diff --git a/src/ResultScene.h b/src/ResultScene.h index 14b6d52..9ee0500 100644 --- a/src/ResultScene.h +++ b/src/ResultScene.h @@ -1,8 +1,5 @@ #pragma once -#include "ElementStore.h" -#include "Frame.h" -#include "iAllocator.h" #include "iScene.h" #include "OffsetClock.h" #include "Scoreboard.h" @@ -21,12 +18,13 @@ class ResultScene : public iScene { ResultScene& operator=(ResultScene&&) = delete; ResultScene& operator=(const ResultScene&) = delete; - ResultScene(iAllocator* alloc, const iClock* clock, const Scoreboard& sb); + ResultScene(const Param& p, const Scoreboard& sb); UniqPtr Update(Frame& f) override; private: - iAllocator* alloc_; + Param param_; + OffsetClock clock_; Scoreboard sb_; diff --git a/src/Texture.cc b/src/Texture.cc index 7012004..9b3588f 100644 --- a/src/Texture.cc +++ b/src/Texture.cc @@ -81,7 +81,7 @@ void gj::Texture::Draw(Colorbuffer& fb) const { if (dstx < 0 || w <= dstx) continue; if (dsty < 0 || h <= dsty) continue; - dst[dstx + w*dsty] = src[srcx + srcw*srcy] * alpha_; + dst[dstx + w*dsty] += src[srcx + srcw*srcy] * alpha_; } } } \ No newline at end of file diff --git a/src/TitleScene.cc b/src/TitleScene.cc new file mode 100644 index 0000000..9d1b91c --- /dev/null +++ b/src/TitleScene.cc @@ -0,0 +1,129 @@ +#include "TitleScene.h" + +#include +#include +#include +#include +#include + +#include "thirdparty/picojson.h" + +#include "common.h" +#include "Font.h" +#include "PlayScene.h" + + +gj::TitleScene::TitleScene(const Param& p) : + param_(p), + score_(L"penguin: you didn't see anything..."), + next_(L"> L"), prev_(L"H <"), + guide_(L"H:PREV / SPACE:PLAY / L:NEXT"), + logo_(Colorbuffer(p.alloc, 1, 1)) { + /* load score list */ + std::ifstream ifs(kListPath); + if (!ifs) Abort(std::string(kListPath)+" is missing"); + + ::picojson::value root; + const std::string err = ::picojson::parse(root, ifs); + ifs.close(); + + if (err.size()) Abort(std::string(kListPath)+": "+err); + + std::string line; + auto& list = root.get<::picojson::array>(); + for (auto& e : list) { + auto& obj = e.get<::picojson::object>(); + + Score s; + + s.displayName = obj["displayName"].get(); + s.music = obj["music"].get(); + s.score = obj["score"].get(); + s.playOffset = static_cast(obj["playOffset"].get()); + + list_.push_back(s); + } + if (list_.size() == 0) Abort("no score is registered"); + + SelectScore_(0); + + /* render logo */ + Font font(param_.alloc, "res/font/shippori.ttf"); + logo_ = Texture(font.RenderGlyphs(L"GlyphsJuke", 64)); +} + +gj::UniqPtr gj::TitleScene::Update(Frame& frame) { + for (const auto c : frame.input) { + switch (c) { + case 'h': + SelectScore_(select_index_? select_index_-1: list_.size()-1); + break; + case 'l': + SelectScore_((select_index_+1)%list_.size()); + break; + case ' ': + return param_.alloc->MakeUniq( + param_, list_[select_index_].displayName, list_[select_index_].score); + } + } + + const uint64_t now = param_.clock->now(); + + const int32_t w = static_cast(frame.w); + const int32_t h = static_cast(frame.h); + + const int32_t selector_y = static_cast(h*.75); + + const uint64_t period1 = XorShift(now/1000+1)%1000 + 500; + const uint64_t period2 = XorShift(now/1000+5)%1000 + 500; + logo_.SetAlpha(static_cast(XorShift(now/period1+1)%100/100.*.4+.4)); + + const double shift_x = (XorShift(now/period1+1)%100/100.*2 - 1)*.05; + const double shift_y = (XorShift(now/period2+3)%100/100.*2 - 1)*.05; + const double scale_x = (XorShift(now/period1+3)%100/100.*2 - 1)*.1; + + const double theta = (XorShift(now/period2+13)%100/100.*2 - 1)*.01*kPi*2; + + const double c = cos(theta), s = sin(theta); + auto M = mat3{ + {1+scale_x, 0, 0}, + {0, .8, 0}, + {shift_x, shift_y+.4, 1} + }; + auto Mr = mat3{ + {c, -s, 0}, + {s, c, 0}, + {0, 0, 1}, + }; + M = ::linalg::mul(Mr, M); + logo_.SetMatrix(M); + frame.Add(&logo_); + + next_.SetPosition(static_cast(w*.8-next_.width()), selector_y); + frame.Add(&next_); + + prev_.SetPosition(static_cast(w*.2), selector_y); + frame.Add(&prev_); + + score_.SetPosition((w-score_.width())/2, selector_y); + frame.Add(&score_); + + guide_.SetPosition((w-guide_.width())/2, selector_y+3); + frame.Add(&guide_); + + pe_.seed = XorShift(now/period1+10); + pe_.maxShift = (XorShift(now/period1+7)%100/100.*2 - 1)*.1; + frame.Add(&pe_); + + return nullptr; +} + +void gj::TitleScene::SelectScore_(size_t index) { + const auto& s = list_[index]; + score_ = Text(ConvertStrToWstr(s.displayName)); + select_index_ = index; + + param_.audio->SetVolume(.2); + param_.audio->SetLpfIntensity(.99); + param_.audio->PlayMusic(s.music, s.playOffset); +} \ No newline at end of file diff --git a/src/TitleScene.h b/src/TitleScene.h new file mode 100644 index 0000000..1bb95b7 --- /dev/null +++ b/src/TitleScene.h @@ -0,0 +1,53 @@ +#pragma once + +#include "common.h" +#include "GlitchPosteffect.h" +#include "iScene.h" +#include "Text.h" +#include "Texture.h" + +namespace gj { + + +class TitleScene : public iScene { + public: + static constexpr auto kListPath = "res/list.json"; + + TitleScene() = delete; + TitleScene(TitleScene&&) = delete; + TitleScene(const TitleScene&) = delete; + + TitleScene& operator=(TitleScene&&) = delete; + TitleScene& operator=(const TitleScene&) = delete; + + TitleScene(const Param& p); + + UniqPtr Update(Frame& frame) override; + + private: + struct Score { + std::string displayName; + std::string score; + std::string music; + double playOffset; + }; + + Param param_; + + Text score_; + Text next_; + Text prev_; + Text guide_; + + Texture logo_; + + size_t select_index_; + std::vector list_; + + GlitchPosteffect pe_; + + void SelectScore_(size_t index); +}; + + +} \ No newline at end of file diff --git a/src/common.h b/src/common.h index c4edbbf..11de546 100644 --- a/src/common.h +++ b/src/common.h @@ -17,6 +17,8 @@ namespace gj { using mat3 = ::linalg::mat; using vec3 = ::linalg::vec; +constexpr double kPi = 3.14159265358979323846264338327950288; + static inline std::wstring ConvertStrToWstr(const std::string& str) { std::wstring ret; @@ -38,6 +40,12 @@ static inline size_t CountWstrBytes(const std::wstring& str) { return n; } +static inline uint64_t XorShift(uint64_t x) { + x = x ^ (x << 13); + x = x ^ (x >> 7); + return x ^ (x << 17); +} + [[noreturn]] static inline void Abort(const std::string& msg) { diff --git a/src/iAudioDevice.h b/src/iAudioDevice.h index b418850..a38a675 100644 --- a/src/iAudioDevice.h +++ b/src/iAudioDevice.h @@ -18,8 +18,11 @@ public: virtual ~iAudioDevice() = default; - virtual void PlayMusic(const std::string& path) = 0; + virtual void PlayMusic(const std::string& path, double offset) = 0; virtual void StopMusic() = 0; + + virtual void SetVolume(double amp) = 0; + virtual void SetLpfIntensity(double v) = 0; }; diff --git a/src/iScene.h b/src/iScene.h index f9feb02..c9c2fa3 100644 --- a/src/iScene.h +++ b/src/iScene.h @@ -4,6 +4,8 @@ #include "Frame.h" #include "iAllocator.h" +#include "iAudioDevice.h" +#include "iClock.h" #include "iDrawable.h" #include "iWritable.h" @@ -13,6 +15,12 @@ namespace gj { class iScene { public: + struct Param { + iAllocator* alloc; + iAudioDevice* audio; + const iClock* clock; + }; + iScene() = default; iScene(iScene&&) = default; iScene(const iScene&) = default; diff --git a/thirdparty/picojson.h b/thirdparty/picojson.h new file mode 100644 index 0000000..76742fe --- /dev/null +++ b/thirdparty/picojson.h @@ -0,0 +1,1200 @@ +/* + * Copyright 2009-2010 Cybozu Labs, Inc. + * Copyright 2011-2014 Kazuho Oku + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef picojson_h +#define picojson_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// for isnan/isinf +#if __cplusplus >= 201103L +#include +#else +extern "C" { +#ifdef _MSC_VER +#include +#elif defined(__INTEL_COMPILER) +#include +#else +#include +#endif +} +#endif + +#ifndef PICOJSON_USE_RVALUE_REFERENCE +#if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600) +#define PICOJSON_USE_RVALUE_REFERENCE 1 +#else +#define PICOJSON_USE_RVALUE_REFERENCE 0 +#endif +#endif // PICOJSON_USE_RVALUE_REFERENCE + +#ifndef PICOJSON_NOEXCEPT +#if PICOJSON_USE_RVALUE_REFERENCE +#define PICOJSON_NOEXCEPT noexcept +#else +#define PICOJSON_NOEXCEPT throw() +#endif +#endif + +// experimental support for int64_t (see README.mkdn for detail) +#ifdef PICOJSON_USE_INT64 +#define __STDC_FORMAT_MACROS +#include +#if __cplusplus >= 201103L +#include +#else +extern "C" { +#include +} +#endif +#endif + +// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 +#ifndef PICOJSON_USE_LOCALE +#define PICOJSON_USE_LOCALE 1 +#endif +#if PICOJSON_USE_LOCALE +extern "C" { +#include +} +#endif + +#ifndef PICOJSON_ASSERT +#define PICOJSON_ASSERT(e) \ + do { \ + if (!(e)) \ + throw std::runtime_error(#e); \ + } while (0) +#endif + +#ifdef _MSC_VER +#define SNPRINTF _snprintf_s +#pragma warning(push) +#pragma warning(disable : 4244) // conversion from int to char +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4702) // unreachable code +#pragma warning(disable : 4706) // assignment within conditional expression +#else +#define SNPRINTF snprintf +#endif + +namespace picojson { + +enum { + null_type, + boolean_type, + number_type, + string_type, + array_type, + object_type +#ifdef PICOJSON_USE_INT64 + , + int64_type +#endif +}; + +enum { INDENT_WIDTH = 2, DEFAULT_MAX_DEPTHS = 100 }; + +struct null {}; + +class value { +public: + typedef std::vector array; + typedef std::map object; + union _storage { + bool boolean_; + double number_; +#ifdef PICOJSON_USE_INT64 + int64_t int64_; +#endif + std::string *string_; + array *array_; + object *object_; + }; + +protected: + int type_; + _storage u_; + +public: + value(); + value(int type, bool); + explicit value(bool b); +#ifdef PICOJSON_USE_INT64 + explicit value(int64_t i); +#endif + explicit value(double n); + explicit value(const std::string &s); + explicit value(const array &a); + explicit value(const object &o); +#if PICOJSON_USE_RVALUE_REFERENCE + explicit value(std::string &&s); + explicit value(array &&a); + explicit value(object &&o); +#endif + explicit value(const char *s); + value(const char *s, size_t len); + ~value(); + value(const value &x); + value &operator=(const value &x); +#if PICOJSON_USE_RVALUE_REFERENCE + value(value &&x) PICOJSON_NOEXCEPT; + value &operator=(value &&x) PICOJSON_NOEXCEPT; +#endif + void swap(value &x) PICOJSON_NOEXCEPT; + template bool is() const; + template const T &get() const; + template T &get(); + template void set(const T &); +#if PICOJSON_USE_RVALUE_REFERENCE + template void set(T &&); +#endif + bool evaluate_as_boolean() const; + const value &get(const size_t idx) const; + const value &get(const std::string &key) const; + value &get(const size_t idx); + value &get(const std::string &key); + + bool contains(const size_t idx) const; + bool contains(const std::string &key) const; + std::string to_str() const; + template void serialize(Iter os, bool prettify = false) const; + std::string serialize(bool prettify = false) const; + +private: + template value(const T *); // intentionally defined to block implicit conversion of pointer to bool + template static void _indent(Iter os, int indent); + template void _serialize(Iter os, int indent) const; + std::string _serialize(int indent) const; + void clear(); +}; + +typedef value::array array; +typedef value::object object; + +inline value::value() : type_(null_type), u_() { +} + +inline value::value(int type, bool) : type_(type), u_() { + switch (type) { +#define INIT(p, v) \ + case p##type: \ + u_.p = v; \ + break + INIT(boolean_, false); + INIT(number_, 0.0); +#ifdef PICOJSON_USE_INT64 + INIT(int64_, 0); +#endif + INIT(string_, new std::string()); + INIT(array_, new array()); + INIT(object_, new object()); +#undef INIT + default: + break; + } +} + +inline value::value(bool b) : type_(boolean_type), u_() { + u_.boolean_ = b; +} + +#ifdef PICOJSON_USE_INT64 +inline value::value(int64_t i) : type_(int64_type), u_() { + u_.int64_ = i; +} +#endif + +inline value::value(double n) : type_(number_type), u_() { + if ( +#ifdef _MSC_VER + !_finite(n) +#elif __cplusplus >= 201103L + std::isnan(n) || std::isinf(n) +#else + isnan(n) || isinf(n) +#endif + ) { + throw std::overflow_error(""); + } + u_.number_ = n; +} + +inline value::value(const std::string &s) : type_(string_type), u_() { + u_.string_ = new std::string(s); +} + +inline value::value(const array &a) : type_(array_type), u_() { + u_.array_ = new array(a); +} + +inline value::value(const object &o) : type_(object_type), u_() { + u_.object_ = new object(o); +} + +#if PICOJSON_USE_RVALUE_REFERENCE +inline value::value(std::string &&s) : type_(string_type), u_() { + u_.string_ = new std::string(std::move(s)); +} + +inline value::value(array &&a) : type_(array_type), u_() { + u_.array_ = new array(std::move(a)); +} + +inline value::value(object &&o) : type_(object_type), u_() { + u_.object_ = new object(std::move(o)); +} +#endif + +inline value::value(const char *s) : type_(string_type), u_() { + u_.string_ = new std::string(s); +} + +inline value::value(const char *s, size_t len) : type_(string_type), u_() { + u_.string_ = new std::string(s, len); +} + +inline void value::clear() { + switch (type_) { +#define DEINIT(p) \ + case p##type: \ + delete u_.p; \ + break + DEINIT(string_); + DEINIT(array_); + DEINIT(object_); +#undef DEINIT + default: + break; + } +} + +inline value::~value() { + clear(); +} + +inline value::value(const value &x) : type_(x.type_), u_() { + switch (type_) { +#define INIT(p, v) \ + case p##type: \ + u_.p = v; \ + break + INIT(string_, new std::string(*x.u_.string_)); + INIT(array_, new array(*x.u_.array_)); + INIT(object_, new object(*x.u_.object_)); +#undef INIT + default: + u_ = x.u_; + break; + } +} + +inline value &value::operator=(const value &x) { + if (this != &x) { + value t(x); + swap(t); + } + return *this; +} + +#if PICOJSON_USE_RVALUE_REFERENCE +inline value::value(value &&x) PICOJSON_NOEXCEPT : type_(null_type), u_() { + swap(x); +} +inline value &value::operator=(value &&x) PICOJSON_NOEXCEPT { + swap(x); + return *this; +} +#endif +inline void value::swap(value &x) PICOJSON_NOEXCEPT { + std::swap(type_, x.type_); + std::swap(u_, x.u_); +} + +#define IS(ctype, jtype) \ + template <> inline bool value::is() const { \ + return type_ == jtype##_type; \ + } +IS(null, null) +IS(bool, boolean) +#ifdef PICOJSON_USE_INT64 +IS(int64_t, int64) +#endif +IS(std::string, string) +IS(array, array) +IS(object, object) +#undef IS +template <> inline bool value::is() const { + return type_ == number_type +#ifdef PICOJSON_USE_INT64 + || type_ == int64_type +#endif + ; +} + +#define GET(ctype, var) \ + template <> inline const ctype &value::get() const { \ + PICOJSON_ASSERT("type mismatch! call is() before get()" && is()); \ + return var; \ + } \ + template <> inline ctype &value::get() { \ + PICOJSON_ASSERT("type mismatch! call is() before get()" && is()); \ + return var; \ + } +GET(bool, u_.boolean_) +GET(std::string, *u_.string_) +GET(array, *u_.array_) +GET(object, *u_.object_) +#ifdef PICOJSON_USE_INT64 +GET(double, + (type_ == int64_type && (const_cast(this)->type_ = number_type, (const_cast(this)->u_.number_ = u_.int64_)), + u_.number_)) +GET(int64_t, u_.int64_) +#else +GET(double, u_.number_) +#endif +#undef GET + +#define SET(ctype, jtype, setter) \ + template <> inline void value::set(const ctype &_val) { \ + clear(); \ + type_ = jtype##_type; \ + setter \ + } +SET(bool, boolean, u_.boolean_ = _val;) +SET(std::string, string, u_.string_ = new std::string(_val);) +SET(array, array, u_.array_ = new array(_val);) +SET(object, object, u_.object_ = new object(_val);) +SET(double, number, u_.number_ = _val;) +#ifdef PICOJSON_USE_INT64 +SET(int64_t, int64, u_.int64_ = _val;) +#endif +#undef SET + +#if PICOJSON_USE_RVALUE_REFERENCE +#define MOVESET(ctype, jtype, setter) \ + template <> inline void value::set(ctype && _val) { \ + clear(); \ + type_ = jtype##_type; \ + setter \ + } +MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));) +MOVESET(array, array, u_.array_ = new array(std::move(_val));) +MOVESET(object, object, u_.object_ = new object(std::move(_val));) +#undef MOVESET +#endif + +inline bool value::evaluate_as_boolean() const { + switch (type_) { + case null_type: + return false; + case boolean_type: + return u_.boolean_; + case number_type: + return u_.number_ != 0; +#ifdef PICOJSON_USE_INT64 + case int64_type: + return u_.int64_ != 0; +#endif + case string_type: + return !u_.string_->empty(); + default: + return true; + } +} + +inline const value &value::get(const size_t idx) const { + static value s_null; + PICOJSON_ASSERT(is()); + return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; +} + +inline value &value::get(const size_t idx) { + static value s_null; + PICOJSON_ASSERT(is()); + return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; +} + +inline const value &value::get(const std::string &key) const { + static value s_null; + PICOJSON_ASSERT(is()); + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end() ? i->second : s_null; +} + +inline value &value::get(const std::string &key) { + static value s_null; + PICOJSON_ASSERT(is()); + object::iterator i = u_.object_->find(key); + return i != u_.object_->end() ? i->second : s_null; +} + +inline bool value::contains(const size_t idx) const { + PICOJSON_ASSERT(is()); + return idx < u_.array_->size(); +} + +inline bool value::contains(const std::string &key) const { + PICOJSON_ASSERT(is()); + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end(); +} + +inline std::string value::to_str() const { + switch (type_) { + case null_type: + return "null"; + case boolean_type: + return u_.boolean_ ? "true" : "false"; +#ifdef PICOJSON_USE_INT64 + case int64_type: { + char buf[sizeof("-9223372036854775808")]; + SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); + return buf; + } +#endif + case number_type: { + char buf[256]; + double tmp; + SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); +#if PICOJSON_USE_LOCALE + char *decimal_point = localeconv()->decimal_point; + if (strcmp(decimal_point, ".") != 0) { + size_t decimal_point_len = strlen(decimal_point); + for (char *p = buf; *p != '\0'; ++p) { + if (strncmp(p, decimal_point, decimal_point_len) == 0) { + return std::string(buf, p) + "." + (p + decimal_point_len); + } + } + } +#endif + return buf; + } + case string_type: + return *u_.string_; + case array_type: + return "array"; + case object_type: + return "object"; + default: + PICOJSON_ASSERT(0); +#ifdef _MSC_VER + __assume(0); +#endif + } + return std::string(); +} + +template void copy(const std::string &s, Iter oi) { + std::copy(s.begin(), s.end(), oi); +} + +template struct serialize_str_char { + Iter oi; + void operator()(char c) { + switch (c) { +#define MAP(val, sym) \ + case val: \ + copy(sym, oi); \ + break + MAP('"', "\\\""); + MAP('\\', "\\\\"); + MAP('/', "\\/"); + MAP('\b', "\\b"); + MAP('\f', "\\f"); + MAP('\n', "\\n"); + MAP('\r', "\\r"); + MAP('\t', "\\t"); +#undef MAP + default: + if (static_cast(c) < 0x20 || c == 0x7f) { + char buf[7]; + SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff); + copy(buf, buf + 6, oi); + } else { + *oi++ = c; + } + break; + } + } +}; + +template void serialize_str(const std::string &s, Iter oi) { + *oi++ = '"'; + serialize_str_char process_char = {oi}; + std::for_each(s.begin(), s.end(), process_char); + *oi++ = '"'; +} + +template void value::serialize(Iter oi, bool prettify) const { + return _serialize(oi, prettify ? 0 : -1); +} + +inline std::string value::serialize(bool prettify) const { + return _serialize(prettify ? 0 : -1); +} + +template void value::_indent(Iter oi, int indent) { + *oi++ = '\n'; + for (int i = 0; i < indent * INDENT_WIDTH; ++i) { + *oi++ = ' '; + } +} + +template void value::_serialize(Iter oi, int indent) const { + switch (type_) { + case string_type: + serialize_str(*u_.string_, oi); + break; + case array_type: { + *oi++ = '['; + if (indent != -1) { + ++indent; + } + for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) { + if (i != u_.array_->begin()) { + *oi++ = ','; + } + if (indent != -1) { + _indent(oi, indent); + } + i->_serialize(oi, indent); + } + if (indent != -1) { + --indent; + if (!u_.array_->empty()) { + _indent(oi, indent); + } + } + *oi++ = ']'; + break; + } + case object_type: { + *oi++ = '{'; + if (indent != -1) { + ++indent; + } + for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) { + if (i != u_.object_->begin()) { + *oi++ = ','; + } + if (indent != -1) { + _indent(oi, indent); + } + serialize_str(i->first, oi); + *oi++ = ':'; + if (indent != -1) { + *oi++ = ' '; + } + i->second._serialize(oi, indent); + } + if (indent != -1) { + --indent; + if (!u_.object_->empty()) { + _indent(oi, indent); + } + } + *oi++ = '}'; + break; + } + default: + copy(to_str(), oi); + break; + } + if (indent == 0) { + *oi++ = '\n'; + } +} + +inline std::string value::_serialize(int indent) const { + std::string s; + _serialize(std::back_inserter(s), indent); + return s; +} + +template class input { +protected: + Iter cur_, end_; + bool consumed_; + int line_; + +public: + input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1) { + } + int getc() { + if (consumed_) { + if (*cur_ == '\n') { + ++line_; + } + ++cur_; + } + if (cur_ == end_) { + consumed_ = false; + return -1; + } + consumed_ = true; + return *cur_ & 0xff; + } + void ungetc() { + consumed_ = false; + } + Iter cur() const { + if (consumed_) { + input *self = const_cast *>(this); + self->consumed_ = false; + ++self->cur_; + } + return cur_; + } + int line() const { + return line_; + } + void skip_ws() { + while (1) { + int ch = getc(); + if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { + ungetc(); + break; + } + } + } + bool expect(const int expected) { + skip_ws(); + if (getc() != expected) { + ungetc(); + return false; + } + return true; + } + bool match(const std::string &pattern) { + for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { + if (getc() != *pi) { + ungetc(); + return false; + } + } + return true; + } +}; + +template inline int _parse_quadhex(input &in) { + int uni_ch = 0, hex; + for (int i = 0; i < 4; i++) { + if ((hex = in.getc()) == -1) { + return -1; + } + if ('0' <= hex && hex <= '9') { + hex -= '0'; + } else if ('A' <= hex && hex <= 'F') { + hex -= 'A' - 0xa; + } else if ('a' <= hex && hex <= 'f') { + hex -= 'a' - 0xa; + } else { + in.ungetc(); + return -1; + } + uni_ch = uni_ch * 16 + hex; + } + return uni_ch; +} + +template inline bool _parse_codepoint(String &out, input &in) { + int uni_ch; + if ((uni_ch = _parse_quadhex(in)) == -1) { + return false; + } + if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { + if (0xdc00 <= uni_ch) { + // a second 16-bit of a surrogate pair appeared + return false; + } + // first 16-bit of surrogate pair, get the next one + if (in.getc() != '\\' || in.getc() != 'u') { + in.ungetc(); + return false; + } + int second = _parse_quadhex(in); + if (!(0xdc00 <= second && second <= 0xdfff)) { + return false; + } + uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); + uni_ch += 0x10000; + } + if (uni_ch < 0x80) { + out.push_back(static_cast(uni_ch)); + } else { + if (uni_ch < 0x800) { + out.push_back(static_cast(0xc0 | (uni_ch >> 6))); + } else { + if (uni_ch < 0x10000) { + out.push_back(static_cast(0xe0 | (uni_ch >> 12))); + } else { + out.push_back(static_cast(0xf0 | (uni_ch >> 18))); + out.push_back(static_cast(0x80 | ((uni_ch >> 12) & 0x3f))); + } + out.push_back(static_cast(0x80 | ((uni_ch >> 6) & 0x3f))); + } + out.push_back(static_cast(0x80 | (uni_ch & 0x3f))); + } + return true; +} + +template inline bool _parse_string(String &out, input &in) { + while (1) { + int ch = in.getc(); + if (ch < ' ') { + in.ungetc(); + return false; + } else if (ch == '"') { + return true; + } else if (ch == '\\') { + if ((ch = in.getc()) == -1) { + return false; + } + switch (ch) { +#define MAP(sym, val) \ + case sym: \ + out.push_back(val); \ + break + MAP('"', '\"'); + MAP('\\', '\\'); + MAP('/', '/'); + MAP('b', '\b'); + MAP('f', '\f'); + MAP('n', '\n'); + MAP('r', '\r'); + MAP('t', '\t'); +#undef MAP + case 'u': + if (!_parse_codepoint(out, in)) { + return false; + } + break; + default: + return false; + } + } else { + out.push_back(static_cast(ch)); + } + } + return false; +} + +template inline bool _parse_array(Context &ctx, input &in) { + if (!ctx.parse_array_start()) { + return false; + } + size_t idx = 0; + if (in.expect(']')) { + return ctx.parse_array_stop(idx); + } + do { + if (!ctx.parse_array_item(in, idx)) { + return false; + } + idx++; + } while (in.expect(',')); + return in.expect(']') && ctx.parse_array_stop(idx); +} + +template inline bool _parse_object(Context &ctx, input &in) { + if (!ctx.parse_object_start()) { + return false; + } + if (in.expect('}')) { + return ctx.parse_object_stop(); + } + do { + std::string key; + if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) { + return false; + } + if (!ctx.parse_object_item(in, key)) { + return false; + } + } while (in.expect(',')); + return in.expect('}') && ctx.parse_object_stop(); +} + +template inline std::string _parse_number(input &in) { + std::string num_str; + while (1) { + int ch = in.getc(); + if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') { + num_str.push_back(static_cast(ch)); + } else if (ch == '.') { +#if PICOJSON_USE_LOCALE + num_str += localeconv()->decimal_point; +#else + num_str.push_back('.'); +#endif + } else { + in.ungetc(); + break; + } + } + return num_str; +} + +template inline bool _parse(Context &ctx, input &in) { + in.skip_ws(); + int ch = in.getc(); + switch (ch) { +#define IS(ch, text, op) \ + case ch: \ + if (in.match(text) && op) { \ + return true; \ + } else { \ + return false; \ + } + IS('n', "ull", ctx.set_null()); + IS('f', "alse", ctx.set_bool(false)); + IS('t', "rue", ctx.set_bool(true)); +#undef IS + case '"': + return ctx.parse_string(in); + case '[': + return _parse_array(ctx, in); + case '{': + return _parse_object(ctx, in); + default: + if (('0' <= ch && ch <= '9') || ch == '-') { + double f; + char *endp; + in.ungetc(); + std::string num_str(_parse_number(in)); + if (num_str.empty()) { + return false; + } +#ifdef PICOJSON_USE_INT64 + { + errno = 0; + intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); + if (errno == 0 && std::numeric_limits::min() <= ival && ival <= std::numeric_limits::max() && + endp == num_str.c_str() + num_str.size()) { + ctx.set_int64(ival); + return true; + } + } +#endif + f = strtod(num_str.c_str(), &endp); + if (endp == num_str.c_str() + num_str.size()) { + ctx.set_number(f); + return true; + } + return false; + } + break; + } + in.ungetc(); + return false; +} + +class deny_parse_context { +public: + bool set_null() { + return false; + } + bool set_bool(bool) { + return false; + } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t) { + return false; + } +#endif + bool set_number(double) { + return false; + } + template bool parse_string(input &) { + return false; + } + bool parse_array_start() { + return false; + } + template bool parse_array_item(input &, size_t) { + return false; + } + bool parse_array_stop(size_t) { + return false; + } + bool parse_object_start() { + return false; + } + template bool parse_object_item(input &, const std::string &) { + return false; + } +}; + +class default_parse_context { +protected: + value *out_; + size_t depths_; + +public: + default_parse_context(value *out, size_t depths = DEFAULT_MAX_DEPTHS) : out_(out), depths_(depths) { + } + bool set_null() { + *out_ = value(); + return true; + } + bool set_bool(bool b) { + *out_ = value(b); + return true; + } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t i) { + *out_ = value(i); + return true; + } +#endif + bool set_number(double f) { + *out_ = value(f); + return true; + } + template bool parse_string(input &in) { + *out_ = value(string_type, false); + return _parse_string(out_->get(), in); + } + bool parse_array_start() { + if (depths_ == 0) + return false; + --depths_; + *out_ = value(array_type, false); + return true; + } + template bool parse_array_item(input &in, size_t) { + array &a = out_->get(); + a.push_back(value()); + default_parse_context ctx(&a.back(), depths_); + return _parse(ctx, in); + } + bool parse_array_stop(size_t) { + ++depths_; + return true; + } + bool parse_object_start() { + if (depths_ == 0) + return false; + *out_ = value(object_type, false); + return true; + } + template bool parse_object_item(input &in, const std::string &key) { + object &o = out_->get(); + default_parse_context ctx(&o[key], depths_); + return _parse(ctx, in); + } + bool parse_object_stop() { + ++depths_; + return true; + } + +private: + default_parse_context(const default_parse_context &); + default_parse_context &operator=(const default_parse_context &); +}; + +class null_parse_context { +protected: + size_t depths_; + +public: + struct dummy_str { + void push_back(int) { + } + }; + +public: + null_parse_context(size_t depths = DEFAULT_MAX_DEPTHS) : depths_(depths) { + } + bool set_null() { + return true; + } + bool set_bool(bool) { + return true; + } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t) { + return true; + } +#endif + bool set_number(double) { + return true; + } + template bool parse_string(input &in) { + dummy_str s; + return _parse_string(s, in); + } + bool parse_array_start() { + if (depths_ == 0) + return false; + --depths_; + return true; + } + template bool parse_array_item(input &in, size_t) { + return _parse(*this, in); + } + bool parse_array_stop(size_t) { + ++depths_; + return true; + } + bool parse_object_start() { + if (depths_ == 0) + return false; + --depths_; + return true; + } + template bool parse_object_item(input &in, const std::string &) { + ++depths_; + return _parse(*this, in); + } + bool parse_object_stop() { + return true; + } + +private: + null_parse_context(const null_parse_context &); + null_parse_context &operator=(const null_parse_context &); +}; + +// obsolete, use the version below +template inline std::string parse(value &out, Iter &pos, const Iter &last) { + std::string err; + pos = parse(out, pos, last, &err); + return err; +} + +template inline Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err) { + input in(first, last); + if (!_parse(ctx, in) && err != NULL) { + char buf[64]; + SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); + *err = buf; + while (1) { + int ch = in.getc(); + if (ch == -1 || ch == '\n') { + break; + } else if (ch >= ' ') { + err->push_back(static_cast(ch)); + } + } + } + return in.cur(); +} + +template inline Iter parse(value &out, const Iter &first, const Iter &last, std::string *err) { + default_parse_context ctx(&out); + return _parse(ctx, first, last, err); +} + +inline std::string parse(value &out, const std::string &s) { + std::string err; + parse(out, s.begin(), s.end(), &err); + return err; +} + +inline std::string parse(value &out, std::istream &is) { + std::string err; + parse(out, std::istreambuf_iterator(is.rdbuf()), std::istreambuf_iterator(), &err); + return err; +} + +template struct last_error_t { static std::string s; }; +template std::string last_error_t::s; + +inline void set_last_error(const std::string &s) { + last_error_t::s = s; +} + +inline const std::string &get_last_error() { + return last_error_t::s; +} + +inline bool operator==(const value &x, const value &y) { + if (x.is()) + return y.is(); +#define PICOJSON_CMP(type) \ + if (x.is()) \ + return y.is() && x.get() == y.get() + PICOJSON_CMP(bool); + PICOJSON_CMP(double); + PICOJSON_CMP(std::string); + PICOJSON_CMP(array); + PICOJSON_CMP(object); +#undef PICOJSON_CMP + PICOJSON_ASSERT(0); +#ifdef _MSC_VER + __assume(0); +#endif + return false; +} + +inline bool operator!=(const value &x, const value &y) { + return !(x == y); +} +} + +#if !PICOJSON_USE_RVALUE_REFERENCE +namespace std { +template <> inline void swap(picojson::value &x, picojson::value &y) { + x.swap(y); +} +} +#endif + +inline std::istream &operator>>(std::istream &is, picojson::value &x) { + picojson::set_last_error(std::string()); + const std::string err(picojson::parse(x, is)); + if (!err.empty()) { + picojson::set_last_error(err); + is.setstate(std::ios::failbit); + } + return is; +} + +inline std::ostream &operator<<(std::ostream &os, const picojson::value &x) { + x.serialize(std::ostream_iterator(os)); + return os; +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif