Initial port
This commit is contained in:
Родитель
a3a0caab96
Коммит
89e304be9f
|
@ -1,330 +1,2 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
build
|
||||
.vscode
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "external/Catch2"]
|
||||
path = external/Catch2
|
||||
url = https://github.com/catchorg/Catch2
|
|
@ -0,0 +1,19 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project(jsonbuilder)
|
||||
|
||||
# Include sub-projects.
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
# Only include testing stuff if we are the top level
|
||||
if (${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})
|
||||
enable_testing()
|
||||
|
||||
if (NOT TARGET Catch2::Catch2)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/external/Catch2/contrib")
|
||||
add_subdirectory(external/Catch2)
|
||||
endif ()
|
||||
|
||||
add_subdirectory(test)
|
||||
endif ()
|
|
@ -0,0 +1 @@
|
|||
uuid-dev
|
|
@ -0,0 +1,2 @@
|
|||
- Fix comments
|
||||
- Consider a CMake export config file
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Input variables:
|
||||
# UUID_PREFIX
|
||||
#
|
||||
# Output variables
|
||||
# UUID_FOUND
|
||||
# UUID_LIBRARIES
|
||||
# UUID_INCLUDE_DIRS
|
||||
#
|
||||
|
||||
if (UUID_INCLUDE_DIRS AND UUID_LIBRARIES)
|
||||
set(UUID_FIND_QUIETLY TRUE)
|
||||
else ()
|
||||
find_path(UUID_INCLUDE_DIRS NAMES uuid/uuid.h HINTS ${UUID_PREFIX})
|
||||
find_library(UUID_LIBRARIES NAMES uuid HINTS ${UUID_PREFIX})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(UUID DEFAULT_MSG UUID_LIBRARIES UUID_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(UUID_LIBRARIES UUID_INCLUDE_DIRS)
|
||||
endif()
|
||||
|
||||
add_library(uuid INTERFACE)
|
||||
target_include_directories(uuid INTERFACE ${UUID_INCLUDE_DIRS})
|
||||
target_link_libraries(uuid INTERFACE ${UUID_LIBRARIES})
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 979bbf03bb00bc55ca09783791b5091a2247df68
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
JsonRenderer converts JsonBuilder trees into utf8 JSON text.
|
||||
|
||||
Summary:
|
||||
- JsonRenderer
|
||||
- JsonRenderBool
|
||||
- JsonRenderFalse
|
||||
- JsonRenderTime
|
||||
- JsonRenderFloat
|
||||
- JsonRenderInt
|
||||
- JsonRenderNull
|
||||
- JsonRenderTrue
|
||||
- JsonRenderUInt
|
||||
- JsonRenderUuid
|
||||
- JsonRenderUuidWithBraces
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jsonbuilder/JsonBuilder.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
namespace jsonbuilder
|
||||
{
|
||||
/*
|
||||
Converts JsonBuilder data into utf-8 JSON text.
|
||||
Recognizes the built-in JsonType types. To support other (custom) types,
|
||||
derive from JsonRenderer and override RenderCustom.
|
||||
*/
|
||||
class JsonRenderer
|
||||
{
|
||||
protected:
|
||||
typedef JsonInternal::PodVector<char> RenderBuffer;
|
||||
typedef JsonBuilder::const_iterator iterator;
|
||||
|
||||
private:
|
||||
RenderBuffer m_renderBuffer;
|
||||
std::string_view m_newLine;
|
||||
unsigned m_indentSpaces;
|
||||
unsigned m_indent;
|
||||
bool m_pretty;
|
||||
|
||||
public:
|
||||
typedef JsonInternal::PodVector<char>::size_type size_type;
|
||||
|
||||
/*
|
||||
Initializes a new instance of the JsonRenderer class.
|
||||
Optionally sets the initial value of the formatting properties.
|
||||
*/
|
||||
explicit JsonRenderer(
|
||||
bool pretty = false,
|
||||
std::string_view const& newLine = "\n",
|
||||
unsigned indentSpaces = 2) throw();
|
||||
|
||||
/*
|
||||
Preallocates memory in the rendering buffer (increases capacity).
|
||||
*/
|
||||
void Reserve(size_type cb); // may throw bad_alloc, length_error
|
||||
|
||||
/*
|
||||
Gets the current size of the rendering buffer, in bytes.
|
||||
*/
|
||||
size_type Size() const throw();
|
||||
|
||||
/*
|
||||
Gets the current capacity of the rendering buffer (how large it can grow
|
||||
without allocating additional memory), in bytes.
|
||||
*/
|
||||
size_type Capacity() const throw();
|
||||
|
||||
/*
|
||||
Gets a value indicating whether the output will be formatted nicely.
|
||||
If true, insignificant whitespace (spaces and newlines) will be added to
|
||||
improve readability by humans and to put each value on its own line.
|
||||
If false, all insignificant whitespace will be omitted.
|
||||
Default value is false.
|
||||
*/
|
||||
bool Pretty() const throw();
|
||||
|
||||
/*
|
||||
Set a value indicating whether the output will be formatted nicely.
|
||||
If true, insignificant whitespace (spaces and newlines) will be added to
|
||||
improve readability by humans and to put each value on its own line.
|
||||
If false, all insignificant whitespace will be omitted.
|
||||
Default value is false.
|
||||
*/
|
||||
void Pretty(bool) throw();
|
||||
|
||||
/*
|
||||
Gets the string that is used for newline when Pretty() is true.
|
||||
Default value is "\r\n".
|
||||
*/
|
||||
std::string_view const& NewLine() const throw();
|
||||
|
||||
/*
|
||||
Sets the string that is used for newline when Pretty() is true.
|
||||
Note that the JsonRenderer will store a copy of the string_view, but it
|
||||
does not make a copy of the actual string. The string passed in here must
|
||||
be valid for as long as the JsonRenderer exists.
|
||||
Default value is "\r\n".
|
||||
*/
|
||||
void NewLine(std::string_view const&) throw();
|
||||
|
||||
/*
|
||||
Gets the number of spaces per indent level. Default value is 2.
|
||||
*/
|
||||
unsigned IndentSpaces() const throw();
|
||||
|
||||
/*
|
||||
Sets the number of spaces per indent level. Default value is 2.
|
||||
*/
|
||||
void IndentSpaces(unsigned) throw();
|
||||
|
||||
/*
|
||||
Renders the contents of the specified JsonBuilder as utf-8 JSON, starting
|
||||
at the root value.
|
||||
Returns a string_view with the resulting JSON string. The returned string
|
||||
is nul-terminated, but the nul is not included as part of the string_view
|
||||
itself, so return.data()[return.size()] == '\0'.
|
||||
The returned string_view is valid until the next call to Render or until
|
||||
this JsonBuilder is destroyed.
|
||||
*/
|
||||
std::string_view Render(JsonBuilder const& builder); // may throw
|
||||
// bad_alloc,
|
||||
// length_error
|
||||
|
||||
/*
|
||||
Renders the contents of a JsonBuilder as utf-8 JSON, starting at the
|
||||
specified value.
|
||||
Returns a string_view with the resulting JSON string. The returned string
|
||||
is nul-terminated, but the nul is not included as part of the string_view
|
||||
itself, so return.data()[return.size()] == '\0'.
|
||||
The returned string_view is valid until the next call to Render or until
|
||||
this JsonBuilder is destroyed.
|
||||
*/
|
||||
std::string_view
|
||||
Render(JsonBuilder::const_iterator const& it); // may throw bad_alloc,
|
||||
// length_error
|
||||
|
||||
protected:
|
||||
/*
|
||||
Override this method to provide rendering behavior for custom value types.
|
||||
The utf-8 representation of the value referenced by itValue should be
|
||||
appended to the end of buffer by calling buffer.push_back with the bytes
|
||||
of the utf-8 representation.
|
||||
*/
|
||||
virtual void RenderCustom(
|
||||
RenderBuffer& buffer,
|
||||
iterator const& itValue); // may throw bad_alloc, length_error
|
||||
|
||||
private:
|
||||
/*
|
||||
Renders any value and its children by dispatching to the appropriate
|
||||
subroutine.
|
||||
CANNOT RENDER THE ROOT! (Don't call this if it.IsRoot() is true.)
|
||||
*/
|
||||
void RenderValue(iterator const& it);
|
||||
|
||||
/*
|
||||
Renders an object/array value and its children.
|
||||
itParent must be an array or object.
|
||||
Set showNames = true for object. Set showNames = false for array.
|
||||
Can be called with itParent == end() to render the root.
|
||||
Example output: {"ObjectName":{"ArrayName":[7]}}
|
||||
*/
|
||||
void RenderStructure(iterator const& itParent, bool showNames);
|
||||
|
||||
/*
|
||||
Renders value as floating-point. Requires that cb be 4 or 8. Data will be
|
||||
interpreted as a little-endian float or double.
|
||||
Example output: 123.45
|
||||
*/
|
||||
void RenderFloat(double const& value);
|
||||
|
||||
/*
|
||||
Renders value as signed integer. Requires that cb be 1, 2, 4 or 8. Data will
|
||||
be interpreted as a little-endian signed integer.
|
||||
Example output: -12345
|
||||
*/
|
||||
void RenderInt(long long signed const& value);
|
||||
|
||||
/*
|
||||
Renders value as unsigned integer. Requires that cb be 1, 2, 4 or 8. Data
|
||||
will be interpreted as a little-endian unsigned integer.
|
||||
Example output: 12345
|
||||
*/
|
||||
void RenderUInt(long long unsigned const& value);
|
||||
|
||||
/*
|
||||
Renders value as time. Requires that cb be 8. Data will be interpreted as
|
||||
number of 100ns intervals since 1601-01-01T00:00:00Z.
|
||||
Example output: "2015-04-02T02:09:14.7927652Z".
|
||||
*/
|
||||
void RenderTime(std::chrono::system_clock::time_point const& value);
|
||||
|
||||
/*
|
||||
Renders value as UUID. Requires that cb be 16.
|
||||
Example output: "CD8D0A5E-6409-4B8E-9366-B815CEF0E35D".
|
||||
*/
|
||||
void RenderUuid(uuid_t const& value);
|
||||
|
||||
/*
|
||||
Renders value as a string. Converts pch to utf-8, escapes any control
|
||||
characters, and adds quotes around the result. Example output: "String\n"
|
||||
*/
|
||||
void RenderString(std::string_view const& value);
|
||||
|
||||
/*
|
||||
If pretty-printing is disabled, has no effect.
|
||||
If pretty-printing is enabled, writes m_newLine followed by
|
||||
(m_indent * m_indentSpaces) space characters.
|
||||
*/
|
||||
void RenderNewline();
|
||||
};
|
||||
|
||||
/*
|
||||
Renders the given value as an unsigned decimal integer, e.g. "123".
|
||||
Returns the number of characters written, not counting the nul-termination.
|
||||
*/
|
||||
unsigned JsonRenderUInt(long long unsigned n, char* pBuffer) throw();
|
||||
|
||||
/*
|
||||
Renders the given value as a signed decimal integer, e.g. "-123".
|
||||
Returns the number of characters written, not counting the nul-termination.
|
||||
*/
|
||||
unsigned JsonRenderInt(long long signed n, char* pBuffer) throw();
|
||||
|
||||
/*
|
||||
Renders the given value as a signed floating-point, e.g. "-123.1", or "null"
|
||||
if the value is not finite.
|
||||
Returns the number of characters written, not counting the nul-termination.
|
||||
*/
|
||||
unsigned JsonRenderFloat(double n, char* pBuffer) throw();
|
||||
|
||||
/*
|
||||
Renders the string "true" or "false".
|
||||
Returns the number of characters written, not counting the nul-termination.
|
||||
(Always returns 4 or 5.)
|
||||
*/
|
||||
unsigned JsonRenderBool(bool b, char* pBuffer) throw();
|
||||
|
||||
/*
|
||||
Renders the string "null".
|
||||
Returns the number of characters written, not counting the nul-termination.
|
||||
(Always returns 4.)
|
||||
*/
|
||||
unsigned JsonRenderNull(char* pBuffer) throw();
|
||||
|
||||
/*
|
||||
Renders the given FILETIME value (uint64) as an ISO 8601 string, e.g.
|
||||
"2015-04-02T02:09:14.7927652Z".
|
||||
Returns the number of characters written, not counting the nul-termination.
|
||||
(Always returns 28.)
|
||||
*/
|
||||
unsigned JsonRenderTime(
|
||||
std::chrono::system_clock::time_point const& ft,
|
||||
char* pBuffer) throw();
|
||||
|
||||
/*
|
||||
Renders the given GUID value as a string in uppercase without braces, e.g.
|
||||
"CD8D0A5E-6409-4B8E-9366-B815CEF0E35D".
|
||||
Returns the number of characters written, not counting the nul-termination.
|
||||
(Always returns 36.)
|
||||
*/
|
||||
unsigned JsonRenderUuid(uuid_t const& g, char* pBuffer) throw();
|
||||
|
||||
/*
|
||||
Renders the given GUID value as a string in uppercase with braces, e.g.
|
||||
"{CD8D0A5E-6409-4B8E-9366-B815CEF0E35D}".
|
||||
Returns the number of characters written, not counting the nul-termination.
|
||||
(Always returns 38.)
|
||||
*/
|
||||
unsigned JsonRenderUuidWithBraces(uuid_t const& g, char* pBuffer) throw();
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules)
|
||||
find_package(uuid REQUIRED)
|
||||
|
||||
set(JB_PrivateHeaders)
|
||||
set(JB_SourceFiles JsonBuilder.cpp JsonRenderer.cpp PodVector.cpp)
|
||||
|
||||
set(PublicHeaderSrcPrefix ${PROJECT_SOURCE_DIR}/include/jsonbuilder)
|
||||
set(JB_PublicHeaders ${PublicHeaderSrcPrefix}/JsonBuilder.h ${PublicHeaderSrcPrefix}/JsonRenderer.h)
|
||||
|
||||
# Add source to this project's executable.
|
||||
add_library(jsonbuilder ${JB_SourceFiles} ${JB_PrivateHeaders} ${JB_PublicHeaders})
|
||||
target_include_directories(jsonbuilder PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
target_link_libraries(jsonbuilder PUBLIC uuid)
|
||||
|
||||
set_property(TARGET jsonbuilder PROPERTY CXX_STANDARD 17)
|
||||
set_property(TARGET jsonbuilder PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
install(TARGETS jsonbuilder DESTINATION lib)
|
||||
install(FILES ${JB_PublicHeaders} DESTINATION include/jsonbuilder)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,485 @@
|
|||
#include "jsonbuilder/JsonRenderer.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
|
||||
#define WriteChar(ch) m_renderBuffer.push_back(ch)
|
||||
#define WriteChars(pch, cch) m_renderBuffer.append(pch, cch)
|
||||
|
||||
namespace jsonbuilder
|
||||
{
|
||||
/*
|
||||
Efficiently multiply two 32-bit unsigned integers to get a 64-bit result.
|
||||
(The current VC compiler does not optimize this -- if we don't use an
|
||||
intrinsic, it makes a call to _aullmul.)
|
||||
*/
|
||||
#if defined(_M_ARM) || defined(_M_ARM64)
|
||||
# define UMul64(a, b) _arm_umull(a, b)
|
||||
#elif defined(_M_IX86) || defined(_M_X64)
|
||||
# define UMul64(a, b) __emulu(a, b)
|
||||
#else
|
||||
static long long unsigned UMul64(unsigned a, unsigned b)
|
||||
{
|
||||
return static_cast<long long unsigned>(a) * b;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Given a number from 0..15, returns the corresponding uppercase hex digit,
|
||||
i.e. '0'..'F'. Note: it is an error to pass a value larger than 15.
|
||||
*/
|
||||
static inline constexpr char u4_to_hex_upper(char unsigned _Val4)
|
||||
{
|
||||
return ("0123456789ABCDEF")[_Val4];
|
||||
}
|
||||
|
||||
/*
|
||||
Multiply two 64-bit unsigned integers to get a 128-bit result. Return the high
|
||||
64 bits of the answer.
|
||||
*/
|
||||
__attribute__((unused)) static long long unsigned
|
||||
UMul128Hi(long long unsigned a, long long unsigned b)
|
||||
{
|
||||
#if defined(_M_X64) || defined(_M_ARM64)
|
||||
long long unsigned const high = __umulh(a, b);
|
||||
#else
|
||||
long long unsigned const mid =
|
||||
UMul64(static_cast<unsigned>(a), static_cast<unsigned>(b >> 32)) +
|
||||
(UMul64(static_cast<unsigned>(a), static_cast<unsigned>(b)) >> 32);
|
||||
long long unsigned const high =
|
||||
UMul64(static_cast<unsigned>(a >> 32), static_cast<unsigned>(b >> 32)) +
|
||||
(mid >> 32) +
|
||||
((UMul64(static_cast<unsigned>(a >> 32), static_cast<unsigned>(b)) +
|
||||
static_cast<unsigned>(mid)) >>
|
||||
32);
|
||||
#endif
|
||||
return high;
|
||||
}
|
||||
|
||||
/*
|
||||
Formats a uint32 with leading 0s. Always writes cch characters.
|
||||
*/
|
||||
static void FormatUint(unsigned n, char* pch, unsigned cch)
|
||||
{
|
||||
do
|
||||
{
|
||||
--cch;
|
||||
pch[cch] = '0' + (n % 10);
|
||||
n = n / 10;
|
||||
} while (cch != 0);
|
||||
}
|
||||
|
||||
template<unsigned CB, class N>
|
||||
static unsigned JsonRenderXInt(N const& n, char* pBuffer)
|
||||
{
|
||||
// TODO: Why can't we use std::to_wchars when we're using c++17?
|
||||
std::string result = std::to_string(n);
|
||||
std::size_t const cch =
|
||||
std::min(result.size(), static_cast<std::size_t>(CB - 1));
|
||||
strncpy(pBuffer, result.c_str(), cch);
|
||||
pBuffer[cch] = 0;
|
||||
|
||||
pBuffer[cch] = 0;
|
||||
return static_cast<unsigned>(cch);
|
||||
}
|
||||
|
||||
unsigned JsonRenderUInt(long long unsigned n, char* pBuffer) throw()
|
||||
{
|
||||
return JsonRenderXInt<21>(n, pBuffer);
|
||||
}
|
||||
|
||||
unsigned JsonRenderInt(long long signed n, char* pBuffer) throw()
|
||||
{
|
||||
return JsonRenderXInt<21>(n, pBuffer);
|
||||
}
|
||||
|
||||
unsigned JsonRenderFloat(double n, char* pBuffer) throw()
|
||||
{
|
||||
unsigned cch;
|
||||
|
||||
if (isfinite(n))
|
||||
{
|
||||
cch = static_cast<unsigned>(snprintf(pBuffer, 31, "%.17g", n));
|
||||
if (cch > 31)
|
||||
{
|
||||
cch = 31;
|
||||
}
|
||||
pBuffer[cch] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cch = JsonRenderNull(pBuffer);
|
||||
}
|
||||
|
||||
return cch;
|
||||
}
|
||||
|
||||
unsigned JsonRenderBool(bool b, char* pBuffer) throw()
|
||||
{
|
||||
unsigned cch;
|
||||
if (b)
|
||||
{
|
||||
strcpy(pBuffer, "true");
|
||||
cch = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(pBuffer, "false");
|
||||
cch = 5;
|
||||
}
|
||||
return cch;
|
||||
}
|
||||
|
||||
unsigned JsonRenderNull(char* pBuffer) throw()
|
||||
{
|
||||
strcpy(pBuffer, "null");
|
||||
return 4;
|
||||
}
|
||||
|
||||
unsigned JsonRenderTime(
|
||||
std::chrono::system_clock::time_point const& timePoint,
|
||||
char* pBuffer) throw()
|
||||
{
|
||||
time_t printableTime = std::chrono::system_clock::to_time_t(timePoint);
|
||||
tm timeStruct = {};
|
||||
gmtime_r(&printableTime, &timeStruct);
|
||||
|
||||
auto subsecondDuration =
|
||||
timePoint.time_since_epoch() % std::chrono::seconds{ 1 };
|
||||
auto subsecondNanos =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(subsecondDuration);
|
||||
|
||||
FormatUint(static_cast<unsigned>(timeStruct.tm_year + 1900), pBuffer + 0, 4);
|
||||
pBuffer[4] = '-';
|
||||
FormatUint(static_cast<unsigned>(timeStruct.tm_mon + 1), pBuffer + 5, 2);
|
||||
pBuffer[7] = '-';
|
||||
FormatUint(static_cast<unsigned>(timeStruct.tm_mday), pBuffer + 8, 2);
|
||||
pBuffer[10] = 'T';
|
||||
FormatUint(static_cast<unsigned>(timeStruct.tm_hour), pBuffer + 11, 2);
|
||||
pBuffer[13] = ':';
|
||||
FormatUint(static_cast<unsigned>(timeStruct.tm_min), pBuffer + 14, 2);
|
||||
pBuffer[16] = ':';
|
||||
FormatUint(static_cast<unsigned>(timeStruct.tm_sec), pBuffer + 17, 2);
|
||||
pBuffer[19] = '.';
|
||||
FormatUint(
|
||||
static_cast<unsigned>(subsecondNanos.count() / 100), pBuffer + 20, 7);
|
||||
pBuffer[27] = 'Z';
|
||||
pBuffer[28] = 0;
|
||||
return 28;
|
||||
}
|
||||
|
||||
unsigned JsonRenderUuid(uuid_t const& g, char* pBuffer) throw()
|
||||
{
|
||||
uuid_unparse_upper(g, pBuffer);
|
||||
return 36;
|
||||
}
|
||||
|
||||
unsigned JsonRenderUuidWithBraces(uuid_t const& g, char* pBuffer) throw()
|
||||
{
|
||||
pBuffer[0] = '{';
|
||||
JsonRenderUuid(g, pBuffer + 1);
|
||||
pBuffer[37] = '}';
|
||||
pBuffer[38] = 0;
|
||||
return 38;
|
||||
}
|
||||
|
||||
JsonRenderer::JsonRenderer(
|
||||
bool pretty,
|
||||
std::string_view const& newLine,
|
||||
unsigned indentSpaces) throw()
|
||||
: m_newLine(newLine), m_indentSpaces(indentSpaces), m_pretty(pretty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void JsonRenderer::Reserve(size_type cb)
|
||||
{
|
||||
m_renderBuffer.reserve(cb);
|
||||
}
|
||||
|
||||
JsonRenderer::size_type JsonRenderer::Size() const throw()
|
||||
{
|
||||
return m_renderBuffer.size();
|
||||
}
|
||||
|
||||
JsonRenderer::size_type JsonRenderer::Capacity() const throw()
|
||||
{
|
||||
return m_renderBuffer.capacity();
|
||||
}
|
||||
|
||||
bool JsonRenderer::Pretty() const throw()
|
||||
{
|
||||
return m_pretty;
|
||||
}
|
||||
|
||||
void JsonRenderer::Pretty(bool value) throw()
|
||||
{
|
||||
m_pretty = value;
|
||||
}
|
||||
|
||||
std::string_view const& JsonRenderer::NewLine() const throw()
|
||||
{
|
||||
return m_newLine;
|
||||
}
|
||||
|
||||
void JsonRenderer::NewLine(std::string_view const& value) throw()
|
||||
{
|
||||
m_newLine = value;
|
||||
}
|
||||
|
||||
unsigned JsonRenderer::IndentSpaces() const throw()
|
||||
{
|
||||
return m_indentSpaces;
|
||||
}
|
||||
|
||||
void JsonRenderer::IndentSpaces(unsigned value) throw()
|
||||
{
|
||||
m_indentSpaces = value;
|
||||
}
|
||||
|
||||
std::string_view JsonRenderer::Render(JsonBuilder const& builder)
|
||||
{
|
||||
auto itRoot = builder.end();
|
||||
m_renderBuffer.clear();
|
||||
m_indent = 0;
|
||||
RenderStructure(itRoot, true);
|
||||
WriteChar('\0');
|
||||
return std::string_view(m_renderBuffer.data(), m_renderBuffer.size() - 1);
|
||||
}
|
||||
|
||||
std::string_view JsonRenderer::Render(JsonBuilder::const_iterator const& it)
|
||||
{
|
||||
m_renderBuffer.clear();
|
||||
m_indent = 0;
|
||||
if (it.IsRoot())
|
||||
{
|
||||
RenderStructure(it, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderValue(it);
|
||||
}
|
||||
WriteChar('\0');
|
||||
return std::string_view(m_renderBuffer.data(), m_renderBuffer.size() - 1);
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderCustom(RenderBuffer&, iterator const& it)
|
||||
{
|
||||
auto const cchMax = 32u;
|
||||
auto pch = m_renderBuffer.GetAppendPointer(cchMax);
|
||||
unsigned cch =
|
||||
static_cast<unsigned>(snprintf(pch, cchMax, "\"Custom#%u\"", it->Type()));
|
||||
pch += cch > cchMax ? cchMax : cch;
|
||||
m_renderBuffer.SetEndPointer(pch);
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderValue(iterator const& it)
|
||||
{
|
||||
assert(!it.IsRoot());
|
||||
switch (it->Type())
|
||||
{
|
||||
case JsonObject:
|
||||
RenderStructure(it, true);
|
||||
break;
|
||||
case JsonArray:
|
||||
RenderStructure(it, false);
|
||||
break;
|
||||
case JsonNull:
|
||||
WriteChars("null", 4);
|
||||
break;
|
||||
case JsonBool:
|
||||
if (it->GetUnchecked<bool>())
|
||||
{
|
||||
WriteChars("true", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteChars("false", 5);
|
||||
}
|
||||
break;
|
||||
case JsonUtf8:
|
||||
RenderString(it->GetUnchecked<std::string_view>());
|
||||
break;
|
||||
case JsonFloat:
|
||||
RenderFloat(it->GetUnchecked<double>());
|
||||
break;
|
||||
case JsonInt:
|
||||
RenderInt(it->GetUnchecked<long long signed>());
|
||||
break;
|
||||
case JsonUInt:
|
||||
RenderUInt(it->GetUnchecked<long long unsigned>());
|
||||
break;
|
||||
case JsonTime:
|
||||
RenderTime(it->GetUnchecked<std::chrono::system_clock::time_point>());
|
||||
break;
|
||||
case JsonUuid:
|
||||
RenderUuid(it->GetUnchecked<UuidStruct>().Data);
|
||||
break;
|
||||
default:
|
||||
RenderCustom(m_renderBuffer, it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderStructure(iterator const& itParent, bool showNames)
|
||||
{
|
||||
WriteChar(showNames ? '{' : '[');
|
||||
|
||||
auto it = itParent.begin();
|
||||
auto itEnd = itParent.end();
|
||||
if (it != itEnd)
|
||||
{
|
||||
m_indent += m_indentSpaces;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (m_pretty)
|
||||
{
|
||||
RenderNewline();
|
||||
}
|
||||
|
||||
if (showNames)
|
||||
{
|
||||
RenderString(it->Name());
|
||||
WriteChar(':');
|
||||
|
||||
if (m_pretty)
|
||||
{
|
||||
WriteChar(' ');
|
||||
}
|
||||
}
|
||||
|
||||
RenderValue(it);
|
||||
|
||||
++it;
|
||||
if (it == itEnd)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
WriteChar(',');
|
||||
}
|
||||
|
||||
m_indent -= m_indentSpaces;
|
||||
|
||||
if (m_pretty)
|
||||
{
|
||||
RenderNewline();
|
||||
}
|
||||
}
|
||||
|
||||
WriteChar(showNames ? '}' : ']');
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderFloat(double const& value)
|
||||
{
|
||||
auto pch = m_renderBuffer.GetAppendPointer(32);
|
||||
pch += JsonRenderFloat(value, pch);
|
||||
m_renderBuffer.SetEndPointer(pch);
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderInt(long long signed const& value)
|
||||
{
|
||||
auto const cchMax = 20u;
|
||||
auto pch = m_renderBuffer.GetAppendPointer(cchMax);
|
||||
|
||||
// TODO: Why can't we use std::to_wchars when we're using c++17?
|
||||
unsigned cch = static_cast<unsigned>(snprintf(pch, cchMax, "%lld", value));
|
||||
pch += cch > cchMax ? cchMax : cch;
|
||||
m_renderBuffer.SetEndPointer(pch);
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderUInt(long long unsigned const& value)
|
||||
{
|
||||
auto const cchMax = 20u;
|
||||
auto pch = m_renderBuffer.GetAppendPointer(cchMax);
|
||||
|
||||
// TODO: Why can't we use std::to_wchars when we're using c++17?
|
||||
unsigned cch = static_cast<unsigned>(snprintf(pch, cchMax, "%llu", value));
|
||||
pch += cch > cchMax ? cchMax : cch;
|
||||
m_renderBuffer.SetEndPointer(pch);
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderTime(std::chrono::system_clock::time_point const& value)
|
||||
{
|
||||
auto pch = m_renderBuffer.GetAppendPointer(32);
|
||||
*pch++ = '"';
|
||||
pch += JsonRenderTime(value, pch);
|
||||
*pch++ = '"';
|
||||
m_renderBuffer.SetEndPointer(pch);
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderUuid(uuid_t const& value)
|
||||
{
|
||||
auto pch = m_renderBuffer.GetAppendPointer(38);
|
||||
*pch++ = '"';
|
||||
pch += JsonRenderUuid(value, pch);
|
||||
*pch++ = '"';
|
||||
m_renderBuffer.SetEndPointer(pch);
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderString(std::string_view const& value)
|
||||
{
|
||||
WriteChar('"');
|
||||
for (auto ch : value)
|
||||
{
|
||||
if (ch >= 0 && ch < 0x20)
|
||||
{
|
||||
// Control character - must be escaped.
|
||||
switch (ch)
|
||||
{
|
||||
case 8:
|
||||
WriteChar('\\');
|
||||
WriteChar('b');
|
||||
break;
|
||||
case 9:
|
||||
WriteChar('\\');
|
||||
WriteChar('t');
|
||||
break;
|
||||
case 10:
|
||||
WriteChar('\\');
|
||||
WriteChar('n');
|
||||
break;
|
||||
case 12:
|
||||
WriteChar('\\');
|
||||
WriteChar('f');
|
||||
break;
|
||||
case 13:
|
||||
WriteChar('\\');
|
||||
WriteChar('r');
|
||||
break;
|
||||
default:
|
||||
auto p = m_renderBuffer.GetAppendPointer(6);
|
||||
*p++ = '\\';
|
||||
*p++ = 'u';
|
||||
*p++ = '0';
|
||||
*p++ = '0';
|
||||
*p++ = u4_to_hex_upper((ch >> 4) & 0xf);
|
||||
*p++ = u4_to_hex_upper(ch & 0xf);
|
||||
m_renderBuffer.SetEndPointer(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ch == '"' || ch == '\\')
|
||||
{
|
||||
// ASCII character - pass through (escape quote and backslash)
|
||||
WriteChar('\\');
|
||||
WriteChar(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteChar(ch);
|
||||
}
|
||||
}
|
||||
WriteChar('"');
|
||||
}
|
||||
|
||||
void JsonRenderer::RenderNewline()
|
||||
{
|
||||
WriteChars(m_newLine.data(), static_cast<unsigned>(m_newLine.size()));
|
||||
m_renderBuffer.append(m_indent, ' ');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
#include <cassert>
|
||||
|
||||
#include "jsonbuilder/JsonBuilder.h"
|
||||
|
||||
|
||||
namespace jsonbuilder
|
||||
{
|
||||
//
|
||||
// TODO:
|
||||
// Copied from OS code
|
||||
// with a bug fix, since (1 << 32) is undefined in the c standard and evaluates
|
||||
// to 1 on some hardware. Try to find a better source or update PodVector.cpp to
|
||||
// not rely on this.
|
||||
//
|
||||
static inline unsigned char
|
||||
BitScanReverse(unsigned long* Index, unsigned long Mask)
|
||||
{
|
||||
if (Mask == 0 || Index == 0)
|
||||
return 0;
|
||||
|
||||
unsigned int ii = 0;
|
||||
for (ii = ((sizeof(Mask) * 8) - 1); ii >= 0; --ii)
|
||||
{
|
||||
unsigned long tempMask = 1 << ii;
|
||||
if ((Mask & tempMask) != 0)
|
||||
{
|
||||
*Index = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (ii >= 0 ? (unsigned char) 1 : (unsigned char) 0);
|
||||
}
|
||||
|
||||
namespace JsonInternal
|
||||
{
|
||||
void PodVectorBase::CheckOffset(size_type index, size_type currentSize) throw()
|
||||
{
|
||||
(void) index; // Unreferenced parameter
|
||||
(void) currentSize; // Unreferenced parameter
|
||||
assert(index < currentSize);
|
||||
}
|
||||
|
||||
void PodVectorBase::CheckRange(void const* p1, void const* p2, void const* p3) throw()
|
||||
{
|
||||
(void) p1; // Unreferenced parameter
|
||||
(void) p2; // Unreferenced parameter
|
||||
(void) p3; // Unreferenced parameter
|
||||
assert(p1 <= p2 && p2 <= p3);
|
||||
}
|
||||
|
||||
PodVectorBase::size_type PodVectorBase::CheckedAdd(size_type a, size_type b)
|
||||
{
|
||||
size_type c = a + b;
|
||||
if (c < a)
|
||||
{
|
||||
throw std::length_error("JsonVector - exceeded maximum capacity");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void PodVectorBase::InitData(void* pDest, void const* pSource, size_t cb) throw()
|
||||
{
|
||||
memcpy(pDest, pSource, cb);
|
||||
}
|
||||
|
||||
PodVectorBase::size_type
|
||||
PodVectorBase::GetNewCapacity(size_type minCapacity, size_type maxCapacity)
|
||||
{
|
||||
size_type cap;
|
||||
if (minCapacity <= 15)
|
||||
{
|
||||
cap = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
long unsigned index;
|
||||
BitScanReverse(&index, minCapacity);
|
||||
cap = static_cast<uint32_t>((2 << index) - 1);
|
||||
}
|
||||
|
||||
if (maxCapacity < cap)
|
||||
{
|
||||
if (maxCapacity < minCapacity)
|
||||
{
|
||||
throw std::length_error("JsonVector - exceeded maximum capacity");
|
||||
}
|
||||
|
||||
cap = maxCapacity;
|
||||
}
|
||||
|
||||
assert(15 <= cap);
|
||||
assert(minCapacity <= cap);
|
||||
assert(cap <= maxCapacity);
|
||||
return cap;
|
||||
}
|
||||
|
||||
void* PodVectorBase::Reallocate(void* pb, size_t cb, bool zeroInitializeMemory)
|
||||
{
|
||||
void* const pbNew = pb ? realloc(pb, cb) : malloc(cb);
|
||||
|
||||
if (pbNew == nullptr)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
if (zeroInitializeMemory)
|
||||
{
|
||||
memset(pbNew, 0, cb);
|
||||
}
|
||||
|
||||
return pbNew;
|
||||
}
|
||||
|
||||
void PodVectorBase::Deallocate(void* pb) throw()
|
||||
{
|
||||
if (pb)
|
||||
{
|
||||
free(pb);
|
||||
}
|
||||
}
|
||||
}
|
||||
// namespace JsonInternal
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
# Add source to this project's executable.
|
||||
add_executable(jsonbuilderTest CatchMain.cpp TestBuilder.cpp TestRenderer.cpp)
|
||||
target_link_libraries(jsonbuilderTest PRIVATE jsonbuilder Catch2::Catch2)
|
||||
|
||||
set_property(TARGET jsonbuilderTest PROPERTY CXX_STANDARD 17)
|
||||
|
||||
include(CTest)
|
||||
include(Catch)
|
||||
catch_discover_tests(jsonbuilderTest)
|
||||
|
||||
install(TARGETS jsonbuilderTest DESTINATION bin)
|
|
@ -0,0 +1,2 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch.hpp>
|
|
@ -0,0 +1,630 @@
|
|||
#include <catch2/catch.hpp>
|
||||
#include <jsonbuilder/JsonBuilder.h>
|
||||
|
||||
using namespace jsonbuilder;
|
||||
|
||||
TEST_CASE("JsonBuilder buffer reserve", "[builder]")
|
||||
{
|
||||
constexpr auto c_maxSize = JsonBuilder::buffer_max_size();
|
||||
|
||||
JsonBuilder b;
|
||||
|
||||
SECTION("Buffer begins empty")
|
||||
{
|
||||
REQUIRE(b.buffer_size() == 0);
|
||||
REQUIRE(b.buffer_capacity() == 0);
|
||||
}
|
||||
|
||||
SECTION("Buffer reserving 0 remains empty")
|
||||
{
|
||||
b.buffer_reserve(0);
|
||||
REQUIRE(b.buffer_size() == 0);
|
||||
REQUIRE(b.buffer_capacity() == 0);
|
||||
}
|
||||
|
||||
SECTION("Buffer reserving 1 keeps size 0 and makes a capacity of at least "
|
||||
"the size of one element")
|
||||
{
|
||||
b.buffer_reserve(1);
|
||||
REQUIRE(b.buffer_size() == 0);
|
||||
REQUIRE(b.buffer_capacity() >= 4);
|
||||
}
|
||||
|
||||
SECTION("Buffer reserving 2 keeps size 0 and makes a capacity of at least "
|
||||
"the size of one element")
|
||||
{
|
||||
b.buffer_reserve(2);
|
||||
REQUIRE(b.buffer_size() == 0);
|
||||
REQUIRE(b.buffer_capacity() >= 4);
|
||||
}
|
||||
|
||||
SECTION("Buffer reserving 5 keeps size 0 and makes a capacity of at least "
|
||||
"the size of two elements")
|
||||
{
|
||||
b.buffer_reserve(5);
|
||||
REQUIRE(b.buffer_size() == 0);
|
||||
REQUIRE(b.buffer_capacity() >= 8);
|
||||
}
|
||||
|
||||
SECTION("Buffer reserving max succeeds or throws std::bad_alloc")
|
||||
{
|
||||
auto reserveMax = [&]() {
|
||||
try
|
||||
{
|
||||
b.buffer_reserve(c_maxSize);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{}
|
||||
};
|
||||
|
||||
REQUIRE_NOTHROW(reserveMax());
|
||||
}
|
||||
|
||||
SECTION("Buffer reserving one more than max throws std::length_error")
|
||||
{
|
||||
REQUIRE_THROWS_AS(b.buffer_reserve(c_maxSize + 1), std::length_error);
|
||||
}
|
||||
|
||||
SECTION("Buffer reserving maximum of size_type throws std::length_error")
|
||||
{
|
||||
auto maxSizeType = ~JsonBuilder::size_type(0);
|
||||
REQUIRE_THROWS_AS(b.buffer_reserve(maxSizeType), std::length_error);
|
||||
}
|
||||
}
|
||||
|
||||
template<class InputType, class OutputType>
|
||||
static void TestInputOutputScalar()
|
||||
{
|
||||
using InputLimits = std::numeric_limits<InputType>;
|
||||
using OutputLimits = std::numeric_limits<OutputType>;
|
||||
|
||||
JsonBuilder b;
|
||||
|
||||
b.push_back(b.end(), "", InputLimits::lowest());
|
||||
b.push_back(b.end(), "", InputLimits::min());
|
||||
b.push_back(b.end(), "", InputLimits::max());
|
||||
b.push_back(b.end(), "", OutputLimits::lowest());
|
||||
b.push_back(b.end(), "", OutputLimits::min());
|
||||
b.push_back(b.end(), "", OutputLimits::max());
|
||||
|
||||
REQUIRE_NOTHROW(b.ValidateData());
|
||||
|
||||
InputType i;
|
||||
|
||||
auto it = b.begin();
|
||||
REQUIRE(it->GetUnchecked<InputType>() == InputLimits::lowest());
|
||||
REQUIRE(it->ConvertTo(i));
|
||||
REQUIRE(i == InputLimits::lowest());
|
||||
|
||||
++it;
|
||||
REQUIRE(it->GetUnchecked<InputType>() == InputLimits::min());
|
||||
REQUIRE(it->ConvertTo(i));
|
||||
REQUIRE(i == InputLimits::min());
|
||||
|
||||
++it;
|
||||
REQUIRE(it->GetUnchecked<InputType>() == InputLimits::max());
|
||||
REQUIRE(it->ConvertTo(i));
|
||||
REQUIRE(i == InputLimits::max());
|
||||
|
||||
++it;
|
||||
REQUIRE(
|
||||
it->GetUnchecked<InputType>() ==
|
||||
static_cast<InputType>(OutputLimits::lowest()));
|
||||
if (it->ConvertTo(i))
|
||||
{
|
||||
REQUIRE(i == static_cast<InputType>(OutputLimits::lowest()));
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE(i == 0);
|
||||
REQUIRE(it->GetUnchecked<InputType>() != OutputLimits::lowest());
|
||||
}
|
||||
|
||||
++it;
|
||||
REQUIRE(
|
||||
it->GetUnchecked<InputType>() ==
|
||||
static_cast<InputType>(OutputLimits::min()));
|
||||
if (it->ConvertTo(i))
|
||||
{
|
||||
REQUIRE(i == static_cast<InputType>(OutputLimits::min()));
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE(i == 0);
|
||||
REQUIRE(it->GetUnchecked<InputType>() != OutputLimits::min());
|
||||
}
|
||||
|
||||
++it;
|
||||
REQUIRE(
|
||||
it->GetUnchecked<InputType>() ==
|
||||
static_cast<InputType>(OutputLimits::max()));
|
||||
if (it->ConvertTo(i))
|
||||
{
|
||||
REQUIRE(i == static_cast<InputType>(OutputLimits::max()));
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE(i == 0);
|
||||
REQUIRE(it->GetUnchecked<InputType>() != OutputLimits::max());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder numeric limits", "[builder]")
|
||||
{
|
||||
SECTION("signed char") { TestInputOutputScalar<signed char, int64_t>(); }
|
||||
SECTION("signed short") { TestInputOutputScalar<signed short, int64_t>(); }
|
||||
SECTION("signed int") { TestInputOutputScalar<signed int, int64_t>(); }
|
||||
SECTION("signed long") { TestInputOutputScalar<signed long, int64_t>(); }
|
||||
SECTION("signed long long")
|
||||
{
|
||||
TestInputOutputScalar<signed long long, int64_t>();
|
||||
}
|
||||
|
||||
SECTION("unsigned char")
|
||||
{
|
||||
TestInputOutputScalar<unsigned char, uint64_t>();
|
||||
}
|
||||
SECTION("unsigned short")
|
||||
{
|
||||
TestInputOutputScalar<unsigned short, uint64_t>();
|
||||
}
|
||||
SECTION("unsigned int") { TestInputOutputScalar<unsigned int, uint64_t>(); }
|
||||
SECTION("unsigned long")
|
||||
{
|
||||
TestInputOutputScalar<unsigned long, uint64_t>();
|
||||
}
|
||||
SECTION("unsigned long long")
|
||||
{
|
||||
TestInputOutputScalar<unsigned long long, uint64_t>();
|
||||
}
|
||||
|
||||
SECTION("float") { TestInputOutputScalar<float, double>(); }
|
||||
SECTION("double") { TestInputOutputScalar<double, double>(); }
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder string push_back")
|
||||
{
|
||||
JsonBuilder b;
|
||||
|
||||
SECTION("push_back std::string_view")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", std::string_view{ "ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back std::string")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", std::string{ "ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back char")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", ' ');
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == " ");
|
||||
}
|
||||
|
||||
SECTION("push_back char*")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", const_cast<char*>("ABC"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABC");
|
||||
}
|
||||
|
||||
SECTION("push_back const char*")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", static_cast<const char*>("DEF"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "DEF");
|
||||
}
|
||||
|
||||
SECTION("push_back const char[]")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", "HIJ");
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "HIJ");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder chrono push_back", "[builder]")
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
JsonBuilder b;
|
||||
auto itr = b.push_back(b.end(), "CurrentTime", now);
|
||||
auto retrieved = itr->GetUnchecked<std::chrono::system_clock::time_point>();
|
||||
REQUIRE(retrieved == now);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder uuid push_back", "[builder]")
|
||||
{
|
||||
UuidStruct uuid;
|
||||
uuid_generate(uuid.Data);
|
||||
|
||||
JsonBuilder b;
|
||||
auto itr = b.push_back(b.end(), "Uuid", uuid);
|
||||
|
||||
auto retrieved = itr->GetUnchecked<UuidStruct>();
|
||||
REQUIRE(uuid_compare(retrieved.Data, uuid.Data) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder find", "[builder]")
|
||||
{
|
||||
JsonBuilder b;
|
||||
|
||||
// Empty object
|
||||
REQUIRE(b.find("a1") == b.end());
|
||||
REQUIRE(b.find("a1", "a2") == b.end());
|
||||
|
||||
// Single object (a1)
|
||||
auto itA1 = b.push_back(b.end(), "a1", JsonObject);
|
||||
REQUIRE(b.find("a1") == itA1);
|
||||
REQUIRE(b.find(b.end(), "a1") == itA1);
|
||||
REQUIRE(b.find("b1") == b.end());
|
||||
REQUIRE(b.find(b.end(), "b1") == b.end());
|
||||
REQUIRE(b.find("a1", "a2") == b.end());
|
||||
|
||||
// Second object b2, sibling of a1
|
||||
auto itB1 = b.push_back(b.end(), "b1", JsonObject);
|
||||
REQUIRE(b.find("a1") == itA1);
|
||||
REQUIRE(b.find("a1", "a2") == b.end());
|
||||
REQUIRE(b.find("b1") == itB1);
|
||||
REQUIRE(b.find("c1") == b.end());
|
||||
|
||||
// First child of a1, a2
|
||||
auto itA1A2 = b.push_back(itA1, "a2", JsonObject);
|
||||
REQUIRE(b.find("a1") == itA1);
|
||||
REQUIRE(b.find("a1", "a2") == itA1A2);
|
||||
REQUIRE(b.find(b.end(), "a1", "a2") == itA1A2);
|
||||
REQUIRE(b.find("a1", "a2", "a3") == b.end());
|
||||
REQUIRE(b.find("b1") == itB1);
|
||||
REQUIRE(b.find("c1") == b.end());
|
||||
|
||||
// First child of a2, a3
|
||||
auto itA1A2A3 = b.push_back(itA1A2, "a3", 0);
|
||||
REQUIRE(b.find("a1", "a2", "a3") == itA1A2A3);
|
||||
REQUIRE(b.find(itA1, "a2") == itA1A2);
|
||||
REQUIRE(b.find(itB1, "a2") == b.end());
|
||||
|
||||
REQUIRE_NOTHROW(b.ValidateData());
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder constructors", "[builder]")
|
||||
{
|
||||
JsonBuilder b;
|
||||
b.push_back(b.end(), "aname", "ava");
|
||||
b.push_back(b.end(), "bname", "bva");
|
||||
REQUIRE_NOTHROW(b.ValidateData());
|
||||
|
||||
SECTION("Copy constructor")
|
||||
{
|
||||
JsonBuilder copy{ b };
|
||||
REQUIRE_NOTHROW(copy.ValidateData());
|
||||
|
||||
auto it = copy.begin();
|
||||
REQUIRE(it->Name() == "aname");
|
||||
REQUIRE(it->GetUnchecked<std::string_view>() == "ava");
|
||||
|
||||
++it;
|
||||
REQUIRE(it->Name() == "bname");
|
||||
REQUIRE(it->GetUnchecked<std::string_view>() == "bva");
|
||||
}
|
||||
|
||||
SECTION("Move constructor")
|
||||
{
|
||||
JsonBuilder move{ std::move(b) };
|
||||
REQUIRE_NOTHROW(move.ValidateData());
|
||||
|
||||
auto it = move.begin();
|
||||
REQUIRE(it->Name() == "aname");
|
||||
REQUIRE(it->GetUnchecked<std::string_view>() == "ava");
|
||||
|
||||
++it;
|
||||
REQUIRE(it->Name() == "bname");
|
||||
REQUIRE(it->GetUnchecked<std::string_view>() == "bva");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder erase", "[builder]")
|
||||
{
|
||||
JsonBuilder b;
|
||||
b.push_back(b.end(), "aname", "ava");
|
||||
b.push_back(b.end(), "bname", "bva");
|
||||
REQUIRE_NOTHROW(b.ValidateData());
|
||||
|
||||
SECTION("erase a single child element")
|
||||
{
|
||||
auto itr = b.erase(b.begin());
|
||||
REQUIRE_NOTHROW(b.ValidateData());
|
||||
REQUIRE(itr == b.begin());
|
||||
REQUIRE(b.count(b.end()) == 1);
|
||||
}
|
||||
|
||||
SECTION("erase all children")
|
||||
{
|
||||
auto itr = b.erase(b.begin(), b.end());
|
||||
REQUIRE_NOTHROW(b.ValidateData());
|
||||
REQUIRE(itr == b.end());
|
||||
REQUIRE(b.begin() == b.end());
|
||||
REQUIRE(b.count(itr) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder conversions", "[builder]")
|
||||
{
|
||||
int64_t ival;
|
||||
uint64_t uval;
|
||||
double fval;
|
||||
std::string_view sval;
|
||||
bool bval;
|
||||
std::chrono::system_clock::time_point tval;
|
||||
UuidStruct uuidval;
|
||||
|
||||
JsonBuilder b;
|
||||
|
||||
SECTION("JsonNull")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "FirstItem", JsonNull);
|
||||
|
||||
REQUIRE(itr->IsNull());
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE(!itr->ConvertTo(fval));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("false")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", false);
|
||||
|
||||
REQUIRE(itr->GetUnchecked<bool>() == false);
|
||||
REQUIRE((itr->ConvertTo(bval) && !bval));
|
||||
REQUIRE(!itr->ConvertTo(fval));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("true")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", true);
|
||||
|
||||
REQUIRE(itr->GetUnchecked<bool>() == true);
|
||||
REQUIRE((itr->ConvertTo(bval) && bval));
|
||||
REQUIRE(!itr->ConvertTo(fval));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("int64_t")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 123);
|
||||
|
||||
REQUIRE(itr->GetUnchecked<int64_t>() == 123);
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 123));
|
||||
REQUIRE((itr->ConvertTo(ival) && ival == 123));
|
||||
REQUIRE((itr->ConvertTo(uval) && uval == 123));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("uint64_t")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 123u);
|
||||
|
||||
REQUIRE(itr->GetUnchecked<uint64_t>() == 123u);
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 123));
|
||||
REQUIRE((itr->ConvertTo(ival) && ival == 123));
|
||||
REQUIRE((itr->ConvertTo(uval) && uval == 123));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("double")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 123.0);
|
||||
|
||||
REQUIRE(itr->GetUnchecked<double>() == 123.0);
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 123));
|
||||
REQUIRE((itr->ConvertTo(ival) && ival == 123));
|
||||
REQUIRE((itr->ConvertTo(uval) && uval == 123));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", "ABC");
|
||||
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABC");
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE(!itr->ConvertTo(fval));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE((itr->ConvertTo(sval) && sval == "ABC"));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("less than int64_t min as double")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", -9223372036854777856.0);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == -9223372036854777856.0));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("int64_t min")
|
||||
{
|
||||
// This value of -9223372036854775808 cannot be described as a literal
|
||||
// due to negative literals being positive literals with a unary minus
|
||||
// applied.
|
||||
constexpr int64_t c_int64min = (-9223372036854775807ll - 1);
|
||||
|
||||
auto itr = b.push_back(b.end(), "", c_int64min);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == -9223372036854775808.0));
|
||||
REQUIRE((itr->ConvertTo(ival) && ival == c_int64min));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("-1")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", -1);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == -1.0));
|
||||
REQUIRE((itr->ConvertTo(ival) && ival == -1));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("0")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 0);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 0.0));
|
||||
REQUIRE((itr->ConvertTo(ival) && ival == 0));
|
||||
REQUIRE((itr->ConvertTo(uval) && uval == 0));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("int64_t max")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 9223372036854775807);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 9223372036854775807.0));
|
||||
REQUIRE((itr->ConvertTo(ival) && ival == 9223372036854775807));
|
||||
REQUIRE((itr->ConvertTo(uval) && uval == 9223372036854775807));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("greater than int64_t max and less than uint64_t max")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 9223372036854775808ull);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 9223372036854775808.0));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE((itr->ConvertTo(uval) && uval == 9223372036854775808ull));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("greater than int64_t max and less than uint64_t max as double")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 9223372036854777856.0);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 9223372036854777856.0));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE((itr->ConvertTo(uval) && uval == 9223372036854777856ull));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("uint64_t max")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 18446744073709551615ull);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 18446744073709551615.0));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE((itr->ConvertTo(uval) && uval == 18446744073709551615ull));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("greater than uint64_t max")
|
||||
{
|
||||
auto itr = b.push_back(b.end(), "", 18446744073709551616.0);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE((itr->ConvertTo(fval) && fval == 18446744073709551615.0));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("time")
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto itr = b.push_back(b.end(), "", now);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE(!itr->ConvertTo(fval));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE((itr->ConvertTo(tval) && tval == now));
|
||||
REQUIRE(!itr->ConvertTo(uuidval));
|
||||
}
|
||||
|
||||
SECTION("uuid")
|
||||
{
|
||||
UuidStruct uuid;
|
||||
uuid_generate(uuid.Data);
|
||||
|
||||
auto itr = b.push_back(b.end(), "", uuid);
|
||||
|
||||
REQUIRE(!itr->ConvertTo(bval));
|
||||
REQUIRE(!itr->ConvertTo(fval));
|
||||
REQUIRE(!itr->ConvertTo(ival));
|
||||
REQUIRE(!itr->ConvertTo(uval));
|
||||
REQUIRE(!itr->ConvertTo(sval));
|
||||
REQUIRE(!itr->ConvertTo(tval));
|
||||
REQUIRE(
|
||||
(itr->ConvertTo(uuidval) &&
|
||||
0 == uuid_compare(uuidval.Data, uuid.Data)));
|
||||
}
|
||||
}
|
||||
// int main()
|
||||
// {
|
||||
// JsonBuilder builder;
|
||||
// builder.push_back(builder.end(), "field", 5l);
|
||||
// builder.push_back(builder.end(), "String", "Grandes écoles");
|
||||
// builder.push_back(
|
||||
// builder.end(), "CurrentTime", std::chrono::system_clock::now());
|
||||
|
||||
// JsonRenderer renderer;
|
||||
// renderer.Pretty(true);
|
||||
|
||||
// std::string_view output = renderer.Render(builder);
|
||||
|
||||
// std::cout << output << std::endl;
|
||||
|
||||
// return 0;
|
||||
// }
|
|
@ -0,0 +1,239 @@
|
|||
#include <cstdio>
|
||||
#include <iterator>
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <jsonbuilder/JsonRenderer.h>
|
||||
|
||||
using namespace jsonbuilder;
|
||||
|
||||
template<class N>
|
||||
static void TestUInt(N n)
|
||||
{
|
||||
unsigned const cchBuf = 24;
|
||||
char buf1[cchBuf];
|
||||
char buf2[cchBuf];
|
||||
unsigned cch;
|
||||
|
||||
memset(buf1, 1, sizeof(buf1));
|
||||
cch = JsonRenderUInt(n, buf1);
|
||||
REQUIRE(cch < cchBuf);
|
||||
for (unsigned i = cch + 1; i != cchBuf; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == 1);
|
||||
}
|
||||
std::snprintf(
|
||||
buf2, std::size(buf2), "%llu", static_cast<long long unsigned>(n));
|
||||
for (unsigned i = 0; i <= cch; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == buf2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<class N>
|
||||
static void TestInt(N n)
|
||||
{
|
||||
unsigned const cchBuf = 24;
|
||||
char buf1[cchBuf];
|
||||
char buf2[cchBuf];
|
||||
unsigned cch;
|
||||
|
||||
memset(buf1, 1, sizeof(buf1));
|
||||
cch = JsonRenderInt(n, buf1);
|
||||
REQUIRE(cch < cchBuf);
|
||||
for (unsigned i = cch + 1; i != cchBuf; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == 1);
|
||||
}
|
||||
std::snprintf(buf2, std::size(buf2), "%lld", static_cast<long long signed>(n));
|
||||
for (unsigned i = 0; i <= cch; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == buf2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<class N>
|
||||
static void TestFloat(N n)
|
||||
{
|
||||
unsigned const cchBuf = 32;
|
||||
char buf1[cchBuf];
|
||||
char buf2[cchBuf];
|
||||
unsigned cch;
|
||||
|
||||
memset(buf1, 1, sizeof(buf1));
|
||||
cch = JsonRenderFloat(n, buf1);
|
||||
REQUIRE(cch < cchBuf);
|
||||
for (unsigned i = cch + 1; i != cchBuf; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == 1);
|
||||
}
|
||||
std::snprintf(buf2, std::size(buf2), "%.17g", static_cast<double>(n));
|
||||
for (unsigned i = 0; i <= cch; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == buf2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void TestBool(bool n)
|
||||
{
|
||||
unsigned const cchBuf = 6;
|
||||
char buf1[cchBuf];
|
||||
char const* buf2 = n ? "true" : "false";
|
||||
memset(buf1, 1, sizeof(buf1));
|
||||
unsigned cch = JsonRenderBool(n, buf1);
|
||||
REQUIRE(cch < cchBuf);
|
||||
for (unsigned i = cch + 1; i != cchBuf; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == 1);
|
||||
}
|
||||
for (unsigned i = 0; i <= cch; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == buf2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Unused
|
||||
// static void TestTime() {}
|
||||
|
||||
template<class N>
|
||||
static void TestUInts()
|
||||
{
|
||||
SECTION("0") { TestUInt<N>(0); }
|
||||
SECTION("min") { TestUInt<N>(std::numeric_limits<N>::min()); }
|
||||
SECTION("max") { TestUInt<N>(std::numeric_limits<N>::max()); }
|
||||
}
|
||||
|
||||
template<class N>
|
||||
static void TestInts()
|
||||
{
|
||||
SECTION("0") { TestInt<N>(0); }
|
||||
SECTION("min") { TestInt<N>(std::numeric_limits<N>::min()); }
|
||||
SECTION("max") { TestInt<N>(std::numeric_limits<N>::max()); }
|
||||
}
|
||||
|
||||
template<class N>
|
||||
static void TestFloats()
|
||||
{
|
||||
SECTION("0") { TestFloat<N>(0); }
|
||||
SECTION("min") { TestFloat<N>(std::numeric_limits<N>::min()); }
|
||||
SECTION("max") { TestFloat<N>(std::numeric_limits<N>::max()); }
|
||||
}
|
||||
|
||||
TEST_CASE("JsonRenderer values match printf", "[renderer]")
|
||||
{
|
||||
SECTION("signed char") { TestInts<signed char>(); }
|
||||
SECTION("signed short") { TestInts<signed short>(); }
|
||||
SECTION("signed int") { TestInts<signed int>(); }
|
||||
SECTION("signed long") { TestInts<signed long>(); }
|
||||
SECTION("signed long long") { TestInts<signed long long>(); }
|
||||
|
||||
SECTION("unsigned char") { TestUInts<unsigned char>(); }
|
||||
SECTION("unsigned short") { TestUInts<unsigned short>(); }
|
||||
SECTION("unsigned int") { TestUInts<unsigned int>(); }
|
||||
SECTION("unsigned long") { TestUInts<unsigned long>(); }
|
||||
SECTION("unsigned long long") { TestUInts<unsigned long long>(); }
|
||||
|
||||
SECTION("float") { TestFloats<float>(); }
|
||||
SECTION("double") { TestFloats<double>(); }
|
||||
|
||||
SECTION("bool-false") { TestBool(false); }
|
||||
SECTION("bool-true") { TestBool(true); }
|
||||
}
|
||||
|
||||
TEST_CASE("JsonRenderer JsonNull")
|
||||
{
|
||||
unsigned const cchBuf = 24;
|
||||
char buf1[cchBuf];
|
||||
char const* buf2 = "null";
|
||||
memset(buf1, 1, sizeof(buf1));
|
||||
unsigned cch = JsonRenderNull(buf1);
|
||||
REQUIRE(cch < cchBuf);
|
||||
for (unsigned i = cch + 1; i != cchBuf; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == 1);
|
||||
}
|
||||
for (unsigned i = 0; i <= cch; i++)
|
||||
{
|
||||
REQUIRE(buf1[i] == buf2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
TEST_CASE("JsonRenderer JsonTime", "[renderer]")
|
||||
{
|
||||
auto epoch = std::chrono::system_clock::from_time_t(0);
|
||||
|
||||
char chars[39];
|
||||
memset(chars, 1, sizeof(chars));
|
||||
|
||||
unsigned cch = JsonRenderTime(epoch, chars);
|
||||
REQUIRE(cch == strlen(chars));
|
||||
REQUIRE(chars == "1970-01-01T00:00:00.0000000Z"sv);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonRenderer JsonUuid", "[renderer]")
|
||||
{
|
||||
uuid_t uuid;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
uuid[i] = i;
|
||||
}
|
||||
|
||||
char chars[39];
|
||||
memset(chars, 1, sizeof(chars));
|
||||
|
||||
SECTION("Without braces")
|
||||
{
|
||||
unsigned cch = JsonRenderUuid(uuid, chars);
|
||||
REQUIRE(cch == strlen(chars));
|
||||
REQUIRE(chars == "00010203-0405-0607-0809-0A0B0C0D0E0F"sv);
|
||||
}
|
||||
|
||||
SECTION("With braces")
|
||||
{
|
||||
unsigned cch = JsonRenderUuidWithBraces(uuid, chars);
|
||||
REQUIRE(cch == strlen(chars));
|
||||
REQUIRE(chars == "{00010203-0405-0607-0809-0A0B0C0D0E0F}"sv);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonRenderer full object", "[renderer]")
|
||||
{
|
||||
JsonBuilder b;
|
||||
|
||||
auto objItr = b.push_back(b.end(), "obj", JsonObject);
|
||||
b.push_back(objItr, "str", "strval");
|
||||
b.push_back(objItr, "str2", "str2val");
|
||||
|
||||
auto arrItr = b.push_back(b.end(), "arr", JsonArray);
|
||||
b.push_back(arrItr, "useless", 1);
|
||||
b.push_back(arrItr, "useless2", 2);
|
||||
|
||||
SECTION("Default renderer")
|
||||
{
|
||||
JsonRenderer renderer;
|
||||
auto renderString = renderer.Render(b);
|
||||
REQUIRE(
|
||||
renderString ==
|
||||
R"({"obj":{"str":"strval","str2":"str2val"},"arr":[1,2]})");
|
||||
}
|
||||
|
||||
SECTION("Pretty renderer")
|
||||
{
|
||||
JsonRenderer renderer;
|
||||
renderer.Pretty(true);
|
||||
auto renderString = renderer.Render(b);
|
||||
REQUIRE(
|
||||
renderString ==
|
||||
R"({
|
||||
"obj": {
|
||||
"str": "strval",
|
||||
"str2": "str2val"
|
||||
},
|
||||
"arr": [
|
||||
1,
|
||||
2
|
||||
]
|
||||
})");
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче