From a9dc9a0208bdcc4656de929a8114049761bc1462 Mon Sep 17 00:00:00 2001 From: RERASER <2641320887@qq.com> Date: Sat, 2 Aug 2025 22:37:33 +0800 Subject: [PATCH 1/2] fix priority and add wasapi hook --- .../component/iidx/custom_resolution.cpp | 16 +++ .../iidx/wasapi_proxy/audio_client.cpp | 129 ++++++++++++++++++ .../iidx/wasapi_proxy/audio_client.hpp | 27 ++++ .../component/iidx/wasapi_proxy/mmdevice.cpp | 59 ++++++++ .../component/iidx/wasapi_proxy/mmdevice.hpp | 18 +++ .../iidx/wasapi_proxy/mmdevice_collection.cpp | 48 +++++++ .../iidx/wasapi_proxy/mmdevice_collection.hpp | 16 +++ .../iidx/wasapi_proxy/mmdevice_enumerator.cpp | 75 ++++++++++ .../iidx/wasapi_proxy/mmdevice_enumerator.hpp | 19 +++ src/client/main.cpp | 2 +- src/client/std_include.hpp | 2 + 11 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 src/client/component/iidx/wasapi_proxy/audio_client.cpp create mode 100644 src/client/component/iidx/wasapi_proxy/audio_client.hpp create mode 100644 src/client/component/iidx/wasapi_proxy/mmdevice.cpp create mode 100644 src/client/component/iidx/wasapi_proxy/mmdevice.hpp create mode 100644 src/client/component/iidx/wasapi_proxy/mmdevice_collection.cpp create mode 100644 src/client/component/iidx/wasapi_proxy/mmdevice_collection.hpp create mode 100644 src/client/component/iidx/wasapi_proxy/mmdevice_enumerator.cpp create mode 100644 src/client/component/iidx/wasapi_proxy/mmdevice_enumerator.hpp diff --git a/src/client/component/iidx/custom_resolution.cpp b/src/client/component/iidx/custom_resolution.cpp index 55a1402..5160473 100644 --- a/src/client/component/iidx/custom_resolution.cpp +++ b/src/client/component/iidx/custom_resolution.cpp @@ -150,6 +150,18 @@ namespace iidx::custom_resolution return SetWindowPos(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); } + + HRESULT WINAPI co_create_instance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv) + { + if (IsEqualCLSID(rclsid, __uuidof(MMDeviceEnumerator)) && IsEqualIID(riid, __uuidof(IMMDeviceEnumerator))) + { + IMMDeviceEnumerator* enumerator = nullptr; + } + else + { + return CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv); + } + } } class component final : public component_interface @@ -174,6 +186,10 @@ namespace iidx::custom_resolution { return set_window_pos; } + else if(function == "CoCreateInstance") + { + return co_create_instance; + } return nullptr; } diff --git a/src/client/component/iidx/wasapi_proxy/audio_client.cpp b/src/client/component/iidx/wasapi_proxy/audio_client.cpp new file mode 100644 index 0000000..b1ad8ce --- /dev/null +++ b/src/client/component/iidx/wasapi_proxy/audio_client.cpp @@ -0,0 +1,129 @@ +#include +#include +#include "audio_client.hpp" + +audio_client_proxy::audio_client_proxy(IAudioClient* orig) : client_(orig) +{ +} + +HRESULT __stdcall audio_client_proxy::QueryInterface(REFIID riid, void** ppvObj) +{ + *ppvObj = nullptr; + + auto hr = client_->QueryInterface(riid, ppvObj); + if (hr == NOERROR) *ppvObj = this; + + return hr; +} + +ULONG __stdcall audio_client_proxy::AddRef(void) +{ + return client_->AddRef(); +} + +ULONG __stdcall audio_client_proxy::Release(void) +{ + auto count = client_->Release(); + + if (!count) + { + delete this; + } + + return count; +} + +HRESULT __stdcall audio_client_proxy::Initialize__(AUDCLNT_SHAREMODE ShareMode, DWORD StreamFlags, REFERENCE_TIME hnsBufferDuration, REFERENCE_TIME hnsPeriodicity, const WAVEFORMATEX* pFormat, LPCGUID AudioSessionGuid) +{ + static const bool can_set_lowlatency_mode = [] { + utils::nt::library ntdll{ "ntdll.dll" }; + + OSVERSIONINFOEXW info; + ntdll.invoke_pascal("RtlGetVersion", &info); + + return info.dwMajorVersion >= 10; + }(); + if (ShareMode != AUDCLNT_SHAREMODE_SHARED || !can_set_lowlatency_mode) return -1; + auto INVALID_STREAM_FLAGS = AUDCLNT_STREAMFLAGS_CROSSPROCESS | AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_RATEADJUST | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; + StreamFlags &= ~INVALID_STREAM_FLAGS; + IAudioClient3* client3 = nullptr; + UINT32 defaultPeriodInFrames; + UINT32 fundamentalPeriodInFrames; + UINT32 minPeriodInFrames; + UINT32 maxPeriodInFrames; + auto hr = client_->QueryInterface(__uuidof(IAudioClient3), reinterpret_cast(&client3)); + if (FAILED(hr)) return hr; + hr = client3->GetSharedModeEnginePeriod(pFormat, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); + if (FAILED(hr)) return hr; + hr = client3->InitializeSharedAudioStream(StreamFlags, minPeriodInFrames, pFormat, AudioSessionGuid); + client3->Release(); + return hr; +} + +HRESULT __stdcall audio_client_proxy::Initialize(AUDCLNT_SHAREMODE ShareMode, DWORD StreamFlags, REFERENCE_TIME hnsBufferDuration, REFERENCE_TIME hnsPeriodicity, const WAVEFORMATEX* pFormat, LPCGUID AudioSessionGuid) +{ + auto hr = audio_client_proxy::Initialize__(ShareMode, StreamFlags, hnsBufferDuration, hnsPeriodicity, pFormat, AudioSessionGuid); + if (SUCCEEDED(hr)) + { + return hr; + } + else + { + return client_->Initialize(ShareMode, StreamFlags, hnsBufferDuration, hnsPeriodicity, pFormat, AudioSessionGuid); + } +} + +HRESULT __stdcall audio_client_proxy::GetBufferSize(UINT32* pNumBufferFrames) +{ + return client_->GetBufferSize(pNumBufferFrames); +} + +HRESULT __stdcall audio_client_proxy::GetStreamLatency(REFERENCE_TIME* phnsLatency) +{ + return client_->GetStreamLatency(phnsLatency); +} + +HRESULT __stdcall audio_client_proxy::GetCurrentPadding(UINT32* pNumPaddingFrames) +{ + return client_->GetCurrentPadding(pNumPaddingFrames); +} + +HRESULT __stdcall audio_client_proxy::IsFormatSupported(AUDCLNT_SHAREMODE ShareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) +{ + return client_->IsFormatSupported(ShareMode, pFormat, ppClosestMatch); +} + +HRESULT __stdcall audio_client_proxy::GetMixFormat(WAVEFORMATEX** ppDeviceFormat) +{ + return client_->GetMixFormat(ppDeviceFormat); +} + +HRESULT __stdcall audio_client_proxy::GetDevicePeriod(REFERENCE_TIME* phnsDefaultDevicePeriod, REFERENCE_TIME* phnsMinimumDevicePeriod) +{ + return client_->GetDevicePeriod(phnsDefaultDevicePeriod, phnsMinimumDevicePeriod); +} + +HRESULT __stdcall audio_client_proxy::Start(void) +{ + return client_->Start(); +} + +HRESULT __stdcall audio_client_proxy::Stop(void) +{ + return client_->Stop(); +} + +HRESULT __stdcall audio_client_proxy::Reset(void) +{ + return client_->Reset(); +} + +HRESULT __stdcall audio_client_proxy::SetEventHandle(HANDLE eventHandle) +{ + return client_->SetEventHandle(eventHandle); +} + +HRESULT __stdcall audio_client_proxy::GetService(REFIID riid, void** ppv) +{ + return client_->GetService(riid, ppv); +} \ No newline at end of file diff --git a/src/client/component/iidx/wasapi_proxy/audio_client.hpp b/src/client/component/iidx/wasapi_proxy/audio_client.hpp new file mode 100644 index 0000000..fa99d43 --- /dev/null +++ b/src/client/component/iidx/wasapi_proxy/audio_client.hpp @@ -0,0 +1,27 @@ +#pragma once + +class audio_client_proxy : public IAudioClient +{ +public: + audio_client_proxy(IAudioClient* orig); + +private: + IAudioClient* client_; + HRESULT __stdcall Initialize__(AUDCLNT_SHAREMODE ShareMode, DWORD StreamFlags, REFERENCE_TIME hnsBufferDuration, REFERENCE_TIME hnsPeriodicity, const WAVEFORMATEX* pFormat, LPCGUID AudioSessionGuid); +public: + virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) override; + virtual ULONG __stdcall AddRef(void) override; + virtual ULONG __stdcall Release(void) override; + virtual HRESULT __stdcall Initialize(AUDCLNT_SHAREMODE ShareMode, DWORD StreamFlags, REFERENCE_TIME hnsBufferDuration, REFERENCE_TIME hnsPeriodicity, const WAVEFORMATEX* pFormat, LPCGUID AudioSessionGuid) override; + virtual HRESULT __stdcall GetBufferSize(UINT32* pNumBufferFrames) override; + virtual HRESULT __stdcall GetStreamLatency(REFERENCE_TIME* phnsLatency) override; + virtual HRESULT __stdcall GetCurrentPadding(UINT32* pNumPaddingFrames) override; + virtual HRESULT __stdcall IsFormatSupported(AUDCLNT_SHAREMODE ShareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) override; + virtual HRESULT __stdcall GetMixFormat(WAVEFORMATEX** ppDeviceFormat) override; + virtual HRESULT __stdcall GetDevicePeriod(REFERENCE_TIME* phnsDefaultDevicePeriod, REFERENCE_TIME* phnsMinimumDevicePeriod) override; + virtual HRESULT __stdcall Start(void) override; + virtual HRESULT __stdcall Stop(void) override; + virtual HRESULT __stdcall Reset(void) override; + virtual HRESULT __stdcall SetEventHandle(HANDLE eventHandle) override; + virtual HRESULT __stdcall GetService(REFIID riid, void** ppv) override; +}; \ No newline at end of file diff --git a/src/client/component/iidx/wasapi_proxy/mmdevice.cpp b/src/client/component/iidx/wasapi_proxy/mmdevice.cpp new file mode 100644 index 0000000..f0a9e1f --- /dev/null +++ b/src/client/component/iidx/wasapi_proxy/mmdevice.cpp @@ -0,0 +1,59 @@ +#include +#include "mmdevice.hpp" +#include "audio_client.hpp" + +mmdevice_proxy::mmdevice_proxy(IMMDevice* orig) : device_(orig) +{ +} + +HRESULT __stdcall mmdevice_proxy::QueryInterface(REFIID riid, void** ppvObj) +{ + *ppvObj = nullptr; + + auto hr = device_->QueryInterface(riid, ppvObj); + if (hr == NOERROR) *ppvObj = this; + + return hr; +} + +ULONG __stdcall mmdevice_proxy::AddRef(void) +{ + return device_->AddRef(); +} + +ULONG __stdcall mmdevice_proxy::Release(void) +{ + auto count = device_->Release(); + + if (!count) + { + delete this; + } + + return count; +} + +HRESULT __stdcall mmdevice_proxy::Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) +{ + auto hr = device_->Activate(iid, dwClsCtx, pActivationParams, ppInterface); + if (SUCCEEDED(hr) && IsEqualIID(iid, __uuidof(IAudioClient))) + { + *ppInterface = new audio_client_proxy(reinterpret_cast(*ppInterface)); + } + return hr; +} + +HRESULT __stdcall mmdevice_proxy::OpenPropertyStore(DWORD stgmAccess, IPropertyStore** ppProperties) +{ + return device_->OpenPropertyStore(stgmAccess, ppProperties); +} + +HRESULT __stdcall mmdevice_proxy::GetId(LPWSTR* ppstrId) +{ + return device_->GetId(ppstrId); +} + +HRESULT __stdcall mmdevice_proxy::GetState(DWORD* pdwState) +{ + return device_->GetState(pdwState); +} \ No newline at end of file diff --git a/src/client/component/iidx/wasapi_proxy/mmdevice.hpp b/src/client/component/iidx/wasapi_proxy/mmdevice.hpp new file mode 100644 index 0000000..990d39f --- /dev/null +++ b/src/client/component/iidx/wasapi_proxy/mmdevice.hpp @@ -0,0 +1,18 @@ +#pragma once + +class mmdevice_proxy : public IMMDevice +{ +public: + mmdevice_proxy(IMMDevice* orig); + +private: + IMMDevice* device_; +public: + virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) override; + virtual ULONG __stdcall AddRef(void) override; + virtual ULONG __stdcall Release(void) override; + virtual HRESULT __stdcall Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) override; + virtual HRESULT __stdcall OpenPropertyStore(DWORD stgmAccess, IPropertyStore** ppProperties) override; + virtual HRESULT __stdcall GetId(LPWSTR* ppstrId) override; + virtual HRESULT __stdcall GetState(DWORD* pdwState) override; +}; \ No newline at end of file diff --git a/src/client/component/iidx/wasapi_proxy/mmdevice_collection.cpp b/src/client/component/iidx/wasapi_proxy/mmdevice_collection.cpp new file mode 100644 index 0000000..3c47032 --- /dev/null +++ b/src/client/component/iidx/wasapi_proxy/mmdevice_collection.cpp @@ -0,0 +1,48 @@ +#include +#include "mmdevice_collection.hpp" +#include "mmdevice.hpp" + +mmdevice_collection_proxy::mmdevice_collection_proxy(IMMDeviceCollection* orig) : collection_(orig) +{ +} + +HRESULT __stdcall mmdevice_collection_proxy::QueryInterface(REFIID riid, void** ppvObj) +{ + *ppvObj = nullptr; + + auto hr = collection_->QueryInterface(riid, ppvObj); + if (hr == NOERROR) *ppvObj = this; + + return hr; +} + +ULONG __stdcall mmdevice_collection_proxy::AddRef(void) +{ + return collection_->AddRef(); +} + +ULONG __stdcall mmdevice_collection_proxy::Release(void) +{ + auto count = collection_->Release(); + + if (!count) + { + delete this; + } + + return count; +} + +HRESULT __stdcall mmdevice_collection_proxy::GetCount(UINT* pcDevices) +{ + return collection_->GetCount(pcDevices); +} +HRESULT __stdcall mmdevice_collection_proxy::Item(UINT nDevice, IMMDevice** ppDevice) +{ + auto hr = collection_->Item(nDevice, ppDevice); + if (SUCCEEDED(hr)) + { + *ppDevice = new mmdevice_proxy(*ppDevice); + } + return hr; +} \ No newline at end of file diff --git a/src/client/component/iidx/wasapi_proxy/mmdevice_collection.hpp b/src/client/component/iidx/wasapi_proxy/mmdevice_collection.hpp new file mode 100644 index 0000000..b7696c2 --- /dev/null +++ b/src/client/component/iidx/wasapi_proxy/mmdevice_collection.hpp @@ -0,0 +1,16 @@ +#pragma once + +class mmdevice_collection_proxy : public IMMDeviceCollection +{ +public: + mmdevice_collection_proxy(IMMDeviceCollection* orig); + +private: + IMMDeviceCollection* collection_; +public: + virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) override; + virtual ULONG __stdcall AddRef(void) override; + virtual ULONG __stdcall Release(void) override; + virtual HRESULT __stdcall GetCount(UINT* pcDevices) override; + virtual HRESULT __stdcall Item(UINT nDevice, IMMDevice** ppDevice) override; +}; diff --git a/src/client/component/iidx/wasapi_proxy/mmdevice_enumerator.cpp b/src/client/component/iidx/wasapi_proxy/mmdevice_enumerator.cpp new file mode 100644 index 0000000..f5d4e41 --- /dev/null +++ b/src/client/component/iidx/wasapi_proxy/mmdevice_enumerator.cpp @@ -0,0 +1,75 @@ +#include +#include "mmdevice_enumerator.hpp" +#include "mmdevice_collection.hpp" +#include "mmdevice.hpp" + +mmdevice_enumerator_proxy::mmdevice_enumerator_proxy(IMMDeviceEnumerator* orig) : enumerator_(orig) +{ +} + +HRESULT __stdcall mmdevice_enumerator_proxy::QueryInterface(REFIID riid, void** ppvObj) +{ + *ppvObj = nullptr; + + auto hr = enumerator_->QueryInterface(riid, ppvObj); + if (hr == NOERROR) *ppvObj = this; + + return hr; +} + +ULONG __stdcall mmdevice_enumerator_proxy::AddRef(void) +{ + return enumerator_->AddRef(); +} + +ULONG __stdcall mmdevice_enumerator_proxy::Release(void) +{ + auto count = enumerator_->Release(); + + if (!count) + { + delete this; + } + + return count; +} + +HRESULT __stdcall mmdevice_enumerator_proxy::EnumAudioEndpoints(EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection** ppDevices) +{ + auto hr = enumerator_->EnumAudioEndpoints(dataFlow, dwStateMask, ppDevices); + if (SUCCEEDED(hr)) + { + *ppDevices = new mmdevice_collection_proxy(*ppDevices); + } + return hr; +} + +HRESULT __stdcall mmdevice_enumerator_proxy::GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice** ppEndpoint) +{ + auto hr = enumerator_->GetDefaultAudioEndpoint(dataFlow, role, ppEndpoint); + if (SUCCEEDED(hr)) + { + *ppEndpoint = new mmdevice_proxy(*ppEndpoint); + } + return hr; +} + +HRESULT __stdcall mmdevice_enumerator_proxy::GetDevice(LPCWSTR pwstrId, IMMDevice** ppDevice) +{ + auto hr = enumerator_->GetDevice(pwstrId, ppDevice); + if (SUCCEEDED(hr)) + { + *ppDevice = new mmdevice_proxy(*ppDevice); + } + return hr; +} + +HRESULT __stdcall mmdevice_enumerator_proxy::RegisterEndpointNotificationCallback(IMMNotificationClient* pClient) +{ + return enumerator_->RegisterEndpointNotificationCallback(pClient); +} + +HRESULT __stdcall mmdevice_enumerator_proxy::UnregisterEndpointNotificationCallback(IMMNotificationClient* pClient) +{ + return enumerator_->UnregisterEndpointNotificationCallback(pClient); +} \ No newline at end of file diff --git a/src/client/component/iidx/wasapi_proxy/mmdevice_enumerator.hpp b/src/client/component/iidx/wasapi_proxy/mmdevice_enumerator.hpp new file mode 100644 index 0000000..f210613 --- /dev/null +++ b/src/client/component/iidx/wasapi_proxy/mmdevice_enumerator.hpp @@ -0,0 +1,19 @@ +#pragma once + +class mmdevice_enumerator_proxy : public IMMDeviceEnumerator +{ +public: + mmdevice_enumerator_proxy(IMMDeviceEnumerator* orig); + +private: + IMMDeviceEnumerator* enumerator_; +public: + virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj) override; + virtual ULONG __stdcall AddRef(void) override; + virtual ULONG __stdcall Release(void) override; + virtual HRESULT __stdcall EnumAudioEndpoints(EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection** ppDevices) override; + virtual HRESULT __stdcall GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice** ppEndpoint) override; + virtual HRESULT __stdcall GetDevice(LPCWSTR pwstrId, IMMDevice** ppDevice) override; + virtual HRESULT __stdcall RegisterEndpointNotificationCallback(IMMNotificationClient* pClient) override; + virtual HRESULT __stdcall UnregisterEndpointNotificationCallback(IMMNotificationClient* pClient) override; +}; diff --git a/src/client/main.cpp b/src/client/main.cpp index 5fcc608..d48887b 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -180,7 +180,7 @@ static void set_qos_mode(bool high) SetProcessInformation(self, ProcessPowerThrottling, &priority_info, sizeof(PROCESS_POWER_THROTTLING_STATE)); SetProcessInformation(self, ProcessMemoryPriority, &power_state, sizeof(MEMORY_PRIORITY_INFORMATION)); - SetPriorityClass(self, IDLE_PRIORITY_CLASS); + SetPriorityClass(self, high ? HIGH_PRIORITY_CLASS : IDLE_PRIORITY_CLASS); } int main() diff --git a/src/client/std_include.hpp b/src/client/std_include.hpp index ccf3bcc..f07e140 100644 --- a/src/client/std_include.hpp +++ b/src/client/std_include.hpp @@ -50,6 +50,8 @@ #include #include #include +#include +#include #include // min and max is required by gdi, therefore NOMINMAX won't work From 89acceba717503534f10b989ad3d9eae6b09bddb Mon Sep 17 00:00:00 2001 From: RERASER <2641320887@qq.com> Date: Sat, 2 Aug 2025 23:29:37 +0800 Subject: [PATCH 2/2] use latest glaze to fix msvc error --- deps/saucer/include/glaze/api/api.hpp | 2 +- deps/saucer/include/glaze/api/hash.hpp | 4 - deps/saucer/include/glaze/api/impl.hpp | 33 +- deps/saucer/include/glaze/api/lib.hpp | 2 +- deps/saucer/include/glaze/api/trait.hpp | 20 +- deps/saucer/include/glaze/api/tuplet.hpp | 4 +- deps/saucer/include/glaze/base64/base64.hpp | 65 + deps/saucer/include/glaze/beve.hpp | 11 + .../glaze/{binary => beve}/beve_to_json.hpp | 227 +- .../include/glaze/{binary => beve}/header.hpp | 50 +- .../include/glaze/{binary => beve}/ptr.hpp | 8 +- deps/saucer/include/glaze/beve/read.hpp | 1594 ++++++ deps/saucer/include/glaze/beve/skip.hpp | 241 + .../glaze/{binary => beve}/wrappers.hpp | 15 +- deps/saucer/include/glaze/beve/write.hpp | 987 ++++ deps/saucer/include/glaze/binary.hpp | 11 - deps/saucer/include/glaze/binary/custom.hpp | 192 - deps/saucer/include/glaze/binary/read.hpp | 1364 ----- deps/saucer/include/glaze/binary/skip.hpp | 161 - deps/saucer/include/glaze/binary/write.hpp | 965 ---- deps/saucer/include/glaze/compare/approx.hpp | 11 +- deps/saucer/include/glaze/compare/compare.hpp | 51 +- .../glaze/concepts/container_concepts.hpp | 234 +- .../include/glaze/containers/flat_map.hpp | 327 ++ .../glaze/containers/inplace_vector.hpp | 1111 ++++ .../saucer/include/glaze/core/array_apply.hpp | 52 + deps/saucer/include/glaze/core/cast.hpp | 94 + deps/saucer/include/glaze/core/common.hpp | 1188 +---- deps/saucer/include/glaze/core/constraint.hpp | 205 + deps/saucer/include/glaze/core/context.hpp | 146 +- .../include/glaze/core/convert_struct.hpp | 49 + .../include/glaze/{json => core}/custom.hpp | 62 +- .../include/glaze/core/error_category.hpp | 173 + .../include/glaze/core/feature_test.hpp | 35 + deps/saucer/include/glaze/core/manage.hpp | 129 + deps/saucer/include/glaze/core/meta.hpp | 160 +- deps/saucer/include/glaze/core/opts.hpp | 586 ++- deps/saucer/include/glaze/core/ptr.hpp | 9 +- deps/saucer/include/glaze/core/read.hpp | 99 +- deps/saucer/include/glaze/core/reflect.hpp | 2074 ++++++++ .../include/glaze/core/reflection_tuple.hpp | 39 - deps/saucer/include/glaze/core/seek.hpp | 437 +- deps/saucer/include/glaze/core/to.hpp | 42 + deps/saucer/include/glaze/core/wrappers.hpp | 115 +- deps/saucer/include/glaze/core/write.hpp | 64 +- .../saucer/include/glaze/core/write_chars.hpp | 35 +- deps/saucer/include/glaze/csv.hpp | 1 + deps/saucer/include/glaze/csv/read.hpp | 1157 +++-- deps/saucer/include/glaze/csv/write.hpp | 502 +- deps/saucer/include/glaze/eetf.hpp | 11 + deps/saucer/include/glaze/eetf/cmp.hpp | 50 + deps/saucer/include/glaze/eetf/defs.hpp | 12 + deps/saucer/include/glaze/eetf/ei.hpp | 435 ++ deps/saucer/include/glaze/eetf/opts.hpp | 21 + deps/saucer/include/glaze/eetf/read.hpp | 391 ++ deps/saucer/include/glaze/eetf/types.hpp | 80 + deps/saucer/include/glaze/eetf/wrappers.hpp | 53 + deps/saucer/include/glaze/eetf/write.hpp | 299 ++ .../glaze/exceptions/binary_exceptions.hpp | 32 +- .../glaze/exceptions/core_exceptions.hpp | 24 +- .../glaze/exceptions/csv_exceptions.hpp | 4 +- .../exceptions/json_schema_exceptions.hpp | 4 +- deps/saucer/include/glaze/ext/cli_menu.hpp | 176 +- deps/saucer/include/glaze/ext/eigen.hpp | 293 +- deps/saucer/include/glaze/ext/glaze_asio.hpp | 674 ++- deps/saucer/include/glaze/ext/jsonrpc.hpp | 21 +- deps/saucer/include/glaze/file/file_ops.hpp | 8 +- .../include/glaze/file/hostname_include.hpp | 145 +- .../saucer/include/glaze/file/raw_or_file.hpp | 105 +- .../include/glaze/file/read_directory.hpp | 56 + .../include/glaze/file/write_directory.hpp | 56 + .../saucer/include/glaze/format/format_to.hpp | 23 + deps/saucer/include/glaze/glaze.hpp | 8 +- .../saucer/include/glaze/glaze_exceptions.hpp | 8 +- deps/saucer/include/glaze/json.hpp | 8 +- .../include/glaze/json/escape_unicode.hpp | 257 + deps/saucer/include/glaze/json/invoke.hpp | 272 +- deps/saucer/include/glaze/json/jmespath.hpp | 1121 ++++ .../include/glaze/json/json_concepts.hpp | 30 + .../saucer/include/glaze/json/json_format.hpp | 24 +- deps/saucer/include/glaze/json/json_ptr.hpp | 118 +- deps/saucer/include/glaze/json/json_t.hpp | 301 +- deps/saucer/include/glaze/json/manage.hpp | 129 - .../glaze/json/max_write_precision.hpp | 93 +- deps/saucer/include/glaze/json/minify.hpp | 105 +- deps/saucer/include/glaze/json/ndjson.hpp | 440 +- deps/saucer/include/glaze/json/prettify.hpp | 84 +- deps/saucer/include/glaze/json/ptr.hpp | 2 - deps/saucer/include/glaze/json/raw_string.hpp | 89 +- deps/saucer/include/glaze/json/read.hpp | 4611 ++++++++++------- deps/saucer/include/glaze/json/schema.hpp | 513 +- deps/saucer/include/glaze/json/skip.hpp | 259 +- deps/saucer/include/glaze/json/study.hpp | 6 +- deps/saucer/include/glaze/json/wrappers.hpp | 102 +- deps/saucer/include/glaze/json/write.hpp | 2820 +++++----- deps/saucer/include/glaze/net/cors.hpp | 189 + deps/saucer/include/glaze/net/http.hpp | 150 + deps/saucer/include/glaze/net/http_client.hpp | 1227 +++++ deps/saucer/include/glaze/net/http_router.hpp | 962 ++++ deps/saucer/include/glaze/net/http_server.hpp | 1287 +++++ deps/saucer/include/glaze/net/openapi.hpp | 90 + .../include/glaze/net/rest_registry_impl.hpp | 315 ++ .../glaze/net/websocket_connection.hpp | 790 +++ deps/saucer/include/glaze/record/recorder.hpp | 283 +- .../include/glaze/reflection/get_name.hpp | 107 +- .../include/glaze/reflection/reflect.hpp | 174 - .../include/glaze/reflection/to_tuple.hpp | 2465 +++++---- deps/saucer/include/glaze/rpc/registry.hpp | 217 + deps/saucer/include/glaze/rpc/repe.hpp | 1205 ----- deps/saucer/include/glaze/rpc/repe/header.hpp | 68 + deps/saucer/include/glaze/rpc/repe/repe.hpp | 176 + .../glaze/rpc/repe/repe_registry_impl.hpp | 235 + deps/saucer/include/glaze/stencil/stencil.hpp | 402 ++ .../include/glaze/stencil/stencilcount.hpp | 183 + deps/saucer/include/glaze/thread/async.hpp | 126 + .../include/glaze/thread/async_string.hpp | 800 +++ .../include/glaze/thread/async_vector.hpp | 417 ++ deps/saucer/include/glaze/thread/atomic.hpp | 48 + deps/saucer/include/glaze/thread/guard.hpp | 275 + .../include/glaze/thread/shared_async_map.hpp | 612 +++ .../glaze/thread/shared_async_vector.hpp | 637 +++ .../include/glaze/thread/threadpool.hpp | 11 +- .../include/glaze/thread/value_proxy.hpp | 22 + deps/saucer/include/glaze/toml.hpp | 7 + deps/saucer/include/glaze/toml/opts.hpp | 31 + deps/saucer/include/glaze/toml/read.hpp | 945 ++++ deps/saucer/include/glaze/toml/write.hpp | 581 +++ deps/saucer/include/glaze/trace/trace.hpp | 253 +- deps/saucer/include/glaze/tuplet/tuple.hpp | 590 +-- deps/saucer/include/glaze/util/any.hpp | 237 - deps/saucer/include/glaze/util/atoi.hpp | 946 ++++ deps/saucer/include/glaze/util/bit_array.hpp | 16 +- deps/saucer/include/glaze/util/comment.hpp | 19 - deps/saucer/include/glaze/util/compare.hpp | 184 +- deps/saucer/include/glaze/util/convert.hpp | 11 + deps/saucer/include/glaze/util/dragonbox.hpp | 4233 +++++++++++++++ deps/saucer/include/glaze/util/dtoa.hpp | 1115 +--- deps/saucer/include/glaze/util/dump.hpp | 222 +- deps/saucer/include/glaze/util/expected.hpp | 1505 +----- deps/saucer/include/glaze/util/fast_float.hpp | 4447 ++++++++++++++++ deps/saucer/include/glaze/util/for_each.hpp | 324 +- .../include/glaze/util/glaze_fast_float.hpp | 251 + deps/saucer/include/glaze/util/hash_map.hpp | 218 +- deps/saucer/include/glaze/util/inline.hpp | 22 +- deps/saucer/include/glaze/util/itoa.hpp | 18 +- .../saucer/include/glaze/util/memory_pool.hpp | 76 + deps/saucer/include/glaze/util/parse.hpp | 951 ++-- deps/saucer/include/glaze/util/poly.hpp | 169 - deps/saucer/include/glaze/util/primes_64.hpp | 66 + deps/saucer/include/glaze/util/stoui64.hpp | 390 -- .../include/glaze/util/string_literal.hpp | 58 +- deps/saucer/include/glaze/util/strod.hpp | 797 --- deps/saucer/include/glaze/util/tuple.hpp | 155 +- .../saucer/include/glaze/util/type_traits.hpp | 86 + deps/saucer/include/glaze/util/variant.hpp | 18 +- deps/saucer/include/glaze/version.hpp | 36 + .../component/iidx/custom_resolution.cpp | 8 + 157 files changed, 43804 insertions(+), 18170 deletions(-) create mode 100644 deps/saucer/include/glaze/base64/base64.hpp create mode 100644 deps/saucer/include/glaze/beve.hpp rename deps/saucer/include/glaze/{binary => beve}/beve_to_json.hpp (68%) rename deps/saucer/include/glaze/{binary => beve}/header.hpp (68%) rename deps/saucer/include/glaze/{binary => beve}/ptr.hpp (58%) create mode 100644 deps/saucer/include/glaze/beve/read.hpp create mode 100644 deps/saucer/include/glaze/beve/skip.hpp rename deps/saucer/include/glaze/{binary => beve}/wrappers.hpp (65%) create mode 100644 deps/saucer/include/glaze/beve/write.hpp delete mode 100644 deps/saucer/include/glaze/binary.hpp delete mode 100644 deps/saucer/include/glaze/binary/custom.hpp delete mode 100644 deps/saucer/include/glaze/binary/read.hpp delete mode 100644 deps/saucer/include/glaze/binary/skip.hpp delete mode 100644 deps/saucer/include/glaze/binary/write.hpp create mode 100644 deps/saucer/include/glaze/containers/flat_map.hpp create mode 100644 deps/saucer/include/glaze/containers/inplace_vector.hpp create mode 100644 deps/saucer/include/glaze/core/array_apply.hpp create mode 100644 deps/saucer/include/glaze/core/cast.hpp create mode 100644 deps/saucer/include/glaze/core/constraint.hpp create mode 100644 deps/saucer/include/glaze/core/convert_struct.hpp rename deps/saucer/include/glaze/{json => core}/custom.hpp (76%) create mode 100644 deps/saucer/include/glaze/core/error_category.hpp create mode 100644 deps/saucer/include/glaze/core/feature_test.hpp create mode 100644 deps/saucer/include/glaze/core/manage.hpp create mode 100644 deps/saucer/include/glaze/core/reflect.hpp delete mode 100644 deps/saucer/include/glaze/core/reflection_tuple.hpp create mode 100644 deps/saucer/include/glaze/core/to.hpp create mode 100644 deps/saucer/include/glaze/eetf.hpp create mode 100644 deps/saucer/include/glaze/eetf/cmp.hpp create mode 100644 deps/saucer/include/glaze/eetf/defs.hpp create mode 100644 deps/saucer/include/glaze/eetf/ei.hpp create mode 100644 deps/saucer/include/glaze/eetf/opts.hpp create mode 100644 deps/saucer/include/glaze/eetf/read.hpp create mode 100644 deps/saucer/include/glaze/eetf/types.hpp create mode 100644 deps/saucer/include/glaze/eetf/wrappers.hpp create mode 100644 deps/saucer/include/glaze/eetf/write.hpp create mode 100644 deps/saucer/include/glaze/file/read_directory.hpp create mode 100644 deps/saucer/include/glaze/file/write_directory.hpp create mode 100644 deps/saucer/include/glaze/format/format_to.hpp create mode 100644 deps/saucer/include/glaze/json/escape_unicode.hpp create mode 100644 deps/saucer/include/glaze/json/jmespath.hpp create mode 100644 deps/saucer/include/glaze/json/json_concepts.hpp delete mode 100644 deps/saucer/include/glaze/json/manage.hpp create mode 100644 deps/saucer/include/glaze/net/cors.hpp create mode 100644 deps/saucer/include/glaze/net/http.hpp create mode 100644 deps/saucer/include/glaze/net/http_client.hpp create mode 100644 deps/saucer/include/glaze/net/http_router.hpp create mode 100644 deps/saucer/include/glaze/net/http_server.hpp create mode 100644 deps/saucer/include/glaze/net/openapi.hpp create mode 100644 deps/saucer/include/glaze/net/rest_registry_impl.hpp create mode 100644 deps/saucer/include/glaze/net/websocket_connection.hpp delete mode 100644 deps/saucer/include/glaze/reflection/reflect.hpp create mode 100644 deps/saucer/include/glaze/rpc/registry.hpp delete mode 100644 deps/saucer/include/glaze/rpc/repe.hpp create mode 100644 deps/saucer/include/glaze/rpc/repe/header.hpp create mode 100644 deps/saucer/include/glaze/rpc/repe/repe.hpp create mode 100644 deps/saucer/include/glaze/rpc/repe/repe_registry_impl.hpp create mode 100644 deps/saucer/include/glaze/stencil/stencil.hpp create mode 100644 deps/saucer/include/glaze/stencil/stencilcount.hpp create mode 100644 deps/saucer/include/glaze/thread/async.hpp create mode 100644 deps/saucer/include/glaze/thread/async_string.hpp create mode 100644 deps/saucer/include/glaze/thread/async_vector.hpp create mode 100644 deps/saucer/include/glaze/thread/atomic.hpp create mode 100644 deps/saucer/include/glaze/thread/guard.hpp create mode 100644 deps/saucer/include/glaze/thread/shared_async_map.hpp create mode 100644 deps/saucer/include/glaze/thread/shared_async_vector.hpp create mode 100644 deps/saucer/include/glaze/thread/value_proxy.hpp create mode 100644 deps/saucer/include/glaze/toml.hpp create mode 100644 deps/saucer/include/glaze/toml/opts.hpp create mode 100644 deps/saucer/include/glaze/toml/read.hpp create mode 100644 deps/saucer/include/glaze/toml/write.hpp delete mode 100644 deps/saucer/include/glaze/util/any.hpp create mode 100644 deps/saucer/include/glaze/util/atoi.hpp delete mode 100644 deps/saucer/include/glaze/util/comment.hpp create mode 100644 deps/saucer/include/glaze/util/convert.hpp create mode 100644 deps/saucer/include/glaze/util/dragonbox.hpp create mode 100644 deps/saucer/include/glaze/util/fast_float.hpp create mode 100644 deps/saucer/include/glaze/util/glaze_fast_float.hpp create mode 100644 deps/saucer/include/glaze/util/memory_pool.hpp delete mode 100644 deps/saucer/include/glaze/util/poly.hpp create mode 100644 deps/saucer/include/glaze/util/primes_64.hpp delete mode 100644 deps/saucer/include/glaze/util/stoui64.hpp delete mode 100644 deps/saucer/include/glaze/util/strod.hpp create mode 100644 deps/saucer/include/glaze/version.hpp diff --git a/deps/saucer/include/glaze/api/api.hpp b/deps/saucer/include/glaze/api/api.hpp index b7ce73e..1e6690f 100644 --- a/deps/saucer/include/glaze/api/api.hpp +++ b/deps/saucer/include/glaze/api/api.hpp @@ -100,7 +100,7 @@ namespace glz auto tuple_args = std::forward_as_tuple(std::forward(args)...); - for_each([&](auto I) { std::get(arguments) = &std::get(tuple_args); }); + for_each([&]() { std::get(arguments) = &std::get(tuple_args); }); if constexpr (std::is_pointer_v) { void* ptr{}; diff --git a/deps/saucer/include/glaze/api/hash.hpp b/deps/saucer/include/glaze/api/hash.hpp index c34f02d..09c220c 100644 --- a/deps/saucer/include/glaze/api/hash.hpp +++ b/deps/saucer/include/glaze/api/hash.hpp @@ -77,10 +77,6 @@ namespace glz static constexpr type value{}; static constexpr const std::string_view get() { return {value.data, num_digits(x)}; } }; - - /* instantiate numeric_string::value as needed for different numbers */ - template - constexpr typename numeric_string::type numeric_string::value; } template diff --git a/deps/saucer/include/glaze/api/impl.hpp b/deps/saucer/include/glaze/api/impl.hpp index 4f4ab21..1422f17 100644 --- a/deps/saucer/include/glaze/api/impl.hpp +++ b/deps/saucer/include/glaze/api/impl.hpp @@ -19,8 +19,8 @@ #include "glaze/api/std/vector.hpp" #include "glaze/api/tuplet.hpp" #include "glaze/api/type_support.hpp" -#include "glaze/binary/read.hpp" -#include "glaze/binary/write.hpp" +#include "glaze/beve/read.hpp" +#include "glaze/beve/write.hpp" #include "glaze/glaze.hpp" #include "glaze/json/read.hpp" #include "glaze/json/write.hpp" @@ -36,7 +36,7 @@ namespace glz [[nodiscard]] bool contains(const sv path) noexcept override { - return detail::seek_impl([&](auto&&) {}, user, path); + return seek([&](auto&&) {}, user, path); } bool read(const uint32_t format, const sv path, const sv data) noexcept override @@ -44,12 +44,11 @@ namespace glz error_ctx pe{}; bool success; - if (format == json) { - success = detail::seek_impl([&](auto&& val) { pe = glz::read(val, data); }, user, path); + if (format == JSON) { + success = seek([&](auto&& val) { pe = glz::read(val, data); }, user, path); } else { - success = - detail::seek_impl([&](auto&& val) { pe = glz::read(val, data); }, user, path); + success = seek([&](auto&& val) { pe = glz::read(val, data); }, user, path); } if (success) { @@ -64,11 +63,11 @@ namespace glz bool write(const uint32_t format, const sv path, std::string& data) noexcept override { // TODO: Support write errors when seeking - if (format == json) { - return detail::seek_impl([&](auto&& val) { std::ignore = glz::write_json(val, data); }, user, path); + if (format == JSON) { + return seek([&](auto&& val) { std::ignore = glz::write_json(val, data); }, user, path); } else { - return detail::seek_impl([&](auto&& val) { std::ignore = glz::write_binary(val, data); }, user, path); + return seek([&](auto&& val) { std::ignore = glz::write_beve(val, data); }, user, path); } } @@ -106,11 +105,11 @@ namespace glz bool found = false; - detail::seek_impl( + seek( [&](auto&& parent) { using P = std::decay_t; - detail::seek_impl( + seek( [&](auto&& val) { using V = std::decay_t; if constexpr (std::is_member_function_pointer_v) { @@ -174,7 +173,7 @@ namespace glz decltype(auto) unwrap(T&& val) { using V = std::decay_t; - if constexpr (detail::nullable_t) { + if constexpr (nullable_t) { if (val) { return unwrap(*val); } @@ -197,7 +196,7 @@ namespace glz glz::hash_t type_hash{}; - const auto success = detail::seek_impl( + const auto success = seek( [&](auto&& val) { using V = std::decay_t; if constexpr (std::is_member_function_pointer_v) { @@ -231,11 +230,11 @@ namespace glz const auto parent_ptr = p.first; const auto last_ptr = p.second; - detail::seek_impl( + seek( [&](auto&& parent) { using P = std::decay_t; - detail::seek_impl( + seek( [&](auto&& val) { using V = std::decay_t; if constexpr (std::is_member_function_pointer_v) { @@ -303,7 +302,7 @@ namespace glz using T = std::tuple; constexpr auto N = sizeof...(Args); - for_each([&](auto I) { + for_each([&]() { using V = glz::tuple_element_t; ptr->emplace(name_v, make_api); }); diff --git a/deps/saucer/include/glaze/api/lib.hpp b/deps/saucer/include/glaze/api/lib.hpp index b966ad8..b783957 100644 --- a/deps/saucer/include/glaze/api/lib.hpp +++ b/deps/saucer/include/glaze/api/lib.hpp @@ -45,7 +45,7 @@ namespace glz struct lib_loader final { - using create = glz::iface_fn (*)(void); + using create = glz::iface_fn (*)(void) noexcept; iface api_map{}; std::vector loaded_libs{}; diff --git a/deps/saucer/include/glaze/api/trait.hpp b/deps/saucer/include/glaze/api/trait.hpp index f766414..8cd1da1 100644 --- a/deps/saucer/include/glaze/api/trait.hpp +++ b/deps/saucer/include/glaze/api/trait.hpp @@ -8,6 +8,7 @@ #include "glaze/api/hash.hpp" #include "glaze/core/common.hpp" #include "glaze/core/meta.hpp" +#include "glaze/core/reflect.hpp" #include "glaze/util/string_literal.hpp" namespace glz @@ -21,9 +22,9 @@ namespace glz static constexpr sv type_size_hash = hash128_v>; // must hash for consistent length - static constexpr sv major_version = hash128_i_v[0]>; // must hash for consistent length - static constexpr sv minor_version = hash128_i_v[1]>; // must hash for consistent length - static constexpr sv revision = hash128_i_v[2]>; // must hash for consistent length + static constexpr sv major_version = hash128_i_v.major>; // must hash for consistent length + static constexpr sv minor_version = hash128_i_v.minor>; // must hash for consistent length + static constexpr sv revision = hash128_i_v.patch>; // must hash for consistent length #define std_trait(x) static constexpr sv x = to_sv>() std_trait(is_trivial); @@ -50,8 +51,6 @@ namespace glz std_trait(is_aggregate); #undef std_trait - // static constexpr sv cplusplus = empty_if>(to_sv<__cplusplus>()); - #ifdef __clang__ static constexpr sv clang = "clang"; #endif @@ -64,7 +63,14 @@ namespace glz static constexpr sv blank = ""; // to end possible macros - static constexpr sv members = glz::name_v>; + static constexpr sv members = [] { + if constexpr (glaze_object_t || reflectable) { + return glz::name_v>; + } + else { + return ""; + } + }(); static constexpr sv to_hash = join_v; - static constexpr version_t version = ::glz::version; + static constexpr version_t version = ::glz::version_v; static constexpr sv hash = hash128_v; }; diff --git a/deps/saucer/include/glaze/api/tuplet.hpp b/deps/saucer/include/glaze/api/tuplet.hpp index 8c798a2..4f6d37f 100644 --- a/deps/saucer/include/glaze/api/tuplet.hpp +++ b/deps/saucer/include/glaze/api/tuplet.hpp @@ -9,10 +9,10 @@ namespace glz { template - struct meta> + struct meta> { static constexpr std::string_view name = [](std::index_sequence) { - return join_v, + return join_v, ((I != sizeof...(T) - 1) ? join_v, chars<",">> : join_v>)..., chars<">">>; }(std::make_index_sequence{}); }; diff --git a/deps/saucer/include/glaze/base64/base64.hpp b/deps/saucer/include/glaze/base64/base64.hpp new file mode 100644 index 0000000..e707b7f --- /dev/null +++ b/deps/saucer/include/glaze/base64/base64.hpp @@ -0,0 +1,65 @@ +// Glaze Library +// For the license information refer to glaze.hpp + +#pragma once + +#include +#include +#include +#include + +namespace glz +{ + inline constexpr std::string_view base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + inline std::string read_base64(const std::string_view input) + { + std::string decoded_data; + static constexpr std::array decode_table = [] { + std::array t; + t.fill(-1); + for (int i = 0; i < 64; ++i) { + t[base64_chars[i]] = i; + } + return t; + }(); + + int val = 0, valb = -8; + for (unsigned char c : input) { + if (decode_table[c] == -1) break; // Stop decoding at padding '=' or invalid characters + val = (val << 6) + decode_table[c]; + valb += 6; + if (valb >= 0) { + decoded_data.push_back((val >> valb) & 0xFF); + valb -= 8; + } + } + + return decoded_data; + } + + inline std::string write_base64(const std::string_view input) + { + std::string encoded_data; + + int val = 0, valb = -6; + for (unsigned char c : input) { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) { + encoded_data.push_back(base64_chars[(val >> valb) & 0x3F]); + valb -= 6; + } + } + if (valb > -6) { + encoded_data.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]); + } + while (encoded_data.size() % 4) { + encoded_data.push_back('='); + } + return encoded_data; + } +} diff --git a/deps/saucer/include/glaze/beve.hpp b/deps/saucer/include/glaze/beve.hpp new file mode 100644 index 0000000..1b8d063 --- /dev/null +++ b/deps/saucer/include/glaze/beve.hpp @@ -0,0 +1,11 @@ +// Glaze Library +// For the license information refer to glaze.hpp + +#pragma once + +#include "glaze/beve/header.hpp" +#include "glaze/beve/ptr.hpp" +#include "glaze/beve/read.hpp" +#include "glaze/beve/wrappers.hpp" +#include "glaze/beve/write.hpp" +#include "glaze/thread/atomic.hpp" diff --git a/deps/saucer/include/glaze/binary/beve_to_json.hpp b/deps/saucer/include/glaze/beve/beve_to_json.hpp similarity index 68% rename from deps/saucer/include/glaze/binary/beve_to_json.hpp rename to deps/saucer/include/glaze/beve/beve_to_json.hpp index 79f0326..22f571d 100644 --- a/deps/saucer/include/glaze/binary/beve_to_json.hpp +++ b/deps/saucer/include/glaze/beve/beve_to_json.hpp @@ -3,22 +3,26 @@ #pragma once -#include "glaze/binary/header.hpp" +#include "glaze/beve/header.hpp" #include "glaze/json/write.hpp" namespace glz { namespace detail { - template - inline void beve_to_json_number(auto&& tag, auto&& ctx, auto&& it, auto&&, auto& out, auto&& ix) noexcept + template + inline void beve_to_json_number(auto&& tag, auto&& ctx, auto&& it, auto&& end, auto& out, auto&& ix) noexcept { const auto number_type = (tag & 0b000'11'000) >> 3; - const uint8_t byte_count = detail::byte_count_lookup[tag >> 5]; + const uint8_t byte_count = byte_count_lookup[tag >> 5]; auto write_number = [&](T&& value) { + if ((it + sizeof(T)) > end) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } std::memcpy(&value, it, sizeof(T)); - to_json::template op(value, ctx, out, ix); + to::template op(value, ctx, out, ix); it += sizeof(T); }; @@ -100,9 +104,20 @@ namespace glz } } - template - inline void beve_to_json_value(auto&& ctx, auto&& it, auto&& end, Buffer& out, auto&& ix) noexcept + template + inline void beve_to_json_value(auto&& ctx, auto&& it, auto&& end, Buffer& out, auto&& ix, + uint32_t recursive_depth) { + // Check recursion depth limit + if (recursive_depth >= max_recursive_depth_limit) [[unlikely]] { + ctx.error = error_code::exceeded_max_recursive_depth; + return; + } + + if (it >= end) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } const auto tag = uint8_t(*it); const auto type = tag & 0b00000'111; switch (type) { @@ -129,9 +144,16 @@ namespace glz } case tag::string: { ++it; - const auto n = detail::int_from_compressed(ctx, it, end); + const auto n = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) [[unlikely]] { + return; + } + if (uint64_t(end - it) < n) [[unlikely]] { + ctx.error = error_code::unexpected_end; + return; + } const sv value{reinterpret_cast(it), n}; - to_json::template op(value, ctx, out, ix); + to::template op(value, ctx, out, ix); it += n; break; } @@ -144,17 +166,30 @@ namespace glz dump<'\n'>(out, ix); dumpn(ctx.indentation_level, out, ix); } + else { + ++ctx.indentation_level; + } const auto key_type = (tag & 0b000'11'000) >> 3; switch (key_type) { case 0: { // string key - const auto n_fields = detail::int_from_compressed(ctx, it, end); + const auto n_fields = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) { + return; + } for (size_t i = 0; i < n_fields; ++i) { // convert the key - const auto n = detail::int_from_compressed(ctx, it, end); + const auto n = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) [[unlikely]] { + return; + } + if (uint64_t(end - it) < n) [[unlikely]] { + ctx.error = error_code::unexpected_end; + return; + } const sv key{reinterpret_cast(it), n}; - to_json::template op(key, ctx, out, ix); + to::template op(key, ctx, out, ix); if constexpr (Opts.prettify) { dump<": ">(out, ix); } @@ -162,8 +197,46 @@ namespace glz dump<':'>(out, ix); } it += n; - // convert the value - beve_to_json_value(ctx, it, end, out, ix); + beve_to_json_value(ctx, it, end, out, ix, recursive_depth + 1); + if (bool(ctx.error)) [[unlikely]] { + return; + } + if (i != n_fields - 1) { + dump<','>(out, ix); + if constexpr (Opts.prettify) { + dump<'\n'>(out, ix); + dumpn(ctx.indentation_level, out, ix); + } + } + } + break; + } + case 1: + [[fallthrough]]; // signed integer key + case 2: { + // unsigned integer key + const auto n_fields = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) { + return; + } + for (size_t i = 0; i < n_fields; ++i) { + // convert the key + dump<'"'>(out, ix); + beve_to_json_number(tag, ctx, it, end, out, ix); + if (bool(ctx.error)) [[unlikely]] { + return; + } + dump<'"'>(out, ix); + if constexpr (Opts.prettify) { + dump<": ">(out, ix); + } + else { + dump<':'>(out, ix); + } + beve_to_json_value(ctx, it, end, out, ix, recursive_depth + 1); + if (bool(ctx.error)) [[unlikely]] { + return; + } if (i != n_fields - 1) { dump<','>(out, ix); if constexpr (Opts.prettify) { @@ -185,19 +258,29 @@ namespace glz dump<'\n'>(out, ix); dumpn(ctx.indentation_level, out, ix); } + else { + --ctx.indentation_level; + } dump<'}'>(out, ix); break; } case tag::typed_array: { ++it; const auto value_type = (tag & 0b000'11'000) >> 3; - const uint8_t byte_count = detail::byte_count_lookup[tag >> 5]; + const uint8_t byte_count = byte_count_lookup[tag >> 5]; auto write_array = [&](T&& value) { const auto n = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) [[unlikely]] { + return; + } for (size_t i = 0; i < n; ++i) { + if ((it + sizeof(T)) > end) [[unlikely]] { + ctx.error = error_code::unexpected_end; + return; + } std::memcpy(&value, it, sizeof(T)); - to_json::template op(value, ctx, out, ix); + to::template op(value, ctx, out, ix); it += sizeof(T); if (i != n - 1) { dump<','>(out, ix); @@ -291,10 +374,20 @@ namespace glz case 1: { // array of strings const auto n_strings = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) [[unlikely]] { + return; + } for (size_t i = 0; i < n_strings; ++i) { - const auto n = detail::int_from_compressed(ctx, it, end); + const auto n = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) [[unlikely]] { + return; + } + if (uint64_t(end - it) < n) [[unlikely]] { + ctx.error = error_code::unexpected_end; + return; + } const sv value{reinterpret_cast(it), n}; - to_json::template op(value, ctx, out, ix); + to::template op(value, ctx, out, ix); it += n; if (i != n_strings - 1) { dump<','>(out, ix); @@ -322,9 +415,15 @@ namespace glz case tag::generic_array: { ++it; const auto n = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) [[unlikely]] { + return; + } dump<'['>(out, ix); for (size_t i = 0; i < n; ++i) { - beve_to_json_value(ctx, it, end, out, ix); + beve_to_json_value(ctx, it, end, out, ix, recursive_depth + 1); + if (bool(ctx.error)) [[unlikely]] { + return; + } if (i != n - 1) { dump<','>(out, ix); } @@ -347,54 +446,19 @@ namespace glz skip_compressed_int(ctx, it, end); if (bool(ctx.error)) return; - // IMPORTANT: Code was commented because variants should typically write out the JSON value directly - // This makes sense for auto-deducible values, which should be the default behavior. - // In the future we may want a compile time option that dumps the index for cases that cannot be - // auto-deduced - /*const auto index = int_from_compressed(ctx, it, end); - - dump<'{'>(out, ix); - if constexpr (Opts.prettify) { - ctx.indentation_level += Opts.indentation_width; - dump<'\n'>(out, ix); - dumpn(ctx.indentation_level, out, ix); - } - - if constexpr (Opts.prettify) { - dump(out, ix); - } - else { - dump(out, ix); - } - - to_json>::template op(index, ctx, out, ix); - - dump<','>(out, ix); - if constexpr (Opts.prettify) { - dump<'\n'>(out, ix); - dumpn(ctx.indentation_level, out, ix); - } - - if constexpr (Opts.prettify) { - dump(out, ix); - } - else { - dump(out, ix); - }*/ - - beve_to_json_value(ctx, it, end, out, ix); - - /*if constexpr (Opts.prettify) { - ctx.indentation_level -= Opts.indentation_width; - dump<'\n'>(out, ix); - dumpn(ctx.indentation_level, out, ix); + beve_to_json_value(ctx, it, end, out, ix, recursive_depth + 1); + if (bool(ctx.error)) [[unlikely]] { + return; } - dump<'}'>(out, ix);*/ break; } case 2: { // matrices ++it; + if (it >= end) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } const auto matrix_header = uint8_t(*it); ++it; @@ -404,6 +468,9 @@ namespace glz dump<'\n'>(out, ix); dumpn(ctx.indentation_level, out, ix); } + else { + ++ctx.indentation_level; + } if constexpr (Opts.prettify) { dump(out, ix); @@ -428,7 +495,10 @@ namespace glz dump(out, ix); } - beve_to_json_value(ctx, it, end, out, ix); + beve_to_json_value(ctx, it, end, out, ix, recursive_depth + 1); + if (bool(ctx.error)) [[unlikely]] { + return; + } dump<','>(out, ix); if constexpr (Opts.prettify) { @@ -443,19 +513,29 @@ namespace glz dump(out, ix); } - beve_to_json_value(ctx, it, end, out, ix); + beve_to_json_value(ctx, it, end, out, ix, recursive_depth + 1); + if (bool(ctx.error)) [[unlikely]] { + return; + } if constexpr (Opts.prettify) { ctx.indentation_level -= Opts.indentation_width; dump<'\n'>(out, ix); dumpn(ctx.indentation_level, out, ix); } + else { + --ctx.indentation_level; + } dump<'}'>(out, ix); break; } case 3: { // complex numbers ++it; + if (it >= end) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } const auto complex_header = uint8_t(*it); ++it; @@ -464,12 +544,21 @@ namespace glz // complex array const auto number_tag = complex_header & 0b111'00000; const auto n = int_from_compressed(ctx, it, end); + if (bool(ctx.error)) [[unlikely]] { + return; + } dump<'['>(out, ix); for (size_t i = 0; i < n; ++i) { dump<'['>(out, ix); beve_to_json_number(number_tag, ctx, it, end, out, ix); + if (bool(ctx.error)) [[unlikely]] { + return; + } dump<','>(out, ix); beve_to_json_number(number_tag, ctx, it, end, out, ix); + if (bool(ctx.error)) [[unlikely]] { + return; + } dump<']'>(out, ix); if (i != n - 1) { dump<','>(out, ix); @@ -482,8 +571,14 @@ namespace glz const auto number_tag = complex_header & 0b111'00000; dump<'['>(out, ix); beve_to_json_number(number_tag, ctx, it, end, out, ix); + if (bool(ctx.error)) [[unlikely]] { + return; + } dump<','>(out, ix); beve_to_json_number(number_tag, ctx, it, end, out, ix); + if (bool(ctx.error)) [[unlikely]] { + return; + } dump<']'>(out, ix); } @@ -504,8 +599,8 @@ namespace glz } } - template - [[nodiscard]] inline error_ctx beve_to_json(const BEVEBuffer& beve, JSONBuffer& out) noexcept + template + [[nodiscard]] inline error_ctx beve_to_json(const BEVEBuffer& beve, JSONBuffer& out) { size_t ix{}; // write index @@ -515,7 +610,7 @@ namespace glz context ctx{}; while (it < end) { - detail::beve_to_json_value(ctx, it, end, out, ix); + detail::beve_to_json_value(ctx, it, end, out, ix, 0); if (bool(ctx.error)) { return {ctx.error}; } diff --git a/deps/saucer/include/glaze/binary/header.hpp b/deps/saucer/include/glaze/beve/header.hpp similarity index 68% rename from deps/saucer/include/glaze/binary/header.hpp rename to deps/saucer/include/glaze/beve/header.hpp index 3958683..d99a877 100644 --- a/deps/saucer/include/glaze/binary/header.hpp +++ b/deps/saucer/include/glaze/beve/header.hpp @@ -13,6 +13,20 @@ #include "glaze/core/context.hpp" #include "glaze/util/inline.hpp" +namespace glz +{ + GLZ_ALWAYS_INLINE bool invalid_end(is_context auto& ctx, auto&& it, auto&& end) noexcept + { + if (it >= end) [[unlikely]] { + ctx.error = error_code::unexpected_end; + return true; + } + else [[likely]] { + return false; + } + } +} + namespace glz::tag { constexpr uint8_t null = 0; @@ -46,16 +60,20 @@ namespace glz::tag constexpr uint8_t f128 = 0b100'00'001; } -namespace glz::detail +namespace glz { template constexpr uint8_t byte_count = uint8_t(std::bit_width(sizeof(T)) - 1); - constexpr std::array byte_count_lookup{1, 2, 4, 8, 16, 32, 64, 128}; + inline constexpr std::array byte_count_lookup{1, 2, 4, 8, 16, 32, 64, 128}; - [[nodiscard]] GLZ_ALWAYS_INLINE constexpr size_t int_from_compressed(is_context auto&& ctx, auto&& it, - auto&& end) noexcept + [[nodiscard]] GLZ_ALWAYS_INLINE constexpr size_t int_from_compressed(auto&& ctx, auto&& it, auto&& end) noexcept { + if (it >= end) [[unlikely]] { + ctx.error = error_code::unexpected_end; + return 0; + } + uint8_t header; std::memcpy(&header, it, 1); const uint8_t config = header & 0b000000'11; @@ -82,10 +100,22 @@ namespace glz::detail return h >> 2; } case 3: { - uint64_t h; - std::memcpy(&h, it, 8); - it += 8; - return h >> 2; + // On 32-bit systems it's impossible to address more than 4 GBiB of memory, so we should verify first if we are + // running in 64-bit mode here + if constexpr (sizeof(size_t) > sizeof(uint32_t)) { + uint64_t h; + std::memcpy(&h, it, 8); + it += 8; + h = h >> 2; + static constexpr uint64_t safety_limit = 1ull << 48; // 2^48 + if (h > safety_limit) [[unlikely]] { + ctx.error = error_code::unexpected_end; + return 0; + } + return h; + } + // Fallthrough in case we are in 32-bit mode + [[fallthrough]]; } default: return 0; @@ -94,6 +124,10 @@ namespace glz::detail GLZ_ALWAYS_INLINE constexpr void skip_compressed_int(is_context auto&& ctx, auto&& it, auto&& end) noexcept { + if (invalid_end(ctx, it, end)) { + return; + } + uint8_t header; std::memcpy(&header, it, 1); const uint8_t config = header & 0b000000'11; diff --git a/deps/saucer/include/glaze/binary/ptr.hpp b/deps/saucer/include/glaze/beve/ptr.hpp similarity index 58% rename from deps/saucer/include/glaze/binary/ptr.hpp rename to deps/saucer/include/glaze/beve/ptr.hpp index 3699d2b..6b27180 100644 --- a/deps/saucer/include/glaze/binary/ptr.hpp +++ b/deps/saucer/include/glaze/beve/ptr.hpp @@ -3,8 +3,8 @@ #pragma once -#include "glaze/binary/read.hpp" -#include "glaze/binary/write.hpp" +#include "glaze/beve/read.hpp" +#include "glaze/beve/write.hpp" #include "glaze/core/ptr.hpp" namespace glz @@ -12,12 +12,12 @@ namespace glz template bool read_as_binary(T&& root_value, const sv json_ptr, B&& buffer) { - return read_as(std::forward(root_value), json_ptr, buffer); + return read_as(std::forward(root_value), json_ptr, buffer); } template bool write_as_binary(T&& root_value, const sv json_ptr, B&& buffer) { - return write_as(std::forward(root_value), json_ptr, buffer); + return write_as(std::forward(root_value), json_ptr, buffer); } } diff --git a/deps/saucer/include/glaze/beve/read.hpp b/deps/saucer/include/glaze/beve/read.hpp new file mode 100644 index 0000000..2e36a7f --- /dev/null +++ b/deps/saucer/include/glaze/beve/read.hpp @@ -0,0 +1,1594 @@ +// Glaze Library +// For the license information refer to glaze.hpp + +#pragma once + +#include "glaze/beve/header.hpp" +#include "glaze/beve/skip.hpp" +#include "glaze/core/opts.hpp" +#include "glaze/core/read.hpp" +#include "glaze/core/reflect.hpp" +#include "glaze/file/file_ops.hpp" +#include "glaze/util/dump.hpp" + +// To handle invalid inputs we must check if (it >= end) at the beginning of each function +// This way we can always call a function after incrementing the iterator without needed to do a tail check +// If we know the first function called has an end check, we don't need a guard at the top of the function +// Also, after almost every function call we need to check if an error was produced + +namespace glz +{ + template <> + struct parse + { + template + requires(check_no_header(Opts)) + GLZ_ALWAYS_INLINE static void op(T&& value, Tag&& tag, Ctx&& ctx, It0&& it, It1&& end) + { + if constexpr (const_value_v) { + if constexpr (Opts.error_on_const_read) { + ctx.error = error_code::attempt_const_read; + } + else { + // do not read anything into the const value + skip_value::op(std::forward(ctx), std::forward(it), std::forward(end)); + } + } + else { + using V = std::remove_cvref_t; + from::template op(std::forward(value), std::forward(tag), std::forward(ctx), + std::forward(it), std::forward(end)); + } + } + + template + requires(not check_no_header(Opts)) + GLZ_ALWAYS_INLINE static void op(T&& value, Ctx&& ctx, It0&& it, It1&& end) + { + if constexpr (const_value_v) { + if constexpr (Opts.error_on_const_read) { + ctx.error = error_code::attempt_const_read; + } + else { + // do not read anything into the const value + skip_value::op(std::forward(ctx), std::forward(it), std::forward(end)); + } + } + else { + using V = std::remove_cvref_t; + from::template op(std::forward(value), std::forward(ctx), std::forward(it), + std::forward(end)); + } + } + }; + + template + requires(glaze_value_t && !custom_read) + struct from + { + template + GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, It0&& it, It1&& end) + { + using V = std::decay_t(), meta_wrapper_v))>; + from::template op(get_member(std::forward(value), meta_wrapper_v), + std::forward(ctx), std::forward(it), std::forward(end)); + } + }; + + template + struct from + { + template + GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&& ctx, auto&& it, auto&& end) noexcept + { + if (invalid_end(ctx, it, end)) { + return; + } + if (uint8_t(*it)) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } + ++it; + } + }; + + template <> + struct from