diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e535f5d..36cd11de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ set(headers src/sceneStructs.h src/preview.h src/utilities.h + src/meshio.h ) set(sources @@ -74,6 +75,7 @@ set(sources src/scene.cpp src/preview.cpp src/utilities.cpp + src/meshio.cpp ) set(imgui_headers @@ -111,6 +113,9 @@ source_group("ImGui\\Sources" FILES ${imgui_sources}) #add_subdirectory(src/ImGui) #add_subdirectory(stream_compaction) # TODO: uncomment if using your stream compaction +link_directories("${CMAKE_SOURCE_DIR}\\external\\lib") + + add_executable(${CMAKE_PROJECT_NAME} ${sources} ${headers} ${imgui_sources} ${imgui_headers}) set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES CUDA_SEPARABLE_COMPILATION ON) if(CMAKE_VERSION VERSION_LESS "3.23.0") @@ -123,8 +128,17 @@ endif() target_link_libraries(${CMAKE_PROJECT_NAME} ${LIBRARIES} cudadevrt + OpenImageDenoise #stream_compaction # TODO: uncomment if using your stream compaction ) target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE "$<$,$>:-G;-src-in-ptx>") target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE "$<$,$>:-lineinfo;-src-in-ptx>") set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${CMAKE_PROJECT_NAME}) + +file(GLOB OIDN_DLLS "${CMAKE_SOURCE_DIR}/external/lib/*.dll") +foreach(DLL ${OIDN_DLLS}) + add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${DLL}" + "$") +endforeach() diff --git a/README.md b/README.md index 110697ce..36ad1d36 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,265 @@ + + CUDA Path Tracer ================ -**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 3** +## ๐Ÿš€ Overview + +> University of Pennsylvania, CIS 5650: GPU Programming and Architecture, Project 3 - CUDA Path Tracer +> * Michael Mason +> + [Personal Website](https://www.michaelmason.xyz/) +> * Tested on: Windows 11, Ryzen 9 5900HS @ 3.00GHz 16GB, RTX 3080 (Laptop) 8192MB + +This is a Path Tracing Renderer (i.e. Arnold, RenderMan, V-Ray) written in C++ and CUDA which utilizes GPU hardware. This is an interactive renderer! It supports basic materials and scene handling, including glTF support, color & normal textures, and more! See below. + +### Supported Features + +* glTF File Format +* Albedo Maps +* Normal Maps +* Depth of field +* Open Image Denoiser intergration +* Physically-Based Materials (BxDFs) + * Matte (Perfect Diffuse BRDF) + * Mirror (Perfect Specular BRDF) + * Glass (Specular Reflection BRDF + Specular Transmission BTDF, mixed by fresnel) + * Brushed Metal (Torrance-Sparrow Microfacet BRDF Model with a Trowbridge-Reitz Distribution) + +## โœจ Feature Descriptions + +### Scene Description and Loading + +Scenes in this renderer are described with a basic JSON schema that can specify cameras, materials and object primitives, including Spheres, Cubes and Triangle Meshes (using glTF). For more information about the JSON format, check `INSTRUCTIONS.md` and the scene examples in the scenes folder. + +#### glTF meshes + +One node / one mesh glTF files can be loaded, so long as they contain the appropriate vertex attributes: positions, normals and texture coordinates (if textures are also included). + +The [glTF 2.0 specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html) was referenced for this feature and was of great help! + +

+ +

+ +

+Duck Floatie & Crash Bandicoot Boxes +

+ + +#### Albedo and Normal Maps + +With the glTF format, albedo and normal map textures are renderable and supported. + +Textures are implemented in CUDA using the `CUDA Texture Object API`. You can see how textures can be loaded into the GPU [here](https://github.com/micklemacklemore/CUDA-Path-Tracer/blob/main/src/pathtrace.cu#L126-L169), which can later be accessed by CUDA kernels [here](https://github.com/micklemacklemore/CUDA-Path-Tracer/blob/main/src/interactions.cu#L286-L296) (It's similar to `texture2D` when accessing a sampler in glsl). It's straighforward to use, however the documentation is sparse. I hope this might be another simple example on the internet to help another budding graphics student. + +

+ + +

+ +

+Albedo Pass, Surface Normal Pass +

+ +### Intel Open Image Denoise + +Intel Open Image Denoise is a high-performance open-source library for image noise filtering for ray-tracers. This library is integrated into the renderer and filters out the Monte Carlo sample noise that allows us to render smooth images faster. + +An image that would typically take 10,000 samples to render can be done in merely half the time, with potentially cleaner results. + +To denoise rendered images, the API is fairly straightforward. The denoiser accepts 32-bit floating point channels by default and you can see how it's used [here](https://github.com/micklemacklemore/CUDA-Path-Tracer/blob/main/src/pathtrace.cu#L625-L659). + +For information, go to the [OIDN homepage](https://www.openimagedenoise.org/). + +

+ + +

+ +

+Before & After Denoising +

+ +### Materials (BxDF's) + +This renderer supports basic Bidirectional Scattering Distribution Function (BSDF) materials. At a high-level, BSDF's describe how light is scattered from certain materials. Using terminology from [Physically Based Rendering: From Theory to Implementation](https://pbr-book.org/3ed-2018/Reflection_Models) (commonly known as PBRT), BSDF's can generally be a combination of BRDF's (Reflectance Models, i.e. Diffuse and Specular) and BTDF's (Transmission Models, how light passes through translucent materials). The "Glass" Material below is an example of a BSDF (Specular Reflection BSDF + Transmission BTDF), and is based off of `FresnelSpecular` which is described by PBRT. + +#### Perfect Diffuse BRDF & Perfect Specular BRDF + +

+ + +

+ +#### Glass (Specular Reflection BRDF + Specular Transmission BTDF, mixed by fresnel) + +

+ + +

+ +

+Follow Freeman! +

+ +#### Brushed Metal (Torrance-Sparrow Microfacet BRDF Model with a Trowbridge-Reitz Distribution) + +> ๐Ÿ—จ๏ธ ***Comment:*** Seems to work okay with the implicit shapes, but when applied to meshes with normals, it seems to come with weird artifacts. As far as I know I pretty much ripped it straight out of `PBRTv3`, so I'm not sure where I went wrong! + +

+ +

+ +### Depth of Field + +I found [this blog article](https://pathtracing.home.blog/depth-of-field/) which was used for reference and implementation. Producing depth of field (in layman's terms, ['bokeh' in a photograph](https://www.dropicts.com/how-to-achieve-stunning-bokeh-effect-in-your-photo/)) is done by picking a focal point along a generated ray, and jittering it's origin in the xy directions within an aperture. The focal point is the point along the ray where things will be in focus. Increasing the size of your aperture increases the "blur" effect. + +

+ +

+ +## ๐Ÿ“ Performance Analysis + +### โ” Hypothesis + +In this performance analysis we'll be testing two significant GPGPU optimizations made to the path tracer to boost performance: *Stream Compaction* and *Material Sorting*. It's beneficial to explain some of the code base to understand what these optimizations will do for us. + +```c++ +// sceneStructs.h + +struct PathSegment { + Ray ray; // origin + direction + glm::vec3 color; // accumulated color + int remainingBounces; + bool isFinished; + bool isTerminated; +} +``` + +In the code, the `PathSegment` class represents the path a ray takes at every bounce. Before the first bounce, there are `n == pixel count` number of path segments. + +The `PathSegment` has member attributes that determine whether or not it will continue after each bounce. `bool PathSegment::isFinished` is `true` when the path hit a light source and is ready to contribute to the final image. `bool PathSegment::isTerminated` is `true` when the path has either hit nothing or it never hit a light source. As opposed to *finished* paths, paths that are *terminated* are totally removed via ***Stream Compaction*** and no longer spawn kernels. + +```c++ +// sceneStructs.h + +struct ShadeableIntersection { + int materialID; // index to material data + // ... +} +``` + +Each `PathSegment` has an associated `ShadeableIntersection` which is created at each bounce (by the `computeIntersections` kernel) that contains information about the material at each hit-point of the ray path. + +Due to the one-to-one relationship between these two objects, they share the same index and thus both can (and must) be sorted in the same way. One can sort these objects by the material type for example. + +Sorting by material would be beneficial as it would mean that kernels in the same warp would more likely execute similar code paths and access similar areas of memory when calculating light accumulation. + +This would mean *coherent memory access* and *reduced thread divergence*, and is what is attempted via ***Material Sorting***. + +These optimizations are explained in the next sections. + +#### Stream Compaction + +**Stream Compaction** involves terminating those ray paths at each bounce depth that will not contribute to the eventual final image. Specifically, these are paths that: + +* Ended up intersecting infinity (a.k.a. they did not intersect any object) +* Ended up not hitting a light source. + +The first scenario will never occur in closed scenes, as eventually a ray will always hit geometry. And thus I suspect that stream compaction won't be as effective in closed scenes. We could further decrease it's effectiveness by adding an infinite light such as a "sky light" / "environment light". + +#### Material Sorting + +**Material Sorting** simply sorts both `PathSegments` and `ShadeableIntersections` by their material type. `ShadeableIntersections` store the material information and so it is used as a key in `thrust::sort_by_key`. If there is a considerable number of materials and a high number of non-terminated paths, this could have a considerable performance boost. + +### ๐Ÿงช Method + +To test both optimizations fairly, four test scenes were created and are categorized based on two primary factors: the *geometry complexity* and the *environment type* (open vs. closed): + + +* Light Open Scene (Open Cornell Box with implicit spheres) +* Heavy Open Scene (Open Cornell Box with 5k poly geometry) + +* Light Closed Scene (Closed Cornell Box implicit spheres) +* Heavy Closed Scene (Closed Cornell Box with 5k poly geometry) + +With these scenes, the average application + +> ๐Ÿ“ƒ ***Note:*** I was fairly limited on what I could pick as a "heavy" scene. Using polygonal geometry beyond 10,000 triangles slows the renderer down to a point where any optimization changes appear negligable. This could have been helped by implementing an accelleration structure for polygon meshes such as BVH. + +

+ + +

+ +

+Light Open Scene (left) and Heavy Open Scene (right). Closed versions of these scenes are the same, but contain a fourth wall. +

+ +### ๐Ÿ“Š Results + +#### Stream Compaction + +Overall, it was found that stream compaction had a decent improvement across all scenes, and surprisingly this was also the case for closed scenes as well. + +The most signicant improvements were seen on the heavy scenes in particularly. For the Open Scene (heavy), there 30% decrease in framerate with stream compaction turned on. + +Somewhat unexpectedly, it also appears that the Closed Scene (heavy) experienced a ~11% decrease in framerate. The reason for this, is that even in closed scenes, there are still situations where the PathSegment may terminate early. This is explained and shown in the next section. + +The only scene that didn't experience a performance improvement is Closed Scene (light). It's likely that in this case, the overhead of sorting and removing terminated PathSegment exceeds the computation costs of a small scene that is likely not to have any of it's PathSegments terminated per bounce. + +> ๐Ÿ“ƒ ***Note:*** Milliseconds/Frame represents the average time per frame for the first 120 frames that the application was running. This how ImGui outputs *per-frame* timing data by default. [See Here](https://github.com/ocornut/imgui/discussions/4138). + +

+ +

+ +

+Lower is Better. +

+ +##### Has Advantages For Closed Scenes + +To further understand why stream compaction might be advantageous for closed scenes, the number of unterminated PathSegments were compared for each bounce in a single iteration. In both Open Scene (light) and Open Scene (heavy) we can see a decrease in the number of unterminated PathSegments, and this is expected. + +However, Closed Scene (heavy) was added to illustrate that a very small number of PathSegments (in this case, only one) can be terminated in a closed scene. It was further investigated that on average, Closed Scenes may terminate 1-3 PathSegments per iteration. + +

+ +

+

+Lower is Better. +

+ +In `interactions.cu`, the `scatterRay` device function calculates the BRDF's for each Intersection. + +In some BRDF's, i.e perfectly diffuse, there are cases when the returned BRDF samples and PDF values are either miniscule or `NaN`. In these cases, the path is terminated entirely and the sample isn't considered. This avoids final colors in the final image that may be massively large or small (i.e. a [common result of this are fireflies](https://en.wikipedia.org/wiki/Fireflies_(computer_graphics))). + +```c++ +if (pdf < EPSILON || isnan(pdf)) { + pathSegment.isTerminated = true; + return; +} +``` + +Even though this may happen only 1-3 times per iteration, I suppose those few early terminations accumulate over time and therefore the improvement in performance is significant. + +#### Material Sorting + +It turns out that material sorting was a fairly insignificant optimization. In smaller scenes, it appears that the overhead of performing a sort for each bounce outweigh's the potential SIMT coherency benefits of sorted kernel calls. In the smaller Open Scene (light), Material Sorting increased framerates about 3 times versus having the optimization turned off. -* (TODO) YOUR NAME HERE -* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +For heavier scenes, it does see some performance gain, however the the performance increase (about 7%) was quite small and may be a negligable difference. -### (TODO: Your README) +

+ +

+

+Lower is Better. +

-*DO NOT* leave the README to the last minute! It is a crucial part of the -project, and we will not be able to grade you without a good README. +## References +"duck floaty" (https://skfb.ly/o8Mor) by raholder0909 is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). +"Teapot 2 (draft)" (https://skfb.ly/6VYIo) by MrsM is licensed under Creative Commons Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/). +"Crash Bandicoot Crates" (https://skfb.ly/otww6) by diabeticspoon92 is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). +"Gordon Freeman mosaic" (https://skfb.ly/oCE8R) by Evgeniy P is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/). diff --git a/external/include/OpenImageDenoise/config.h b/external/include/OpenImageDenoise/config.h new file mode 100644 index 00000000..671394cb --- /dev/null +++ b/external/include/OpenImageDenoise/config.h @@ -0,0 +1,81 @@ +// Copyright 2018 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#define OIDN_VERSION_MAJOR 2 +#define OIDN_VERSION_MINOR 3 +#define OIDN_VERSION_PATCH 0 +#define OIDN_VERSION 20300 +#define OIDN_VERSION_STRING "2.3.0" + +/* #undef OIDN_API_NAMESPACE */ +/* #undef OIDN_STATIC_LIB */ + +#if defined(OIDN_API_NAMESPACE) + #define OIDN_API_NAMESPACE_BEGIN namespace { + #define OIDN_API_NAMESPACE_END } + #define OIDN_API_NAMESPACE_USING using namespace ; + #define OIDN_API_EXTERN_C + #define OIDN_NAMESPACE ::oidn + #define OIDN_NAMESPACE_C _oidn + #define OIDN_NAMESPACE_BEGIN namespace { namespace oidn { + #define OIDN_NAMESPACE_END }} +#else + #define OIDN_API_NAMESPACE_BEGIN + #define OIDN_API_NAMESPACE_END + #define OIDN_API_NAMESPACE_USING + #if defined(__cplusplus) + #define OIDN_API_EXTERN_C extern "C" + #else + #define OIDN_API_EXTERN_C + #endif + #define OIDN_NAMESPACE oidn + #define OIDN_NAMESPACE_C oidn + #define OIDN_NAMESPACE_BEGIN namespace oidn { + #define OIDN_NAMESPACE_END } +#endif + +#define OIDN_NAMESPACE_USING using namespace OIDN_NAMESPACE; + +#if defined(OIDN_STATIC_LIB) + #define OIDN_API_IMPORT OIDN_API_EXTERN_C + #define OIDN_API_EXPORT OIDN_API_EXTERN_C +#elif defined(_WIN32) + #define OIDN_API_IMPORT OIDN_API_EXTERN_C __declspec(dllimport) + #define OIDN_API_EXPORT OIDN_API_EXTERN_C __declspec(dllexport) +#else + #define OIDN_API_IMPORT OIDN_API_EXTERN_C + #define OIDN_API_EXPORT OIDN_API_EXTERN_C __attribute__((visibility ("default"))) +#endif + +#if defined(OpenImageDenoise_EXPORTS) + #define OIDN_API OIDN_API_EXPORT +#else + #define OIDN_API OIDN_API_IMPORT +#endif + +#if defined(_WIN32) + #define OIDN_DEPRECATED(msg) __declspec(deprecated(msg)) +#else + #define OIDN_DEPRECATED(msg) __attribute__((deprecated(msg))) +#endif + +#if !defined(OIDN_DEVICE_CPU) + #define OIDN_DEVICE_CPU +#endif +#if !defined(OIDN_DEVICE_SYCL) + #define OIDN_DEVICE_SYCL +#endif +#if !defined(OIDN_DEVICE_CUDA) + #define OIDN_DEVICE_CUDA +#endif +#if !defined(OIDN_DEVICE_HIP) + #define OIDN_DEVICE_HIP +#endif +#if !defined(OIDN_DEVICE_METAL) +/* #undef OIDN_DEVICE_METAL */ +#endif + +#define OIDN_FILTER_RT +#define OIDN_FILTER_RTLIGHTMAP diff --git a/external/include/OpenImageDenoise/oidn.h b/external/include/OpenImageDenoise/oidn.h new file mode 100644 index 00000000..1d184667 --- /dev/null +++ b/external/include/OpenImageDenoise/oidn.h @@ -0,0 +1,517 @@ +// Copyright 2018 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +#include "config.h" + +#if defined(__cplusplus) + #if defined(SYCL_LANGUAGE_VERSION) + #include + #define OIDN_SYCL_HPP + #elif defined(SYCL_FEATURE_SET_FULL) || defined(SYCL_FEATURE_SET_REDUCED) + #define OIDN_SYCL_HPP // not using a SYCL compiler but SYCL headers are included + #else + namespace sycl + { + class device; + class queue; + class event; + } + #endif +#endif + +typedef struct CUstream_st* cudaStream_t; +typedef struct ihipStream_t* hipStream_t; + +#if defined(__OBJC__) + @protocol MTLDevice; + @protocol MTLCommandQueue; + @protocol MTLBuffer; + + typedef id MTLDevice_id; + typedef id MTLCommandQueue_id; + typedef id MTLBuffer_id; +#else + typedef void* MTLDevice_id; + typedef void* MTLCommandQueue_id; + typedef void* MTLBuffer_id; +#endif + +OIDN_API_NAMESPACE_BEGIN + +// ------------------------------------------------------------------------------------------------- +// Physical Device +// ------------------------------------------------------------------------------------------------- + +#define OIDN_UUID_SIZE 16u // size of a universally unique identifier (UUID) of a physical device +#define OIDN_LUID_SIZE 8u // size of a locally unique identifier (LUID) of a physical device + +// Returns the number of supported physical devices. +OIDN_API int oidnGetNumPhysicalDevices(); + +// Gets a boolean parameter of the physical device. +OIDN_API bool oidnGetPhysicalDeviceBool(int physicalDeviceID, const char* name); + +// Gets an integer parameter of the physical device. +OIDN_API int oidnGetPhysicalDeviceInt(int physicalDeviceID, const char* name); + +// Gets an unsigned integer parameter of the physical device. +inline unsigned int oidnGetPhysicalDeviceUInt(int physicalDeviceID, const char* name) +{ + return (unsigned int)oidnGetPhysicalDeviceInt(physicalDeviceID, name); +} + +// Gets a string parameter of the physical device. +OIDN_API const char* oidnGetPhysicalDeviceString(int physicalDeviceID, const char* name); + +// Gets an opaque data parameter of the physical device. +OIDN_API const void* oidnGetPhysicalDeviceData(int physicalDeviceID, const char* name, + size_t* byteSize); + +// ------------------------------------------------------------------------------------------------- +// Device +// ------------------------------------------------------------------------------------------------- + +// Device types +typedef enum +{ + OIDN_DEVICE_TYPE_DEFAULT = 0, // select device automatically + + OIDN_DEVICE_TYPE_CPU = 1, // CPU device + OIDN_DEVICE_TYPE_SYCL = 2, // SYCL device + OIDN_DEVICE_TYPE_CUDA = 3, // CUDA device + OIDN_DEVICE_TYPE_HIP = 4, // HIP device + OIDN_DEVICE_TYPE_METAL = 5, // Metal device +} OIDNDeviceType; + +// Error codes +typedef enum +{ + OIDN_ERROR_NONE = 0, // no error occurred + OIDN_ERROR_UNKNOWN = 1, // an unknown error occurred + OIDN_ERROR_INVALID_ARGUMENT = 2, // an invalid argument was specified + OIDN_ERROR_INVALID_OPERATION = 3, // the operation is not allowed + OIDN_ERROR_OUT_OF_MEMORY = 4, // not enough memory to execute the operation + OIDN_ERROR_UNSUPPORTED_HARDWARE = 5, // the hardware (e.g. CPU) is not supported + OIDN_ERROR_CANCELLED = 6, // the operation was cancelled by the user +} OIDNError; + +// Error callback function +typedef void (*OIDNErrorFunction)(void* userPtr, OIDNError code, const char* message); + +// Device handle +typedef struct OIDNDeviceImpl* OIDNDevice; + +// Returns whether the CPU device is supported. +OIDN_API bool oidnIsCPUDeviceSupported(); + +#if defined(__cplusplus) +// Returns whether the specified SYCL device is supported. +OIDN_API bool oidnIsSYCLDeviceSupported(const sycl::device* device); +#endif + +// Returns whether the specified CUDA device is supported. +OIDN_API bool oidnIsCUDADeviceSupported(int deviceID); + +// Returns whether the specified HIP device is supported. +OIDN_API bool oidnIsHIPDeviceSupported(int deviceID); + +// Returns whether the specified Metal device is supported. +OIDN_API bool oidnIsMetalDeviceSupported(MTLDevice_id device); + +// Creates a device of the specified type. +OIDN_API OIDNDevice oidnNewDevice(OIDNDeviceType type); + +// Creates a device from a physical device specified by its ID (0 to oidnGetNumPhysicalDevices()-1). +OIDN_API OIDNDevice oidnNewDeviceByID(int physicalDeviceID); + +// Creates a device from a physical device specified by its UUID. +OIDN_API OIDNDevice oidnNewDeviceByUUID(const void* uuid); + +// Creates a device from a physical device specified by its LUID. +OIDN_API OIDNDevice oidnNewDeviceByLUID(const void* luid); + +// Creates a device from a physical device specified by its PCI address. +OIDN_API OIDNDevice oidnNewDeviceByPCIAddress(int pciDomain, int pciBus, int pciDevice, + int pciFunction); + +#if defined(__cplusplus) +// Creates a device from the specified list of SYCL queues. +// The queues should belong to different SYCL sub-devices (Xe Stack/Tile) of the same SYCL +// root-device (GPU). +OIDN_API OIDNDevice oidnNewSYCLDevice(const sycl::queue* queues, int numQueues); +#endif + +// Creates a device from the specified pairs of CUDA device IDs and streams (null stream +// corresponds to the default stream). Currently only one device ID/stream is supported. +OIDN_API OIDNDevice oidnNewCUDADevice(const int* deviceIDs, const cudaStream_t* streams, + int numPairs); + +// Creates a device from the specified pairs of HIP device IDs and streams (null stream +// corresponds to the default stream). Currently only one device ID/stream is supported. +OIDN_API OIDNDevice oidnNewHIPDevice(const int* deviceIDs, const hipStream_t* streams, + int numPairs); + +// Creates a device from the specified list of Metal command queues. +// Currently only one queue is supported. +OIDN_API OIDNDevice oidnNewMetalDevice(const MTLCommandQueue_id* commandQueues, int numQueues); + +// Retains the device (increments the reference count). +OIDN_API void oidnRetainDevice(OIDNDevice device); + +// Releases the device (decrements the reference count). +OIDN_API void oidnReleaseDevice(OIDNDevice device); + +// Sets a boolean parameter of the device. +OIDN_API void oidnSetDeviceBool(OIDNDevice device, const char* name, bool value); + +OIDN_DEPRECATED("oidnSetDevice1b is deprecated. Use oidnSetDeviceBool instead.") +inline void oidnSetDevice1b(OIDNDevice device, const char* name, bool value) +{ + oidnSetDeviceBool(device, name, value); +} + +// Sets an integer parameter of the device. +OIDN_API void oidnSetDeviceInt(OIDNDevice device, const char* name, int value); + +OIDN_DEPRECATED("oidnSetDevice1i is deprecated. Use oidnSetDeviceInt instead.") +inline void oidnSetDevice1i(OIDNDevice device, const char* name, int value) +{ + oidnSetDeviceInt(device, name, value); +} + +// Sets an unsigned integer parameter of the device. +inline void oidnSetDeviceUInt(OIDNDevice device, const char* name, unsigned int value) +{ + oidnSetDeviceInt(device, name, (int)value); +} + +// Gets a boolean parameter of the device. +OIDN_API bool oidnGetDeviceBool(OIDNDevice device, const char* name); + +OIDN_DEPRECATED("oidnGetDevice1b is deprecated. Use oidnGetDeviceBool instead.") +inline bool oidnGetDevice1b(OIDNDevice device, const char* name) +{ + return oidnGetDeviceBool(device, name); +} + +// Gets an integer parameter of the device. +OIDN_API int oidnGetDeviceInt(OIDNDevice device, const char* name); + +// Gets an unsigned integer parameter of the device. +inline unsigned int oidnGetDeviceUInt(OIDNDevice device, const char* name) +{ + return (unsigned int)oidnGetDeviceInt(device, name); +} + +OIDN_DEPRECATED("oidnGetDevice1i is deprecated. Use oidnGetDeviceInt instead.") +inline int oidnGetDevice1i(OIDNDevice device, const char* name) +{ + return oidnGetDeviceInt(device, name); +} + +// Sets the error callback function of the device. +OIDN_API void oidnSetDeviceErrorFunction(OIDNDevice device, OIDNErrorFunction func, void* userPtr); + +// Returns the first unqueried error code stored in the device for the current thread, optionally +// also returning a string message (if not NULL), and clears the stored error. Can be called with +// a NULL device as well to check for per-thread global errors (e.g. why a device creation or +// physical device query has failed). +OIDN_API OIDNError oidnGetDeviceError(OIDNDevice device, const char** outMessage); + +// Commits all previous changes to the device. +// Must be called before first using the device (e.g. creating filters). +OIDN_API void oidnCommitDevice(OIDNDevice device); + +// Waits for all asynchronous operations running on the device to complete. +OIDN_API void oidnSyncDevice(OIDNDevice device); + +// ------------------------------------------------------------------------------------------------- +// Buffer +// ------------------------------------------------------------------------------------------------- + +// Formats for images and other data stored in buffers +typedef enum +{ + OIDN_FORMAT_UNDEFINED = 0, + + // 32-bit single-precision floating-point scalar and vector formats + OIDN_FORMAT_FLOAT = 1, + OIDN_FORMAT_FLOAT2, + OIDN_FORMAT_FLOAT3, + OIDN_FORMAT_FLOAT4, + + // 16-bit half-precision floating-point scalar and vector formats + OIDN_FORMAT_HALF = 257, + OIDN_FORMAT_HALF2, + OIDN_FORMAT_HALF3, + OIDN_FORMAT_HALF4, +} OIDNFormat; + +// Storage modes for buffers +typedef enum +{ + OIDN_STORAGE_UNDEFINED = 0, + + // stored on the host, accessible by both host and device + OIDN_STORAGE_HOST = 1, + + // stored on the device, *not* accessible by the host + OIDN_STORAGE_DEVICE = 2, + + // automatically migrated between host and device, accessible by both + // *not* supported by all devices, "managedMemorySupported" device parameter should be checked + OIDN_STORAGE_MANAGED = 3, +} OIDNStorage; + +// External memory type flags +typedef enum +{ + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_NONE = 0, + + // opaque POSIX file descriptor handle + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_OPAQUE_FD = 1 << 0, + + // file descriptor handle for a Linux dma_buf + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_DMA_BUF = 1 << 1, + + // NT handle + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_OPAQUE_WIN32 = 1 << 2, + + // global share (KMT) handle + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_OPAQUE_WIN32_KMT = 1 << 3, + + // NT handle returned by IDXGIResource1::CreateSharedHandle referring to a Direct3D 11 texture + // resource + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D11_TEXTURE = 1 << 4, + + // global share (KMT) handle returned by IDXGIResource::GetSharedHandle referring to a Direct3D 11 + // texture resource + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D11_TEXTURE_KMT = 1 << 5, + + // NT handle returned by IDXGIResource1::CreateSharedHandle referring to a Direct3D 11 resource + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D11_RESOURCE = 1 << 6, + + // global share (KMT) handle returned by IDXGIResource::GetSharedHandle referring to a Direct3D 11 + // resource + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D11_RESOURCE_KMT = 1 << 7, + + // NT handle returned by ID3D12Device::CreateSharedHandle referring to a Direct3D 12 heap + // resource + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D12_HEAP = 1 << 8, + + // NT handle returned by ID3D12Device::CreateSharedHandle referring to a Direct3D 12 committed + // resource + OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D12_RESOURCE = 1 << 9, +} OIDNExternalMemoryTypeFlag; + +// Buffer handle +typedef struct OIDNBufferImpl* OIDNBuffer; + +// Creates a buffer accessible to both the host and device. +OIDN_API OIDNBuffer oidnNewBuffer(OIDNDevice device, size_t byteSize); + +// Creates a buffer with the specified storage mode. +OIDN_API OIDNBuffer oidnNewBufferWithStorage(OIDNDevice device, size_t byteSize, OIDNStorage storage); + +// Creates a shared buffer from memory allocated and owned by the user and accessible to the device. +OIDN_API OIDNBuffer oidnNewSharedBuffer(OIDNDevice device, void* devPtr, size_t byteSize); + +// Creates a shared buffer by importing external memory from a POSIX file descriptor. +OIDN_API OIDNBuffer oidnNewSharedBufferFromFD(OIDNDevice device, + OIDNExternalMemoryTypeFlag fdType, + int fd, size_t byteSize); + +// Creates a shared buffer by importing external memory from a Win32 handle. +OIDN_API OIDNBuffer oidnNewSharedBufferFromWin32Handle(OIDNDevice device, + OIDNExternalMemoryTypeFlag handleType, + void* handle, const void* name, size_t byteSize); + +// Creates a shared buffer from a Metal buffer. +// Only buffers with shared or private storage and hazard tracking are supported. +OIDN_API OIDNBuffer oidnNewSharedBufferFromMetal(OIDNDevice device, MTLBuffer_id buffer); + +// Gets the size of the buffer in bytes. +OIDN_API size_t oidnGetBufferSize(OIDNBuffer buffer); + +// Gets the storage mode of the buffer. +OIDN_API OIDNStorage oidnGetBufferStorage(OIDNBuffer buffer); + +// Gets a pointer to the buffer data, which is accessible to the device but not necessarily to +// the host as well, depending on the storage mode. Null pointer may be returned if the buffer +// is empty or getting a pointer to data with device storage is not supported by the device. +OIDN_API void* oidnGetBufferData(OIDNBuffer buffer); + +// Copies data from a region of the buffer to host memory. +OIDN_API void oidnReadBuffer(OIDNBuffer buffer, size_t byteOffset, size_t byteSize, void* dstHostPtr); + +// Copies data from a region of the buffer to host memory asynchronously. +OIDN_API void oidnReadBufferAsync(OIDNBuffer buffer, + size_t byteOffset, size_t byteSize, void* dstHostPtr); + +// Copies data to a region of the buffer from host memory. +OIDN_API void oidnWriteBuffer(OIDNBuffer buffer, + size_t byteOffset, size_t byteSize, const void* srcHostPtr); + +// Copies data to a region of the buffer from host memory asynchronously. +OIDN_API void oidnWriteBufferAsync(OIDNBuffer buffer, + size_t byteOffset, size_t byteSize, const void* srcHostPtr); + +// Retains the buffer (increments the reference count). +OIDN_API void oidnRetainBuffer(OIDNBuffer buffer); + +// Releases the buffer (decrements the reference count). +OIDN_API void oidnReleaseBuffer(OIDNBuffer buffer); + +// ------------------------------------------------------------------------------------------------- +// Filter +// ------------------------------------------------------------------------------------------------- + +// Filter quality/performance modes +typedef enum +{ + OIDN_QUALITY_DEFAULT = 0, // default quality + + OIDN_QUALITY_FAST = 4, // high performance (for interactive/real-time preview rendering) + OIDN_QUALITY_BALANCED = 5, // balanced quality/performance (for interactive/real-time rendering) + OIDN_QUALITY_HIGH = 6, // high quality (for final-frame rendering) +} OIDNQuality; + +// Progress monitor callback function +typedef bool (*OIDNProgressMonitorFunction)(void* userPtr, double n); + +// Filter handle +typedef struct OIDNFilterImpl* OIDNFilter; + +// Creates a filter of the specified type (e.g. "RT"). +OIDN_API OIDNFilter oidnNewFilter(OIDNDevice device, const char* type); + +// Retains the filter (increments the reference count). +OIDN_API void oidnRetainFilter(OIDNFilter filter); + +// Releases the filter (decrements the reference count). +OIDN_API void oidnReleaseFilter(OIDNFilter filter); + +// Sets an image parameter of the filter with data stored in a buffer. +// If pixelByteStride and/or rowByteStride are zero, these will be computed automatically. +OIDN_API void oidnSetFilterImage(OIDNFilter filter, const char* name, + OIDNBuffer buffer, OIDNFormat format, + size_t width, size_t height, + size_t byteOffset, + size_t pixelByteStride, size_t rowByteStride); + +// Sets an image parameter of the filter with data owned by the user and accessible to the device. +// If pixelByteStride and/or rowByteStride are zero, these will be computed automatically. +OIDN_API void oidnSetSharedFilterImage(OIDNFilter filter, const char* name, + void* devPtr, OIDNFormat format, + size_t width, size_t height, + size_t byteOffset, + size_t pixelByteStride, size_t rowByteStride); + +// Unsets an image parameter of the filter that was previously set. +OIDN_API void oidnUnsetFilterImage(OIDNFilter filter, const char* name); + +OIDN_DEPRECATED("oidnRemoveFilterImage is deprecated. Use oidnUnsetFilterImage instead.") +inline void oidnRemoveFilterImage(OIDNFilter filter, const char* name) +{ + oidnUnsetFilterImage(filter, name); +} + +// Sets an opaque data parameter of the filter owned by the user and accessible to the host. +OIDN_API void oidnSetSharedFilterData(OIDNFilter filter, const char* name, + void* hostPtr, size_t byteSize); + +// Notifies the filter that the contents of an opaque data parameter has been changed. +OIDN_API void oidnUpdateFilterData(OIDNFilter filter, const char* name); + +// Unsets an opaque data parameter of the filter that was previously set. +OIDN_API void oidnUnsetFilterData(OIDNFilter filter, const char* name); + +OIDN_DEPRECATED("oidnRemoveFilterData is deprecated. Use oidnUnsetFilterData instead.") +inline void oidnRemoveFilterData(OIDNFilter filter, const char* name) +{ + oidnUnsetFilterData(filter, name); +} + +// Sets a boolean parameter of the filter. +OIDN_API void oidnSetFilterBool(OIDNFilter filter, const char* name, bool value); + +OIDN_DEPRECATED("oidnSetFilter1b is deprecated. Use oidnSetFilterBool instead.") +inline void oidnSetFilter1b(OIDNFilter filter, const char* name, bool value) +{ + oidnSetFilterBool(filter, name, value); +} + +// Gets a boolean parameter of the filter. +OIDN_API bool oidnGetFilterBool(OIDNFilter filter, const char* name); + +OIDN_DEPRECATED("oidnGetFilter1b is deprecated. Use oidnGetFilterBool instead.") +inline bool oidnGetFilter1b(OIDNFilter filter, const char* name) +{ + return oidnGetFilterBool(filter, name); +} + +// Sets an integer parameter of the filter. +OIDN_API void oidnSetFilterInt(OIDNFilter filter, const char* name, int value); + +OIDN_DEPRECATED("oidnSetFilter1i is deprecated. Use oidnSetFilterInt instead.") +inline void oidnSetFilter1i(OIDNFilter filter, const char* name, int value) +{ + oidnSetFilterInt(filter, name, value); +} + +// Gets an integer parameter of the filter. +OIDN_API int oidnGetFilterInt(OIDNFilter filter, const char* name); + +OIDN_DEPRECATED("oidnGetFilter1i is deprecated. Use oidnGetFilterInt instead.") +inline int oidnGetFilter1i(OIDNFilter filter, const char* name) +{ + return oidnGetFilterInt(filter, name); +} + +// Sets a float parameter of the filter. +OIDN_API void oidnSetFilterFloat(OIDNFilter filter, const char* name, float value); + +OIDN_DEPRECATED("oidnSetFilter1f is deprecated. Use oidnSetFilterFloat instead.") +inline void oidnSetFilter1f(OIDNFilter filter, const char* name, float value) +{ + oidnSetFilterFloat(filter, name, value); +} + +// Gets a float parameter of the filter. +OIDN_API float oidnGetFilterFloat(OIDNFilter filter, const char* name); + +OIDN_DEPRECATED("oidnGetFilter1f is deprecated. Use oidnGetFilterFloat instead.") +inline float oidnGetFilter1f(OIDNFilter filter, const char* name) +{ + return oidnGetFilterFloat(filter, name); +} + +// Sets the progress monitor callback function of the filter. +OIDN_API void oidnSetFilterProgressMonitorFunction(OIDNFilter filter, + OIDNProgressMonitorFunction func, void* userPtr); + +// Commits all previous changes to the filter. +// Must be called before first executing the filter. +OIDN_API void oidnCommitFilter(OIDNFilter filter); + +// Executes the filter. +OIDN_API void oidnExecuteFilter(OIDNFilter filter); + +// Executes the filter asynchronously. +OIDN_API void oidnExecuteFilterAsync(OIDNFilter filter); + +#if defined(__cplusplus) +// Executes the filter of a SYCL device using the specified dependent events asynchronously, and +// optionally returns an event for completion. +OIDN_API void oidnExecuteSYCLFilterAsync(OIDNFilter filter, + const sycl::event* depEvents, int numDepEvents, + sycl::event* doneEvent); +#endif + +OIDN_API_NAMESPACE_END diff --git a/external/include/OpenImageDenoise/oidn.hpp b/external/include/OpenImageDenoise/oidn.hpp new file mode 100644 index 00000000..4588d421 --- /dev/null +++ b/external/include/OpenImageDenoise/oidn.hpp @@ -0,0 +1,1082 @@ +// Copyright 2018 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "oidn.h" +#include +#include +#include +#include +#include +#include +#include +#include + +OIDN_NAMESPACE_BEGIN + + // ----------------------------------------------------------------------------------------------- + // Flags helper type + // ----------------------------------------------------------------------------------------------- + + template + struct IsFlag + { + static constexpr bool value = false; + }; + + template + class Flags + { + public: + static_assert(IsFlag::value, "not a flag type"); + + using MaskType = typename std::underlying_type::type; + + constexpr Flags() noexcept : mask(0) {} + constexpr Flags(FlagT flag) noexcept : mask(static_cast(flag)) {} + constexpr Flags(const Flags& b) noexcept = default; + constexpr explicit Flags(MaskType mask) noexcept : mask(mask) {} + + constexpr bool operator !() const noexcept { return !mask; } + + constexpr Flags operator &(const Flags& b) const noexcept { return Flags(mask & b.mask); } + constexpr Flags operator |(const Flags& b) const noexcept { return Flags(mask | b.mask); } + constexpr Flags operator ^(const Flags& b) const noexcept { return Flags(mask ^ b.mask); } + + Flags& operator =(const Flags& b) noexcept = default; + + Flags& operator &=(const Flags& b) noexcept + { + mask &= b.mask; + return *this; + } + + Flags& operator |=(const Flags& b) noexcept + { + mask |= b.mask; + return *this; + } + + Flags& operator ^=(const Flags& b) noexcept + { + mask ^= b.mask; + return *this; + } + + constexpr bool operator ==(const Flags& b) const noexcept { return mask == b.mask; } + constexpr bool operator !=(const Flags& b) const noexcept { return mask != b.mask; } + + constexpr explicit operator bool() const noexcept { return mask; } + constexpr explicit operator MaskType() const noexcept { return mask; } + + private: + MaskType mask; + }; + + template + inline constexpr Flags operator &(FlagT a, const Flags& b) noexcept + { + return Flags(a) & b; + } + + template + inline constexpr Flags operator |(FlagT a, const Flags& b) noexcept + { + return Flags(a) | b; + } + + template + inline constexpr Flags operator ^(FlagT a, const Flags& b) noexcept + { + return Flags(a) ^ b; + } + + template::value, bool>::type = true> + inline constexpr Flags operator &(FlagT a, FlagT b) noexcept + { + return Flags(a) & b; + } + + template::value, bool>::type = true> + inline constexpr Flags operator |(FlagT a, FlagT b) noexcept + { + return Flags(a) | b; + } + + template::value, bool>::type = true> + inline constexpr Flags operator ^(FlagT a, FlagT b) noexcept + { + return Flags(a) ^ b; + } + + // ----------------------------------------------------------------------------------------------- + // Buffer + // ----------------------------------------------------------------------------------------------- + + // Formats for images and other data stored in buffers + enum class Format + { + Undefined = OIDN_FORMAT_UNDEFINED, + + // 32-bit single-precision floating-point scalar and vector formats + Float = OIDN_FORMAT_FLOAT, + Float2 = OIDN_FORMAT_FLOAT2, + Float3 = OIDN_FORMAT_FLOAT3, + Float4 = OIDN_FORMAT_FLOAT4, + + // 16-bit half-precision floating-point scalar and vector formats + Half = OIDN_FORMAT_HALF, + Half2 = OIDN_FORMAT_HALF2, + Half3 = OIDN_FORMAT_HALF3, + Half4 = OIDN_FORMAT_HALF4, + }; + + // Storage modes for buffers + enum class Storage + { + Undefined = OIDN_STORAGE_UNDEFINED, + + // stored on the host, accessible by both host and device + Host = OIDN_STORAGE_HOST, + + // stored on the device, *not* accessible by the host + Device = OIDN_STORAGE_DEVICE, + + // automatically migrated between host and device, accessible by both + // *not* supported by all devices, "managedMemorySupported" device parameter should be checked + Managed = OIDN_STORAGE_MANAGED, + }; + + // External memory type flags + enum class ExternalMemoryTypeFlag + { + None = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_NONE, + + // opaque POSIX file descriptor handle + OpaqueFD = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_OPAQUE_FD, + + // file descriptor handle for a Linux dma_buf + DMABuf = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_DMA_BUF, + + // NT handle + OpaqueWin32 = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_OPAQUE_WIN32, + + // global share (KMT) handle + OpaqueWin32KMT = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_OPAQUE_WIN32_KMT, + + // NT handle returned by IDXGIResource1::CreateSharedHandle referring to a Direct3D 11 + // texture resource + D3D11Texture = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D11_TEXTURE, + + // global share (KMT) handle returned by IDXGIResource::GetSharedHandle referring to a + // Direct3D 11 texture resource + D3D11TextureKMT = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D11_TEXTURE_KMT, + + // NT handle returned by IDXGIResource1::CreateSharedHandle referring to a Direct3D 11 + // resource + D3D11Resource = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D11_RESOURCE, + + // global share (KMT) handle returned by IDXGIResource::GetSharedHandle referring to a + // Direct3D 11 resource + D3D11ResourceKMT = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D11_RESOURCE_KMT, + + // NT handle returned by ID3D12Device::CreateSharedHandle referring to a Direct3D 12 + // heap resource + D3D12Heap = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D12_HEAP, + + // NT handle returned by ID3D12Device::CreateSharedHandle referring to a Direct3D 12 + // committed resource + D3D12Resource = OIDN_EXTERNAL_MEMORY_TYPE_FLAG_D3D12_RESOURCE, + }; + + template<> struct IsFlag { static constexpr bool value = true; }; + using ExternalMemoryTypeFlags = Flags; + + // Buffer object with automatic reference counting + class BufferRef + { + public: + BufferRef() : handle(nullptr) {} + BufferRef(OIDNBuffer handle) : handle(handle) {} + + BufferRef(const BufferRef& other) : handle(other.handle) + { + if (handle) + oidnRetainBuffer(handle); + } + + BufferRef(BufferRef&& other) noexcept : handle(other.handle) + { + other.handle = nullptr; + } + + BufferRef& operator =(const BufferRef& other) + { + if (&other != this) + { + if (other.handle) + oidnRetainBuffer(other.handle); + if (handle) + oidnReleaseBuffer(handle); + handle = other.handle; + } + return *this; + } + + BufferRef& operator =(BufferRef&& other) noexcept + { + std::swap(handle, other.handle); + return *this; + } + + BufferRef& operator =(OIDNBuffer other) + { + if (other) + oidnRetainBuffer(other); + if (handle) + oidnReleaseBuffer(handle); + handle = other; + return *this; + } + + ~BufferRef() + { + if (handle) + oidnReleaseBuffer(handle); + } + + OIDNBuffer getHandle() const + { + return handle; + } + + operator bool() const + { + return handle != nullptr; + } + + // Releases the buffer (decrements the reference count). + void release() + { + if (handle) + { + oidnReleaseBuffer(handle); + handle = nullptr; + } + } + + // Gets the size of the buffer in bytes. + size_t getSize() const + { + return oidnGetBufferSize(handle); + } + + // Gets the storage mode of the buffer. + Storage getStorage() const + { + return static_cast(oidnGetBufferStorage(handle)); + } + + // Gets a pointer to the buffer data, which is accessible to the device but not necessarily to + // the host as well, depending on the storage mode. Null pointer may be returned if the buffer + // is empty or getting a pointer to data with device storage is not supported by the device. + void* getData() const + { + return oidnGetBufferData(handle); + } + + // Copies data from a region of the buffer to host memory. + void read(size_t byteOffset, size_t byteSize, void* dstHostPtr) const + { + oidnReadBuffer(handle, byteOffset, byteSize, dstHostPtr); + } + + // Copies data from a region of the buffer to host memory asynchronously. + void readAsync(size_t byteOffset, size_t byteSize, void* dstHostPtr) const + { + oidnReadBufferAsync(handle, byteOffset, byteSize, dstHostPtr); + } + + // Copies data to a region of the buffer from host memory. + void write(size_t byteOffset, size_t byteSize, const void* srcHostPtr) + { + oidnWriteBuffer(handle, byteOffset, byteSize, srcHostPtr); + } + + // Copies data to a region of the buffer from host memory asynchronously. + void writeAsync(size_t byteOffset, size_t byteSize, const void* srcHostPtr) + { + oidnWriteBufferAsync(handle, byteOffset, byteSize, srcHostPtr); + } + + private: + OIDNBuffer handle; + }; + + // ----------------------------------------------------------------------------------------------- + // Filter + // ----------------------------------------------------------------------------------------------- + + // Filter quality/performance modes + enum class Quality + { + Default = OIDN_QUALITY_DEFAULT, // default quality + + Fast = OIDN_QUALITY_FAST, // high performance (for interactive/real-time preview rendering) + Balanced = OIDN_QUALITY_BALANCED, // balanced quality/performance (for interactive/real-time rendering) + High = OIDN_QUALITY_HIGH, // high quality (for final-frame rendering) + }; + + // Progress monitor callback function + using ProgressMonitorFunction = OIDNProgressMonitorFunction; + + // Filter object with automatic reference counting + class FilterRef + { + public: + FilterRef() : handle(nullptr) {} + FilterRef(OIDNFilter handle) : handle(handle) {} + + FilterRef(const FilterRef& other) : handle(other.handle) + { + if (handle) + oidnRetainFilter(handle); + } + + FilterRef(FilterRef&& other) noexcept : handle(other.handle) + { + other.handle = nullptr; + } + + FilterRef& operator =(const FilterRef& other) + { + if (&other != this) + { + if (other.handle) + oidnRetainFilter(other.handle); + if (handle) + oidnReleaseFilter(handle); + handle = other.handle; + } + return *this; + } + + FilterRef& operator =(FilterRef&& other) noexcept + { + std::swap(handle, other.handle); + return *this; + } + + FilterRef& operator =(OIDNFilter other) + { + if (other) + oidnRetainFilter(other); + if (handle) + oidnReleaseFilter(handle); + handle = other; + return *this; + } + + ~FilterRef() + { + if (handle) + oidnReleaseFilter(handle); + } + + OIDNFilter getHandle() const + { + return handle; + } + + operator bool() const + { + return handle != nullptr; + } + + // Releases the filter (decrements the reference count). + void release() + { + if (handle) + { + oidnReleaseFilter(handle); + handle = nullptr; + } + } + + // Sets an image parameter of the filter with data stored in a buffer. + void setImage(const char* name, + const BufferRef& buffer, Format format, + size_t width, size_t height, + size_t byteOffset = 0, + size_t pixelByteStride = 0, size_t rowByteStride = 0) + { + oidnSetFilterImage(handle, name, + buffer.getHandle(), static_cast(format), + width, height, + byteOffset, + pixelByteStride, rowByteStride); + } + + // Sets an image parameter of the filter with data owned by the user and accessible to the device. + void setImage(const char* name, + void* devPtr, Format format, + size_t width, size_t height, + size_t byteOffset = 0, + size_t pixelByteStride = 0, size_t rowByteStride = 0) + { + oidnSetSharedFilterImage(handle, name, + devPtr, static_cast(format), + width, height, + byteOffset, + pixelByteStride, rowByteStride); + } + + // Unsets an image parameter of the filter that was previously set. + void unsetImage(const char* name) + { + oidnUnsetFilterImage(handle, name); + } + + OIDN_DEPRECATED("removeImage is deprecated. Use unsetImage instead.") + void removeImage(const char* name) + { + oidnUnsetFilterImage(handle, name); + } + + // Sets an opaque data parameter of the filter owned by the user and accessible to the host. + void setData(const char* name, void* hostPtr, size_t byteSize) + { + oidnSetSharedFilterData(handle, name, hostPtr, byteSize); + } + + // Notifies the filter that the contents of an opaque data parameter has been changed. + void updateData(const char* name) + { + oidnUpdateFilterData(handle, name); + } + + // Unsets an opaque data parameter of the filter that was previously set. + void unsetData(const char* name) + { + oidnUnsetFilterData(handle, name); + } + + OIDN_DEPRECATED("removeData is deprecated. Use unsetData instead.") + void removeData(const char* name) + { + oidnUnsetFilterData(handle, name); + } + + // Sets a boolean parameter of the filter. + void set(const char* name, bool value) + { + oidnSetFilterBool(handle, name, value); + } + + // Sets an integer parameter of the filter. + void set(const char* name, int value) + { + oidnSetFilterInt(handle, name, value); + } + + void set(const char* name, Quality value) + { + oidnSetFilterInt(handle, name, static_cast(value)); + } + + // Sets a float parameter of the filter. + void set(const char* name, float value) + { + oidnSetFilterFloat(handle, name, value); + } + + // Gets a parameter of the filter. + template + T get(const char* name) const; + + // Sets the progress monitor callback function of the filter. + void setProgressMonitorFunction(ProgressMonitorFunction func, void* userPtr = nullptr) + { + oidnSetFilterProgressMonitorFunction(handle, func, userPtr); + } + + // Commits all previous changes to the filter. + void commit() + { + oidnCommitFilter(handle); + } + + // Executes the filter. + void execute() + { + oidnExecuteFilter(handle); + } + + // Executes the filter asynchronously. + void executeAsync() + { + oidnExecuteFilterAsync(handle); + } + + #if defined(OIDN_SYCL_HPP) + // Executes the filter of a SYCL device using the specified dependent events asynchronously, and + // optionally returns an event for completion. + sycl::event executeAsync(const std::vector& depEvents) + { + sycl::event doneEvent; + oidnExecuteSYCLFilterAsync(handle, depEvents.data(), static_cast(depEvents.size()), &doneEvent); + return doneEvent; + } + #endif + + private: + OIDNFilter handle; + }; + + template<> + inline bool FilterRef::get(const char* name) const + { + return oidnGetFilterBool(handle, name); + } + + template<> + inline int FilterRef::get(const char* name) const + { + return oidnGetFilterInt(handle, name); + } + + template<> + inline Quality FilterRef::get(const char* name) const + { + return static_cast(oidnGetFilterInt(handle, name)); + } + + template<> + inline float FilterRef::get(const char* name) const + { + return oidnGetFilterFloat(handle, name); + } + + // ----------------------------------------------------------------------------------------------- + // Device + // ----------------------------------------------------------------------------------------------- + + // Device types + enum class DeviceType + { + Default = OIDN_DEVICE_TYPE_DEFAULT, // select device automatically + + CPU = OIDN_DEVICE_TYPE_CPU, // CPU device + SYCL = OIDN_DEVICE_TYPE_SYCL, // SYCL device + CUDA = OIDN_DEVICE_TYPE_CUDA, // CUDA device + HIP = OIDN_DEVICE_TYPE_HIP, // HIP device + Metal = OIDN_DEVICE_TYPE_METAL, // Metal device + }; + + // Error codes + enum class Error + { + None = OIDN_ERROR_NONE, // no error occurred + Unknown = OIDN_ERROR_UNKNOWN, // an unknown error occurred + InvalidArgument = OIDN_ERROR_INVALID_ARGUMENT, // an invalid argument was specified + InvalidOperation = OIDN_ERROR_INVALID_OPERATION, // the operation is not allowed + OutOfMemory = OIDN_ERROR_OUT_OF_MEMORY, // not enough memory to execute the operation + UnsupportedHardware = OIDN_ERROR_UNSUPPORTED_HARDWARE, // the hardware (e.g. CPU) is not supported + Cancelled = OIDN_ERROR_CANCELLED, // the operation was cancelled by the user + }; + + // Error callback function + typedef void (*ErrorFunction)(void* userPtr, Error code, const char* message); + + // Opaque universally unique identifier (UUID) of a physical device + struct UUID + { + uint8_t bytes[OIDN_UUID_SIZE]; + }; + + // Opaque locally unique identifier (LUID) of a physical device + struct LUID + { + union + { + struct + { + uint32_t low; + int32_t high; + }; + uint8_t bytes[OIDN_LUID_SIZE]; + }; + }; + + // Device object with automatic reference counting + class DeviceRef + { + public: + DeviceRef() : handle(nullptr) {} + DeviceRef(OIDNDevice handle) : handle(handle) {} + + DeviceRef(const DeviceRef& other) : handle(other.handle) + { + if (handle) + oidnRetainDevice(handle); + } + + DeviceRef(DeviceRef&& other) noexcept : handle(other.handle) + { + other.handle = nullptr; + } + + DeviceRef& operator =(const DeviceRef& other) + { + if (&other != this) + { + if (other.handle) + oidnRetainDevice(other.handle); + if (handle) + oidnReleaseDevice(handle); + handle = other.handle; + } + return *this; + } + + DeviceRef& operator =(DeviceRef&& other) noexcept + { + std::swap(handle, other.handle); + return *this; + } + + DeviceRef& operator =(OIDNDevice other) + { + if (other) + oidnRetainDevice(other); + if (handle) + oidnReleaseDevice(handle); + handle = other; + return *this; + } + + ~DeviceRef() + { + if (handle) + oidnReleaseDevice(handle); + } + + OIDNDevice getHandle() const + { + return handle; + } + + operator bool() const + { + return handle != nullptr; + } + + // Releases the device (decrements the reference count). + void release() + { + if (handle) + { + oidnReleaseDevice(handle); + handle = nullptr; + } + } + + // Sets a boolean parameter of the device. + void set(const char* name, bool value) + { + oidnSetDeviceBool(handle, name, value); + } + + // Sets an integer parameter of the device. + void set(const char* name, int value) + { + oidnSetDeviceInt(handle, name, value); + } + + // Sets an unsigned integer parameter of the device. + void set(const char* name, unsigned int value) + { + oidnSetDeviceUInt(handle, name, value); + } + + // Gets a parameter of the device. + template + T get(const char* name) const; + + // Sets the error callback function of the device. + void setErrorFunction(ErrorFunction func, void* userPtr = nullptr) + { + oidnSetDeviceErrorFunction(handle, reinterpret_cast(func), userPtr); + } + + // Returns the first unqueried error code and clears the stored error. + // Can be called for a null device as well to check for global errors (e.g. why a device + // creation or physical device query has failed. + Error getError() + { + return static_cast(oidnGetDeviceError(handle, nullptr)); + } + + // Returns the first unqueried error code and string message, and clears the stored error. + // Can be called for a null device as well to check why a device creation failed. + Error getError(const char*& outMessage) + { + return static_cast(oidnGetDeviceError(handle, &outMessage)); + } + + // Commits all previous changes to the device. + // Must be called before first using the device (e.g. creating filters). + void commit() + { + oidnCommitDevice(handle); + } + + // Waits for all asynchronous operations running on the device to complete. + void sync() + { + oidnSyncDevice(handle); + } + + // Creates a buffer accessible to both the host and device. + BufferRef newBuffer(size_t byteSize) const + { + return oidnNewBuffer(handle, byteSize); + } + + // Creates a buffer with the specified storage mode. + BufferRef newBuffer(size_t byteSize, Storage storage) const + { + return oidnNewBufferWithStorage(handle, byteSize, static_cast(storage)); + } + + // Creates a shared buffer from memory allocated and owned by the user and accessible to the + // device. + BufferRef newBuffer(void* ptr, size_t byteSize) const + { + return oidnNewSharedBuffer(handle, ptr, byteSize); + } + + // Creates a shared buffer by importing external memory from a POSIX file descriptor. + BufferRef newBuffer(ExternalMemoryTypeFlag fdType, int fd, size_t byteSize) const + { + return oidnNewSharedBufferFromFD( + handle, static_cast(fdType), fd, byteSize); + } + + // Creates a shared buffer by importing external memory from a Win32 handle. + BufferRef newBuffer(ExternalMemoryTypeFlag handleType, void* handle, const void* name, size_t byteSize) const + { + return oidnNewSharedBufferFromWin32Handle( + this->handle, static_cast(handleType), handle, name, byteSize); + } + + // Creates a shared buffer from a Metal buffer. + // Only buffers with shared or private storage and hazard tracking are supported. + #if defined(__OBJC__) + BufferRef newBuffer(id buffer) const + { + return oidnNewSharedBufferFromMetal(handle, buffer); + } + #endif + + // Creates a filter of the specified type (e.g. "RT"). + FilterRef newFilter(const char* type) const + { + return oidnNewFilter(handle, type); + } + + private: + OIDNDevice handle; + }; + + template<> + inline bool DeviceRef::get(const char* name) const + { + return oidnGetDeviceBool(handle, name); + } + + template<> + inline int DeviceRef::get(const char* name) const + { + return oidnGetDeviceInt(handle, name); + } + + template<> + inline unsigned int DeviceRef::get(const char* name) const + { + return oidnGetDeviceUInt(handle, name); + } + + template<> + inline DeviceType DeviceRef::get(const char* name) const + { + return static_cast(oidnGetDeviceInt(handle, name)); + } + + template<> + inline ExternalMemoryTypeFlags DeviceRef::get(const char* name) const + { + return ExternalMemoryTypeFlags(oidnGetDeviceInt(handle, name)); + } + + // Returns the first unqueried per-thread global error code and clears the stored error. + inline Error getError() + { + return static_cast(oidnGetDeviceError(nullptr, nullptr)); + } + + // Returns the first unqueried per-thread global error code and string message, and clears the + // stored error. + inline Error getError(const char*& outMessage) + { + return static_cast(oidnGetDeviceError(nullptr, &outMessage)); + } + + // Returns whether the CPU device is supported. + inline bool isCPUDeviceSupported() + { + return oidnIsCPUDeviceSupported(); + } + + // Returns whether the specified SYCL device is supported. +#if defined(OIDN_SYCL_HPP) + inline bool isSYCLDeviceSupported(const sycl::device& device) + { + return oidnIsSYCLDeviceSupported(&device); + } +#endif + + // Returns whether the specified CUDA device is supported. + inline bool isCUDADeviceSupported(int deviceID) + { + return oidnIsCUDADeviceSupported(deviceID); + } + + // Returns whether the specified HIP device is supported. + inline bool isHIPDeviceSupported(int deviceID) + { + return oidnIsHIPDeviceSupported(deviceID); + } + + // Returns whether the specified Metal device is supported. + inline bool isMetalDeviceSupported(MTLDevice_id device) + { + return oidnIsMetalDeviceSupported(device); + } + + // Creates a device of the specified type. + inline DeviceRef newDevice(DeviceType type = DeviceType::Default) + { + return DeviceRef(oidnNewDevice(static_cast(type))); + } + + // Creates a device from a physical device specified by its ID (0 to getNumPhysicalDevices()-1). + inline DeviceRef newDevice(int physicalDeviceID) + { + return DeviceRef(oidnNewDeviceByID(physicalDeviceID)); + } + + // Creates a device from a physical device specified by its UUID. + inline DeviceRef newDevice(const UUID& uuid) + { + return DeviceRef(oidnNewDeviceByUUID(uuid.bytes)); + } + + // Creates a device from a physical device specified by its LUID. + inline DeviceRef newDevice(const LUID& luid) + { + return DeviceRef(oidnNewDeviceByLUID(luid.bytes)); + } + + // Creates a device from a physical device specified by its PCI address. + inline DeviceRef newDevice(int pciDomain, int pciBus, int pciDevice, int pciFunction) + { + return DeviceRef(oidnNewDeviceByPCIAddress(pciDomain, pciBus, pciDevice, pciFunction)); + } + +#if defined(OIDN_SYCL_HPP) + // Creates a device from the specified SYCL queue. + inline DeviceRef newSYCLDevice(const sycl::queue& queue) + { + return DeviceRef(oidnNewSYCLDevice(&queue, 1)); + } + + // Creates a device from the specified list of SYCL queues. + // The queues should belong to different SYCL sub-devices (Xe Stack/Tile) of the same SYCL + // root-device (GPU). + inline DeviceRef newSYCLDevice(const std::vector& queues) + { + return DeviceRef(oidnNewSYCLDevice(queues.data(), static_cast(queues.size()))); + } +#endif + + // Creates a device from the specified CUDA device ID and stream (null stream corresponds to the + // default stream). + inline DeviceRef newCUDADevice(int deviceID, cudaStream_t stream) + { + return DeviceRef(oidnNewCUDADevice(&deviceID, &stream, 1)); + } + + // Creates a device from the specified pairs of CUDA device IDs and streams (null stream + // corresponds to the default stream). Currently only one device ID/stream is supported. + inline DeviceRef newCUDADevice(const std::vector& deviceIDs, + const std::vector& streams) + { + assert(deviceIDs.size() == streams.size()); + return DeviceRef(oidnNewCUDADevice(deviceIDs.data(), streams.data(), + static_cast(streams.size()))); + } + + // Creates a device from the specified HIP device ID and stream (null stream corresponds to the + // default stream). + inline DeviceRef newHIPDevice(int deviceID, hipStream_t stream) + { + return DeviceRef(oidnNewHIPDevice(&deviceID, &stream, 1)); + } + + // Creates a device from the specified pairs of HIP device IDs and streams (null stream + // corresponds to the default stream). Currently only one device ID/stream is supported. + inline DeviceRef newHIPDevice(const std::vector& deviceIDs, + const std::vector& streams) + { + assert(deviceIDs.size() == streams.size()); + return DeviceRef(oidnNewHIPDevice(deviceIDs.data(), streams.data(), + static_cast(streams.size()))); + } + + // Creates a device from the specified Metal command queue. + inline DeviceRef newMetalDevice(MTLCommandQueue_id commandQueue) + { + return DeviceRef(oidnNewMetalDevice(&commandQueue, 1)); + } + + // Creates a device from the specified list of Metal command queues. + // Currently only one queue is supported. + inline DeviceRef newMetalDevice(const std::vector& commandQueues) + { + return DeviceRef(oidnNewMetalDevice(commandQueues.data(), static_cast(commandQueues.size()))); + } + + // ----------------------------------------------------------------------------------------------- + // Physical Device + // ----------------------------------------------------------------------------------------------- + + class PhysicalDeviceRef + { + public: + PhysicalDeviceRef() : id(-1) {} + PhysicalDeviceRef(int id) : id(id) {} + + PhysicalDeviceRef& operator =(int other) + { + id = other; + return *this; + } + + int getID() const + { + return id; + } + + operator bool() const + { + return id >= 0; + } + + // Gets a paramter of the physical device. + template + T get(const char* name) const; + + // Gets an opaque data parameter of the physical device. + std::pair getData(const char* name) const + { + size_t byteSize = 0; + const void* ptr = oidnGetPhysicalDeviceData(id, name, &byteSize); + return {ptr, byteSize}; + } + + // Creates a device from the physical device. + DeviceRef newDevice() + { + return DeviceRef(oidnNewDeviceByID(id)); + } + + private: + int id; + }; + + // Returns the number of supported physical devices. + inline int getNumPhysicalDevices() + { + return oidnGetNumPhysicalDevices(); + } + + template<> + inline bool PhysicalDeviceRef::get(const char* name) const + { + return oidnGetPhysicalDeviceBool(id, name); + } + + template<> + inline int PhysicalDeviceRef::get(const char* name) const + { + return oidnGetPhysicalDeviceInt(id, name); + } + + template<> + inline unsigned int PhysicalDeviceRef::get(const char* name) const + { + return oidnGetPhysicalDeviceUInt(id, name); + } + + template<> + inline DeviceType PhysicalDeviceRef::get(const char* name) const + { + return static_cast(oidnGetPhysicalDeviceInt(id, name)); + } + + template<> + inline const char* PhysicalDeviceRef::get(const char* name) const + { + return oidnGetPhysicalDeviceString(id, name); + } + + template<> + inline std::string PhysicalDeviceRef::get(const char* name) const + { + const char* str = oidnGetPhysicalDeviceString(id, name); + return str ? str : ""; + } + + template<> + inline UUID PhysicalDeviceRef::get(const char* name) const + { + UUID uuid{}; + auto data = getData(name); + if (data.first != nullptr) + { + if (data.second == sizeof(uuid.bytes)) + std::memcpy(uuid.bytes, data.first, sizeof(uuid.bytes)); + else + getData(""); // invoke an error + } + return uuid; + } + + template<> + inline LUID PhysicalDeviceRef::get(const char* name) const + { + LUID luid{}; + auto data = getData(name); + if (data.first != nullptr) + { + if (data.second == sizeof(luid.bytes)) + std::memcpy(luid.bytes, data.first, sizeof(luid.bytes)); + else + getData(""); // invoke an error + } + return luid; + } + +OIDN_NAMESPACE_END diff --git a/external/include/json.hpp b/external/include/json.hpp index 8b72ea65..87475ab3 100644 --- a/external/include/json.hpp +++ b/external/include/json.hpp @@ -1,23 +1,39 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - -/****************************************************************************\ - * Note on documentation: The source files contain links to the online * - * documentation of the public API at https://json.nlohmann.me. This URL * - * contains the most recent documentation and should also be applicable to * - * previous versions; documentation for deprecated functions is not * - * removed, but marked deprecated. See "Generate documentation" section in * - * file docs/README.md. * -\****************************************************************************/ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.10.4 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 10 +#define NLOHMANN_JSON_VERSION_PATCH 4 + #include // all_of, find, for_each #include // nullptr_t, ptrdiff_t, size_t #include // hash, less @@ -27,134 +43,18 @@ #endif // JSON_NO_IO #include // random_access_iterator_tag #include // unique_ptr +#include // accumulate #include // string, stoi, to_string #include // declval, forward, move, pair, swap #include // vector // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - +#include #include -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// This file contains all macro definitions affecting or depending on the ABI - -#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK - #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) - #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 - #warning "Already included a different version of the library!" - #endif - #endif -#endif - -#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) - -#ifndef JSON_DIAGNOSTICS - #define JSON_DIAGNOSTICS 0 -#endif - -#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 -#endif - -#if JSON_DIAGNOSTICS - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag -#else - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS -#endif - -#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp -#else - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION - #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 -#endif - -// Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ - NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) - -#define NLOHMANN_JSON_ABI_TAGS \ - NLOHMANN_JSON_ABI_TAGS_CONCAT( \ - NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ - NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) - -// Construct the namespace version component -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ - _v ## major ## _ ## minor ## _ ## patch -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) - -#if NLOHMANN_JSON_NAMESPACE_NO_VERSION -#define NLOHMANN_JSON_NAMESPACE_VERSION -#else -#define NLOHMANN_JSON_NAMESPACE_VERSION \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ - NLOHMANN_JSON_VERSION_MINOR, \ - NLOHMANN_JSON_VERSION_PATCH) -#endif - -// Combine namespace components -#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b -#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ - NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) - -#ifndef NLOHMANN_JSON_NAMESPACE -#define NLOHMANN_JSON_NAMESPACE \ - nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN -#define NLOHMANN_JSON_NAMESPACE_BEGIN \ - namespace nlohmann \ - { \ - inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) \ - { -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_END -#define NLOHMANN_JSON_NAMESPACE_END \ - } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ - } // namespace nlohmann -#endif - // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - #include // transform @@ -170,34 +70,14 @@ #include // valarray // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - -#include // nullptr_t #include // exception -#if JSON_DIAGNOSTICS - #include // accumulate -#endif #include // runtime_error #include // to_string #include // vector // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - #include // array @@ -205,130 +85,102 @@ #include // uint8_t #include // string -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // declval, pair -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail +namespace nlohmann { - -template struct make_void +namespace detail { - using type = void; -}; -template using void_t = typename make_void::type; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END +/////////////////////////// +// JSON type enumeration // +/////////////////////////// +/*! +@brief the JSON type enumeration -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. -// https://en.cppreference.com/w/cpp/experimental/is_detected -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type -template class Op, class... Args> -struct detector>, Op, Args...> +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t { - using value_t = std::true_type; - using type = Op; + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function }; -template class Op, class... Args> -using is_detected = typename detector::value_t; +/*! +@brief comparison operator for JSON types -template class Op, class... Args> -struct is_detected_lazy : is_detected { }; +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. -template class Op, class... Args> -using detected_t = typename detector::type; +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; -template class Op, class... Args> -using detected_or = detector; + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann -template class Op, class... Args> -using detected_or_t = typename detected_or::type; +// #include -template class Op, class... Args> -using is_detected_exact = std::is_same>; -template class Op, class... Args> -using is_detected_convertible = - std::is_convertible, To>; +#include +// #include -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END +#include // declval, pair // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson -// SPDX-License-Identifier: MIT - /* Hedley - https://nemequ.github.io/hedley * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 */ #if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) @@ -2362,147 +2214,115 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ +// #include -// This file contains all internal macro definitions (except those affecting ABI) -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them -// #include +#include +// #include -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) - #if defined(__clang__) - #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 - #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 - #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #endif -#endif -// C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 - #endif - // the cpp 11 flag is always specified because it is the minimal required version - #define JSON_HAS_CPP_11 -#endif +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann -#ifdef __has_include - #if __has_include() - #include - #endif -#endif -#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) - #ifdef JSON_HAS_CPP_17 - #if defined(__cpp_lib_filesystem) - #define JSON_HAS_FILESYSTEM 1 - #elif defined(__cpp_lib_experimental_filesystem) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif !defined(__has_include) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #endif +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; - // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ - #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; - // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; - // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__clang_major__) && __clang_major__ < 7 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif +template class Op, class... Args> +using is_detected = typename detector::value_t; - // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support - #if defined(_MSC_VER) && _MSC_VER < 1914 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; - // no filesystem support before iOS 13 - #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif +template class Op, class... Args> +using detected_t = typename detector::type; - // no filesystem support before macOS Catalina - #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - #endif -#endif +template class Op, class... Args> +using detected_or = detector; -#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 -#endif +template class Op, class... Args> +using detected_or_t = typename detected_or::type; -#ifndef JSON_HAS_FILESYSTEM - #define JSON_HAS_FILESYSTEM 0 -#endif +template class Op, class... Args> +using is_detected_exact = std::is_same>; -#ifndef JSON_HAS_THREE_WAY_COMPARISON - #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ - && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L - #define JSON_HAS_THREE_WAY_COMPARISON 1 - #else - #define JSON_HAS_THREE_WAY_COMPARISON 0 - #endif -#endif +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann -#ifndef JSON_HAS_RANGES - // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error - #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 - #define JSON_HAS_RANGES 0 - #elif defined(__cpp_lib_ranges) - #define JSON_HAS_RANGES 1 - #else - #define JSON_HAS_RANGES 0 - #endif -#endif -#ifndef JSON_HAS_STATIC_RTTI - #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 - #define JSON_HAS_STATIC_RTTI 1 - #else - #define JSON_HAS_STATIC_RTTI 0 - #endif -#endif +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them -#ifdef JSON_HAS_CPP_17 - #define JSON_INLINE_VARIABLE inline -#else - #define JSON_INLINE_VARIABLE +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif #endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) - #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] -#else - #define JSON_NO_UNIQUE_ADDRESS +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 #endif // disable documentation warnings on clang @@ -2512,7 +2332,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" #endif -// allow disabling exceptions +// allow to disable exceptions #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception #define JSON_TRY try @@ -2546,7 +2366,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif -// allow overriding assert +// allow to override assert #if !defined(JSON_ASSERT) #include // assert #define JSON_ASSERT(x) assert(x) @@ -2600,13 +2420,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP class NumberUnsignedType, class NumberFloatType, \ template class AllocatorType, \ template class JSONSerializer, \ - class BinaryType, \ - class CustomBaseClass> + class BinaryType> #define NLOHMANN_BASIC_JSON_TPL \ basic_json + AllocatorType, JSONSerializer, BinaryType> // Macros to simplify conversion from/to types @@ -2743,7 +2562,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); /*! @brief macro @@ -2754,13 +2572,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } - /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @@ -2770,12 +2581,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } // inspired from https://stackoverflow.com/a/26745591 // allows to call any std function as if (e.g. with begin): @@ -2825,132 +2630,13 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_EXPLICIT explicit #endif -#ifndef JSON_DISABLE_ENUM_SERIALIZATION - #define JSON_DISABLE_ENUM_SERIALIZATION 0 -#endif - -#ifndef JSON_USE_GLOBAL_UDLS - #define JSON_USE_GLOBAL_UDLS 1 -#endif - -#if JSON_HAS_THREE_WAY_COMPARISON - #include // partial_ordering -#endif - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/////////////////////////// -// JSON type enumeration // -/////////////////////////// - -/*! -@brief the JSON type enumeration - -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. - -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. - -@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type - -@since version 1.0.0 -*/ -enum class value_t : std::uint8_t -{ - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function -}; - -/*! -@brief comparison operator for JSON types - -Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string < binary -- furthermore, each type is not smaller than itself -- discarded values are not comparable -- binary is represented as a b"" string in python and directly comparable to a - string; however, making a binary array directly comparable with a string would - be surprising behavior in a JSON file. - -@since version 1.0.0 -*/ -#if JSON_HAS_THREE_WAY_COMPARISON - inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* -#else - inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 #endif -{ - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); -#if JSON_HAS_THREE_WAY_COMPARISON - if (l_index < order.size() && r_index < order.size()) - { - return order[l_index] <=> order[r_index]; // *NOPAD* - } - return std::partial_ordering::unordered; -#else - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; -#endif -} -// GCC selects the built-in operator< over an operator rewritten from -// a user-defined spaceship operator -// Clang, MSVC, and ICC select the rewritten candidate -// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) -#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) -inline bool operator<(const value_t lhs, const value_t rhs) noexcept +namespace nlohmann { - return std::is_lt(lhs <=> rhs); // *NOPAD* -} -#endif - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -2967,13 +2653,12 @@ enforced with an assertion.** @since version 2.0.0 */ -template -inline void replace_substring(StringType& s, const StringType& f, - const StringType& t) +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) { JSON_ASSERT(!f.empty()); for (auto pos = s.find(f); // find first occurrence of f - pos != StringType::npos; // make sure f was found + pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t, and pos = s.find(f, pos + t.size())) // find next occurrence of f {} @@ -2986,11 +2671,10 @@ inline void replace_substring(StringType& s, const StringType& f, * * Note the order of escaping "~" to "~0" and "/" to "~1" is important. */ -template -inline StringType escape(StringType s) +inline std::string escape(std::string s) { - replace_substring(s, StringType{"~"}, StringType{"~0"}); - replace_substring(s, StringType{"/"}, StringType{"~1"}); + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); return s; } @@ -3001,36 +2685,24 @@ inline StringType escape(StringType s) * * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ -template -static void unescape(StringType& s) +static void unescape(std::string& s) { - replace_substring(s, StringType{"~1"}, StringType{"/"}); - replace_substring(s, StringType{"~0"}, StringType{"~"}); + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); } -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END +} // namespace detail +} // namespace nlohmann // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - #include // size_t -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN +namespace nlohmann +{ namespace detail { - /// struct to capture the start position of the current token struct position_t { @@ -3048,96 +2720,508 @@ struct position_t } }; -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-FileCopyrightText: 2018 The Abseil Authors -// SPDX-License-Identifier: MIT - - - -#include // array -#include // size_t -#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type -#include // index_sequence, make_index_sequence, index_sequence_for +} // namespace detail +} // namespace nlohmann // #include -NLOHMANN_JSON_NAMESPACE_BEGIN +namespace nlohmann +{ namespace detail { +//////////////// +// exceptions // +//////////////// -template -using uncvref_t = typename std::remove_cv::type>::type; - -#ifdef JSON_HAS_CPP_14 - -// the following utilities are natively available in C++14 -using std::enable_if_t; -using std::index_sequence; -using std::make_index_sequence; -using std::index_sequence_for; +/*! +@brief general exception of the @ref basic_json class -#else +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors -// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h -// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal -//// START OF CODE FROM GOOGLE ABSEIL +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} -// integer_sequence -// -// Class template representing a compile-time integer sequence. An instantiation -// of `integer_sequence` has a sequence of integers encoded in its -// type through its template arguments (which is a common need when -// working with C++11 variadic templates). `absl::integer_sequence` is designed -// to be a drop-in replacement for C++14's `std::integer_sequence`. -// -// Example: -// -// template< class T, T... Ints > -// void user_function(integer_sequence); -// -// int main() -// { -// // user_function's `T` will be deduced to `int` and `Ints...` -// // will be deduced to `0, 1, 2, 3, 4`. -// user_function(make_integer_sequence()); -// } -template -struct integer_sequence +@since version 3.0.0 +*/ +class exception : public std::exception { - using value_type = T; - static constexpr std::size_t size() noexcept + public: + /// returns the explanatory string + const char* what() const noexcept override { - return sizeof...(Ints); + return m.what(); } -}; -// index_sequence -// -// A helper template for an `integer_sequence` of `size_t`, -// `absl::index_sequence` is designed to be a drop-in replacement for C++14's -// `std::index_sequence`. -template -using index_sequence = integer_sequence; + /// the id of the exception + const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) -namespace utility_internal -{ + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + template + static std::string diagnostics(const BasicJsonType& leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector tokens; + for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (¤t->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + case value_t::null: // LCOV_EXCL_LINE + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + static_cast(leaf_element); + return ""; +#endif + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). +json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + exception::diagnostics(context) + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + template + static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + template + static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + template + static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + template + static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ template struct Extend; @@ -3210,64 +3294,52 @@ template<> struct priority_tag<0> {}; template struct static_const { - static JSON_INLINE_VARIABLE constexpr T value{}; + static constexpr T value{}; }; -#ifndef JSON_HAS_CPP_17 - template - constexpr T static_const::value; -#endif +template +constexpr T static_const::value; + +} // namespace detail +} // namespace nlohmann + +// #include -template -inline constexpr std::array make_array(Args&& ... args) -{ - return std::array {{static_cast(std::forward(args))...}}; -} +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; } // namespace detail -NLOHMANN_JSON_NAMESPACE_END +} // namespace nlohmann // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval #include // tuple -#include // char_traits -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT +// #include +// #include -#include // random_access_iterator_tag -// #include +#include // random_access_iterator_tag // #include // #include -NLOHMANN_JSON_NAMESPACE_BEGIN +namespace nlohmann +{ namespace detail { - template struct iterator_types {}; @@ -3306,136 +3378,118 @@ struct iterator_traits::value>> using pointer = T*; using reference = T&; }; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include +} // namespace detail +} // namespace nlohmann // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - // #include -NLOHMANN_JSON_NAMESPACE_BEGIN - +namespace nlohmann +{ NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); - -NLOHMANN_JSON_NAMESPACE_END +} // namespace nlohmann // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - // #include -NLOHMANN_JSON_NAMESPACE_BEGIN - +namespace nlohmann +{ NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); - -NLOHMANN_JSON_NAMESPACE_END +} // namespace nlohmann // #include // #include // #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.3 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann -// SPDX-License-Identifier: MIT - #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ - #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ - #include // int64_t, uint64_t - #include // map - #include // allocator - #include // string - #include // vector +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector - // #include +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; - /*! - @brief namespace for Niels Lohmann - @see https://github.com/nlohmann - @since version 1.0.0 - */ - NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief JSON Pointer - /*! - @brief default JSONSerializer template argument +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. - This serializer ignores the template arguments and uses ADL - ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) - for serialization. - */ - template - struct adl_serializer; +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; - /// a class to store JSON values - /// @sa https://json.nlohmann.me/api/basic_json/ - template class ObjectType = - std::map, - template class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = - adl_serializer, - class BinaryType = std::vector, // cppcheck-suppress syntaxError - class CustomBaseClass = void> - class basic_json; +/*! +@brief default JSON class - /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document - /// @sa https://json.nlohmann.me/api/json_pointer/ - template - class json_pointer; +This type is the default specialization of the @ref basic_json class which +uses the standard template types. - /*! - @brief default specialization - @sa https://json.nlohmann.me/api/json/ - */ - using json = basic_json<>; +@since version 1.0.0 +*/ +using json = basic_json<>; + +template +struct ordered_map; + +/*! +@brief ordered JSON class - /// @brief a minimal map-like container that preserves insertion order - /// @sa https://json.nlohmann.me/api/ordered_map/ - template - struct ordered_map; +This type preserves the insertion order of object keys. - /// @brief specialization that maintains the insertion order of object keys - /// @sa https://json.nlohmann.me/api/ordered_json/ - using ordered_json = basic_json; +@since version 3.9.0 +*/ +using ordered_json = basic_json; - NLOHMANN_JSON_NAMESPACE_END +} // namespace nlohmann #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ -NLOHMANN_JSON_NAMESPACE_BEGIN +namespace nlohmann +{ /*! @brief detail namespace with internal helper functions @@ -3446,7 +3500,6 @@ implementations of some @ref basic_json methods, and meta-programming helpers. */ namespace detail { - ///////////// // helpers // ///////////// @@ -3465,16 +3518,6 @@ template struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; -// used by exceptions create() member functions -// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t -// false_type otherwise -template -struct is_basic_json_context : - std::integral_constant < bool, - is_basic_json::type>::type>::value - || std::is_same::value > -{}; - ////////////////////// // json_ref helpers // ////////////////////// @@ -3576,81 +3619,6 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> T>::value; }; -template -using detect_key_compare = typename T::key_compare; - -template -struct has_key_compare : std::integral_constant::value> {}; - -// obtains the actual object key comparator -template -struct actual_object_comparator -{ - using object_t = typename BasicJsonType::object_t; - using object_comparator_t = typename BasicJsonType::default_object_comparator_t; - using type = typename std::conditional < has_key_compare::value, - typename object_t::key_compare, object_comparator_t>::type; -}; - -template -using actual_object_comparator_t = typename actual_object_comparator::type; - -///////////////// -// char_traits // -///////////////// - -// Primary template of char_traits calls std char_traits -template -struct char_traits : std::char_traits -{}; - -// Explicitly define char traits for unsigned char since it is not standard -template<> -struct char_traits : std::char_traits -{ - using char_type = unsigned char; - using int_type = uint64_t; - - // Redefine to_int_type function - static int_type to_int_type(char_type c) noexcept - { - return static_cast(c); - } - - static char_type to_char_type(int_type i) noexcept - { - return static_cast(i); - } - - static constexpr int_type eof() noexcept - { - return static_cast(EOF); - } -}; - -// Explicitly define char traits for signed char since it is not standard -template<> -struct char_traits : std::char_traits -{ - using char_type = signed char; - using int_type = uint64_t; - - // Redefine to_int_type function - static int_type to_int_type(char_type c) noexcept - { - return static_cast(c); - } - - static char_type to_char_type(int_type i) noexcept - { - return static_cast(i); - } - - static constexpr int_type eof() noexcept - { - return static_cast(EOF); - } -}; /////////////////// // is_ functions // @@ -3658,10 +3626,10 @@ struct char_traits : std::char_traits // https://en.cppreference.com/w/cpp/types/conjunction template struct conjunction : std::true_type { }; -template struct conjunction : B { }; -template -struct conjunction -: std::conditional(B::value), conjunction, B>::type {}; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; // https://en.cppreference.com/w/cpp/types/negation template struct negation : std::integral_constant < bool, !B::value > { }; @@ -3688,6 +3656,7 @@ template struct is_default_constructible> : conjunction...> {}; + template struct is_constructible : std::is_constructible {}; @@ -3703,6 +3672,7 @@ struct is_constructible> : is_default_constructible struct is_constructible> : is_default_constructible> {}; + template struct is_iterator_traits : std::false_type {}; @@ -3811,861 +3781,189 @@ struct is_constructible_object_type_impl < template struct is_constructible_object_type : is_constructible_object_type_impl {}; - -template -struct is_compatible_string_type -{ - static constexpr auto value = - is_constructible::value; -}; - -template -struct is_constructible_string_type -{ - // launder type through decltype() to fix compilation failure on ICPC -#ifdef __INTEL_COMPILER - using laundered_type = decltype(std::declval()); -#else - using laundered_type = ConstructibleStringType; -#endif - - static constexpr auto value = - conjunction < - is_constructible, - is_detected_exact>::value; -}; - -template -struct is_compatible_array_type_impl : std::false_type {}; - -template -struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t < - is_detected::value&& - is_iterator_traits>>::value&& -// special case for types like std::filesystem::path whose iterator's value_type are themselves -// c.f. https://github.com/nlohmann/json/pull/3073 - !std::is_same>::value >> -{ - static constexpr bool value = - is_constructible>::value; -}; - -template -struct is_compatible_array_type - : is_compatible_array_type_impl {}; - -template -struct is_constructible_array_type_impl : std::false_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t::value >> - : std::true_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t < !std::is_same::value&& - !is_compatible_string_type::value&& - is_default_constructible::value&& -(std::is_move_assignable::value || - std::is_copy_assignable::value)&& -is_detected::value&& -is_iterator_traits>>::value&& -is_detected::value&& -// special case for types like std::filesystem::path whose iterator's value_type are themselves -// c.f. https://github.com/nlohmann/json/pull/3073 -!std::is_same>::value&& - is_complete_type < - detected_t>::value >> -{ - using value_type = range_value_t; - - static constexpr bool value = - std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, - value_type >::value; -}; - -template -struct is_constructible_array_type - : is_constructible_array_type_impl {}; - -template -struct is_compatible_integer_type_impl : std::false_type {}; - -template -struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, - enable_if_t < std::is_integral::value&& - std::is_integral::value&& - !std::is_same::value >> -{ - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits; - using CompatibleLimits = std::numeric_limits; - - static constexpr auto value = - is_constructible::value && - CompatibleLimits::is_integer && - RealLimits::is_signed == CompatibleLimits::is_signed; -}; - -template -struct is_compatible_integer_type - : is_compatible_integer_type_impl {}; - -template -struct is_compatible_type_impl: std::false_type {}; - -template -struct is_compatible_type_impl < - BasicJsonType, CompatibleType, - enable_if_t::value >> -{ - static constexpr bool value = - has_to_json::value; -}; - -template -struct is_compatible_type - : is_compatible_type_impl {}; - -template -struct is_constructible_tuple : std::false_type {}; - -template -struct is_constructible_tuple> : conjunction...> {}; - -template -struct is_json_iterator_of : std::false_type {}; - -template -struct is_json_iterator_of : std::true_type {}; - -template -struct is_json_iterator_of : std::true_type -{}; - -// checks if a given type T is a template specialization of Primary -template