From ca5772a24c5761315158ede9f8f286c327cb0032 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Mon, 8 Dec 2014 21:14:03 +0545 Subject: [PATCH 01/54] Add project folder and some initial files --- .gitattributes | 63 +++++++ .gitignore | 156 ++++++++++++++++++ MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 80 +++++++++ .../Mirror-Client.vcxproj.filters | 30 ++++ MSVC_2013/Mirror-Server.sln | 28 ++++ MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 80 +++++++++ .../Mirror-Server.vcxproj.filters | 33 ++++ include/common/Exception.h | 8 + include/common/common.h | 5 + src/client/main.cpp | 17 ++ src/server/main.cpp | 16 ++ 11 files changed, 516 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 MSVC_2013/Mirror-Client/Mirror-Client.vcxproj create mode 100644 MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters create mode 100644 MSVC_2013/Mirror-Server.sln create mode 100644 MSVC_2013/Mirror-Server/Mirror-Server.vcxproj create mode 100644 MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters create mode 100644 include/common/Exception.h create mode 100644 include/common/common.h create mode 100644 src/client/main.cpp create mode 100644 src/server/main.cpp diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bc915c --- /dev/null +++ b/.gitignore @@ -0,0 +1,156 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# 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 +*.Publish.xml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# 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 + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj new file mode 100644 index 0000000..5ae0ea3 --- /dev/null +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -0,0 +1,80 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C3B011C9-AB6A-467B-B72D-DBF24A92B057} + MirrorClient + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + $(SolutionDir)\..\include;$(BOOST_ROOT); + + + true + $(BOOST_ROOT)\stage\lib; + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)\..\include;$(BOOST_ROOT); + + + true + true + true + $(BOOST_ROOT)\stage\lib; + + + + + + + + + + + + + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters new file mode 100644 index 0000000..fc1f425 --- /dev/null +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {14bd5b1a-4b66-4a88-a663-982947f38b64} + + + {34aae850-5f2a-4f8f-8199-33782a4f11c7} + + + {d8d67157-9725-4631-85d3-0c58bdf52134} + + + {74895e6c-3a19-4821-9b35-9a46ae109ac1} + + + + + include\common + + + include\common + + + + + src + + + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server.sln b/MSVC_2013/Mirror-Server.sln new file mode 100644 index 0000000..4e988b5 --- /dev/null +++ b/MSVC_2013/Mirror-Server.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mirror-Server", "Mirror-Server\Mirror-Server.vcxproj", "{A0F62CED-26B1-4E23-BFAD-DA6FFA974CEB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mirror-Client", "Mirror-Client\Mirror-Client.vcxproj", "{C3B011C9-AB6A-467B-B72D-DBF24A92B057}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A0F62CED-26B1-4E23-BFAD-DA6FFA974CEB}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0F62CED-26B1-4E23-BFAD-DA6FFA974CEB}.Debug|Win32.Build.0 = Debug|Win32 + {A0F62CED-26B1-4E23-BFAD-DA6FFA974CEB}.Release|Win32.ActiveCfg = Release|Win32 + {A0F62CED-26B1-4E23-BFAD-DA6FFA974CEB}.Release|Win32.Build.0 = Release|Win32 + {C3B011C9-AB6A-467B-B72D-DBF24A92B057}.Debug|Win32.ActiveCfg = Debug|Win32 + {C3B011C9-AB6A-467B-B72D-DBF24A92B057}.Debug|Win32.Build.0 = Debug|Win32 + {C3B011C9-AB6A-467B-B72D-DBF24A92B057}.Release|Win32.ActiveCfg = Release|Win32 + {C3B011C9-AB6A-467B-B72D-DBF24A92B057}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj new file mode 100644 index 0000000..e1a91b1 --- /dev/null +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -0,0 +1,80 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A0F62CED-26B1-4E23-BFAD-DA6FFA974CEB} + MirrorServer + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + $(SolutionDir)\..\include;$(BOOST_ROOT); + + + true + $(BOOST_ROOT)\stage\lib; + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)\..\include;$(BOOST_ROOT); + + + true + true + true + $(BOOST_ROOT)\stage\lib; + + + + + + + + + + + + + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters new file mode 100644 index 0000000..ae78178 --- /dev/null +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {32677475-31fd-4fcd-8faf-e5c1b415d692} + + + {901cda44-2609-4a20-bf53-fff3e0af9875} + + + {468317da-7019-44a9-9204-ec31406d9baf} + + + {34d0d713-51c2-4570-b77a-9ac3a2176ef1} + + + {66d0e2a1-f6cd-48cb-b33c-ec7dbdec73da} + + + + + include\common + + + include\common + + + + + src + + + \ No newline at end of file diff --git a/include/common/Exception.h b/include/common/Exception.h new file mode 100644 index 0000000..011fe05 --- /dev/null +++ b/include/common/Exception.h @@ -0,0 +1,8 @@ + +class Exception : public std::runtime_error +{ +public: + Exception(const std::string& errorString) + : std::runtime_error(errorString) + {} +}; \ No newline at end of file diff --git a/include/common/common.h b/include/common/common.h new file mode 100644 index 0000000..2d47aac --- /dev/null +++ b/include/common/common.h @@ -0,0 +1,5 @@ + +#include +#include + +#include "Exception.h" \ No newline at end of file diff --git a/src/client/main.cpp b/src/client/main.cpp new file mode 100644 index 0000000..caa030d --- /dev/null +++ b/src/client/main.cpp @@ -0,0 +1,17 @@ +/* Client - main.cpp */ + +#include + +//boost test for separate thread +void Thread() +{ + while (true) + std::cout << "From Child Thread" << std::endl; +} +int main() +{ + boost::thread t(Thread); + while (true) + std::cout << "From Main Thread" << std::endl; + return 0; +} \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp new file mode 100644 index 0000000..0246580 --- /dev/null +++ b/src/server/main.cpp @@ -0,0 +1,16 @@ +/* Server - main.cpp */ + +#include +int main() +{ + try + { + throw Exception("Aafnai bariko exception"); + } + catch (Exception &ex) + { + std::cout << ex.what() << std::endl; + } + std::cin.get(); + return 0; +} \ No newline at end of file From 402f5de9d33e78bd222615ec7f28891a976b3ae7 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Mon, 8 Dec 2014 22:17:44 +0545 Subject: [PATCH 02/54] added a couple of lines to test --- src/client/main.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/client/main.cpp b/src/client/main.cpp index caa030d..02ba4e1 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -5,13 +5,17 @@ //boost test for separate thread void Thread() { - while (true) - std::cout << "From Child Thread" << std::endl; + int i=0; + while(i<100) + { + std::cout << "From Child Thread" << std::endl; + i++; + } } int main() { boost::thread t(Thread); - while (true) - std::cout << "From Main Thread" << std::endl; + std::cout << "From Main Thread" << std::endl; + t.join(); return 0; -} \ No newline at end of file +} From 9601316787562b62c9b78e9c33d20069dfd14b1d Mon Sep 17 00:00:00 2001 From: paradox Date: Mon, 8 Dec 2014 22:36:55 +0545 Subject: [PATCH 03/54] aina --- src/server/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/main.cpp b/src/server/main.cpp index 0246580..58028b3 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -5,7 +5,7 @@ int main() { try { - throw Exception("Aafnai bariko exception"); + throw Exception("Aafnai bariko exception, aina herera"); } catch (Exception &ex) { From 1fbf7dabc105e133b57cc7f241bbc03934aa08ef Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Mon, 8 Dec 2014 22:43:45 +0545 Subject: [PATCH 04/54] updated .gitignore for ignoring .out and .o files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 1bc915c..8fb39aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +#linux executables and object files +*.out +*.o + # User-specific files *.suo *.user From 60086fea11f4ab9e8a9e3d77e1f3e6fcb719ca01 Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Tue, 9 Dec 2014 15:04:25 +0545 Subject: [PATCH 05/54] corrected solution name --- MSVC_2013/{Mirror-Server.sln => Mirror.sln} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename MSVC_2013/{Mirror-Server.sln => Mirror.sln} (100%) diff --git a/MSVC_2013/Mirror-Server.sln b/MSVC_2013/Mirror.sln similarity index 100% rename from MSVC_2013/Mirror-Server.sln rename to MSVC_2013/Mirror.sln From 7da37b3e46fc61a956c82ede6977185ceb84c0ad Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Tue, 9 Dec 2014 18:31:16 +0545 Subject: [PATCH 06/54] Add some boost headers to common.h --- include/common/common.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/common/common.h b/include/common/common.h index 2d47aac..aa0601d 100644 --- a/include/common/common.h +++ b/include/common/common.h @@ -1,5 +1,8 @@ #include +#include +#include #include +#include #include "Exception.h" \ No newline at end of file From 1160a78b41ff7bcbb1a6a8ccbff8345a28885cf1 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Tue, 9 Dec 2014 18:54:17 +0545 Subject: [PATCH 07/54] Add an empty RtpStreamer class --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 4 ++++ .../Mirror-Client.vcxproj.filters | 6 +++++ MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 4 ++++ .../Mirror-Server.vcxproj.filters | 6 +++++ include/common/RtpSreamer.h | 15 ++++++++++++ src/common/RtpStreamer.cpp | 23 +++++++++++++++++++ 6 files changed, 58 insertions(+) create mode 100644 include/common/RtpSreamer.h create mode 100644 src/common/RtpStreamer.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 5ae0ea3..acc4d54 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -45,6 +45,7 @@ Disabled true $(SolutionDir)\..\include;$(BOOST_ROOT); + _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true @@ -59,6 +60,7 @@ true true $(SolutionDir)\..\include;$(BOOST_ROOT); + _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true @@ -70,9 +72,11 @@ + + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index fc1f425..98240c0 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -21,10 +21,16 @@ include\common + + include\common + src + + src\common + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index e1a91b1..8475aa9 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -45,6 +45,7 @@ Disabled true $(SolutionDir)\..\include;$(BOOST_ROOT); + _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true @@ -59,6 +60,7 @@ true true $(SolutionDir)\..\include;$(BOOST_ROOT); + _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true @@ -70,8 +72,10 @@ + + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index ae78178..adc2db4 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -24,10 +24,16 @@ include\common + + include\common + src + + src\common + \ No newline at end of file diff --git a/include/common/RtpSreamer.h b/include/common/RtpSreamer.h new file mode 100644 index 0000000..8960673 --- /dev/null +++ b/include/common/RtpSreamer.h @@ -0,0 +1,15 @@ +#pragma once + +class RtpStreamer +{ +public: + RtpStreamer(); + ~RtpStreamer(); + + void Initialize(boost::shared_ptr socket, const boost::asio::ip::address &clientAddress); + void CleanUp(); + +private: + boost::shared_ptr m_socket; + boost::asio::ip::address m_clientAddress; +}; \ No newline at end of file diff --git a/src/common/RtpStreamer.cpp b/src/common/RtpStreamer.cpp new file mode 100644 index 0000000..8855a82 --- /dev/null +++ b/src/common/RtpStreamer.cpp @@ -0,0 +1,23 @@ +#include +#include + + +RtpStreamer::RtpStreamer() +{ + CleanUp(); +} + +RtpStreamer::~RtpStreamer() +{} + +void RtpStreamer::Initialize(boost::shared_ptr socket, const boost::asio::ip::address &clientAddress) +{ + m_socket = socket; + m_clientAddress = clientAddress; +} + +void RtpStreamer::CleanUp() +{ + m_socket.reset(); + m_clientAddress = boost::asio::ip::address(); +} \ No newline at end of file From 8c55aefec111402e72f3e31f60b414666e50dff3 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Tue, 9 Dec 2014 19:38:08 +0545 Subject: [PATCH 08/54] Add RtpTransmitter class --- .../~AutoRecover.Mirror-Server.vcxproj | 84 +++++++++++++++++++ include/common/RtpSreamer.h | 15 ---- include/common/RtpTransmitter.h | 40 +++++++++ src/common/RtpStreamer.cpp | 23 ----- src/common/RtpTransmitter.cpp | 54 ++++++++++++ 5 files changed, 178 insertions(+), 38 deletions(-) create mode 100644 MSVC_2013/Mirror-Server/~AutoRecover.Mirror-Server.vcxproj delete mode 100644 include/common/RtpSreamer.h create mode 100644 include/common/RtpTransmitter.h delete mode 100644 src/common/RtpStreamer.cpp create mode 100644 src/common/RtpTransmitter.cpp diff --git a/MSVC_2013/Mirror-Server/~AutoRecover.Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/~AutoRecover.Mirror-Server.vcxproj new file mode 100644 index 0000000..088229a --- /dev/null +++ b/MSVC_2013/Mirror-Server/~AutoRecover.Mirror-Server.vcxproj @@ -0,0 +1,84 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A0F62CED-26B1-4E23-BFAD-DA6FFA974CEB} + MirrorServer + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + true + $(SolutionDir)\..\include;$(BOOST_ROOT); + _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) + + + true + $(BOOST_ROOT)\stage\lib; + + + + + Level3 + MaxSpeed + true + true + true + $(SolutionDir)\..\include;$(BOOST_ROOT); + _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) + + + true + true + true + $(BOOST_ROOT)\stage\lib; + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/include/common/RtpSreamer.h b/include/common/RtpSreamer.h deleted file mode 100644 index 8960673..0000000 --- a/include/common/RtpSreamer.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -class RtpStreamer -{ -public: - RtpStreamer(); - ~RtpStreamer(); - - void Initialize(boost::shared_ptr socket, const boost::asio::ip::address &clientAddress); - void CleanUp(); - -private: - boost::shared_ptr m_socket; - boost::asio::ip::address m_clientAddress; -}; \ No newline at end of file diff --git a/include/common/RtpTransmitter.h b/include/common/RtpTransmitter.h new file mode 100644 index 0000000..32a17c2 --- /dev/null +++ b/include/common/RtpTransmitter.h @@ -0,0 +1,40 @@ +#pragma once + +class RtpTransmissionException : public Exception +{ +public: + RtpTransmissionException(const std::string &errorString) + : Exception("RTP Transmission Error: " + errorString) + {} +}; + +class RtpTransmitter +{ +public: + RtpTransmitter(); + ~RtpTransmitter(); + + void Initialize(boost::shared_ptr socket, const boost::asio::ip::udp::endpoint &clientEndpoint); + void CleanUp(); + + int GetTimeStamp() const { return m_timeStamp; } + void SetTimeStamp(int timeStamp) { m_timeStamp = timeStamp; } + + int GetTimeStampIncrement() const { return m_timeStampIncrement; } + void SetTimeStampIncrement(int timeStampIncrement) { m_timeStampIncrement = timeStampIncrement; } + + uint16_t GetSequenceNumber() const { return m_sequenceNumber; } + void SetSequenceNumber(uint16_t sequenceNumber) { m_sequenceNumber = sequenceNumber; } + + uint8_t GetPayloadType() const { return m_payloadType; } + void SetPayloadType(uint8_t payloadType) { m_payloadType = payloadType; } + + void Send(const std::vector &data); +private: + boost::shared_ptr m_socket; + boost::asio::ip::udp::endpoint m_clientEndpoint; + + uint16_t m_sequenceNumber; + int m_timeStamp, m_timeStampIncrement; + uint8_t m_payloadType; +}; \ No newline at end of file diff --git a/src/common/RtpStreamer.cpp b/src/common/RtpStreamer.cpp deleted file mode 100644 index 8855a82..0000000 --- a/src/common/RtpStreamer.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include - - -RtpStreamer::RtpStreamer() -{ - CleanUp(); -} - -RtpStreamer::~RtpStreamer() -{} - -void RtpStreamer::Initialize(boost::shared_ptr socket, const boost::asio::ip::address &clientAddress) -{ - m_socket = socket; - m_clientAddress = clientAddress; -} - -void RtpStreamer::CleanUp() -{ - m_socket.reset(); - m_clientAddress = boost::asio::ip::address(); -} \ No newline at end of file diff --git a/src/common/RtpTransmitter.cpp b/src/common/RtpTransmitter.cpp new file mode 100644 index 0000000..d2bbf7d --- /dev/null +++ b/src/common/RtpTransmitter.cpp @@ -0,0 +1,54 @@ +#include +#include + +#define RTP_HEADER_SIZE 12 + +RtpTransmitter::RtpTransmitter() +{ + CleanUp(); +} + +RtpTransmitter::~RtpTransmitter() +{} + +void RtpTransmitter::Initialize(boost::shared_ptr socket, const boost::asio::ip::udp::endpoint &clientEndpoint) +{ + m_socket = socket; + m_clientEndpoint = clientEndpoint; + m_sequenceNumber = m_timeStamp = 0; + m_timeStampIncrement = 3600; // for 25fps video +} + +void RtpTransmitter::CleanUp() +{ + m_socket.reset(); + m_clientEndpoint = boost::asio::ip::udp::endpoint(); +} + +void RtpTransmitter::Send(const std::vector &data) +{ + if (!m_socket) + throw RtpTransmissionException("RTP Transmitter hasn't been properly initialized"); + + std::vector packet; + packet.resize(RTP_HEADER_SIZE); + packet[0] = 0x80; // RTP version + packet[1] = m_payloadType; // Payload type + packet[2] = m_sequenceNumber >> 8; // Sequence number of packet + packet[3] = m_sequenceNumber & 0x0FF; + packet[4] = (m_timeStamp & 0xFF000000) >> 24; // Timestamp + packet[5] = (m_timeStamp & 0x00FF0000) >> 16; + packet[6] = (m_timeStamp & 0x0000FF00) >> 8; + packet[7] = (m_timeStamp & 0x000000FF); + packet[8] = 0x13; // 4 byte SSRC (sychronization source identifier) + packet[9] = 0xf9; // we just an arbitrary number here to keep it simple + packet[10] = 0x7e; + packet[11] = 0x67; + + packet.insert(packet.end(), data.begin(), data.end()); + + m_sequenceNumber++; + m_timeStamp += m_timeStampIncrement; + + m_socket->send_to(boost::asio::buffer(packet), m_clientEndpoint); +} \ No newline at end of file From 4ea324adbffb08bc6fff4120e385492da71d2668 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Tue, 9 Dec 2014 21:14:37 +0545 Subject: [PATCH 09/54] Add RtpReceiver class --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 7 +++- .../Mirror-Client.vcxproj.filters | 13 +++++- MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 7 +++- .../Mirror-Server.vcxproj.filters | 13 +++++- include/common/RTPCommon.h | 3 ++ include/common/RtpReceiver.h | 40 +++++++++++++++++++ include/common/RtpTransmitter.h | 7 ++-- include/common/common.h | 5 ++- src/common/RtpReceiver.cpp | 40 +++++++++++++++++++ src/common/RtpTransmitter.cpp | 25 ++++++------ 10 files changed, 135 insertions(+), 25 deletions(-) create mode 100644 include/common/RTPCommon.h create mode 100644 include/common/RtpReceiver.h create mode 100644 src/common/RtpReceiver.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index acc4d54..30ef0ff 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -72,11 +72,14 @@ - + + + - + + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 98240c0..2c0c418 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -21,7 +21,13 @@ include\common - + + include\common + + + include\common + + include\common @@ -29,7 +35,10 @@ src - + + src\common + + src\common diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index 8475aa9..299baa5 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -72,10 +72,13 @@ - + + + - + + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index adc2db4..b1f8a5a 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -24,7 +24,13 @@ include\common - + + include\common + + + include\common + + include\common @@ -32,7 +38,10 @@ src - + + src\common + + src\common diff --git a/include/common/RTPCommon.h b/include/common/RTPCommon.h new file mode 100644 index 0000000..937283b --- /dev/null +++ b/include/common/RTPCommon.h @@ -0,0 +1,3 @@ +#pragma once + +#define RTP_HEADER_SIZE 12 \ No newline at end of file diff --git a/include/common/RtpReceiver.h b/include/common/RtpReceiver.h new file mode 100644 index 0000000..e872ddb --- /dev/null +++ b/include/common/RtpReceiver.h @@ -0,0 +1,40 @@ +#pragma once +#include "RTPCommon.h" + +class RtpReceptionError : public Exception +{ +public: + RtpReceptionError(std::string errorString) + : Exception("RTP Reception Error: " + errorString) + {} +}; + +class RtpReceiver +{ +public: + RtpReceiver(); + ~RtpReceiver(); + + void Initialize(boost::shared_ptr socket); + void CleanUp(); + + int GetTimeStamp() const { return m_timeStamp; } + void SetTimeStamp(int timeStamp) { m_timeStamp = timeStamp; } + + uint16_t GetSequenceNumber() const { return m_sequenceNumber; } + void SetSequenceNumber(uint16_t sequenceNumber) { m_sequenceNumber = sequenceNumber; } + + uint8_t GetPayloadType() const { return m_payloadType; } + void SetPayloadType(uint8_t payloadType) { m_payloadType = payloadType; } + + const udp::endpoint& GetSource() const { return m_source; } + + void Receive(std::vector &data, size_t maxSize = 1500); +private: + boost::shared_ptr m_socket; + udp::endpoint m_source; + + uint16_t m_sequenceNumber; + int m_timeStamp; + uint8_t m_payloadType; +}; \ No newline at end of file diff --git a/include/common/RtpTransmitter.h b/include/common/RtpTransmitter.h index 32a17c2..282feed 100644 --- a/include/common/RtpTransmitter.h +++ b/include/common/RtpTransmitter.h @@ -1,4 +1,5 @@ #pragma once +#include "RTPCommon.h" class RtpTransmissionException : public Exception { @@ -14,7 +15,7 @@ class RtpTransmitter RtpTransmitter(); ~RtpTransmitter(); - void Initialize(boost::shared_ptr socket, const boost::asio::ip::udp::endpoint &clientEndpoint); + void Initialize(boost::shared_ptr socket, const udp::endpoint &destination); void CleanUp(); int GetTimeStamp() const { return m_timeStamp; } @@ -31,8 +32,8 @@ class RtpTransmitter void Send(const std::vector &data); private: - boost::shared_ptr m_socket; - boost::asio::ip::udp::endpoint m_clientEndpoint; + boost::shared_ptr m_socket; + udp::endpoint m_destination; uint16_t m_sequenceNumber; int m_timeStamp, m_timeStampIncrement; diff --git a/include/common/common.h b/include/common/common.h index aa0601d..3e68e1d 100644 --- a/include/common/common.h +++ b/include/common/common.h @@ -5,4 +5,7 @@ #include #include -#include "Exception.h" \ No newline at end of file +#include "Exception.h" + +using boost::asio::ip::udp; +using boost::asio::ip::tcp; \ No newline at end of file diff --git a/src/common/RtpReceiver.cpp b/src/common/RtpReceiver.cpp new file mode 100644 index 0000000..780b394 --- /dev/null +++ b/src/common/RtpReceiver.cpp @@ -0,0 +1,40 @@ +#include +#include + +RtpReceiver::RtpReceiver() +{} + +RtpReceiver::~RtpReceiver() +{ + CleanUp(); +} + +void RtpReceiver::Initialize(boost::shared_ptr socket) +{ + m_socket = socket; + m_sequenceNumber = m_timeStamp = 0; +} + +void RtpReceiver::CleanUp() +{ + m_socket.reset(); +} + +void RtpReceiver::Receive(std::vector &data, size_t maxSize) +{ + if (!m_socket) + throw RtpReceptionError("RTP Receiver hasn't been properly initialized"); + + std::vector packet; + packet.resize(maxSize); + size_t len = m_socket->receive_from(boost::asio::buffer(data), m_source); + packet.resize(len); + + char version = packet[0]; + m_payloadType = packet[1]; + m_sequenceNumber = *((uint16_t*)&packet[2]); + m_timeStamp = *((int*)&packet[4]); + int SSRC = *((int*)&packet[8]); + + data.assign(packet.begin() + RTP_HEADER_SIZE, packet.end()); +} \ No newline at end of file diff --git a/src/common/RtpTransmitter.cpp b/src/common/RtpTransmitter.cpp index d2bbf7d..4f62659 100644 --- a/src/common/RtpTransmitter.cpp +++ b/src/common/RtpTransmitter.cpp @@ -1,20 +1,19 @@ #include #include -#define RTP_HEADER_SIZE 12 - RtpTransmitter::RtpTransmitter() { - CleanUp(); } RtpTransmitter::~RtpTransmitter() -{} +{ + CleanUp(); +} -void RtpTransmitter::Initialize(boost::shared_ptr socket, const boost::asio::ip::udp::endpoint &clientEndpoint) +void RtpTransmitter::Initialize(boost::shared_ptr socket, const udp::endpoint &destination) { m_socket = socket; - m_clientEndpoint = clientEndpoint; + m_destination = destination; m_sequenceNumber = m_timeStamp = 0; m_timeStampIncrement = 3600; // for 25fps video } @@ -22,7 +21,7 @@ void RtpTransmitter::Initialize(boost::shared_ptr void RtpTransmitter::CleanUp() { m_socket.reset(); - m_clientEndpoint = boost::asio::ip::udp::endpoint(); + m_destination = udp::endpoint(); } void RtpTransmitter::Send(const std::vector &data) @@ -32,7 +31,7 @@ void RtpTransmitter::Send(const std::vector &data) std::vector packet; packet.resize(RTP_HEADER_SIZE); - packet[0] = 0x80; // RTP version + packet[0] = (char)0x80; // RTP version packet[1] = m_payloadType; // Payload type packet[2] = m_sequenceNumber >> 8; // Sequence number of packet packet[3] = m_sequenceNumber & 0x0FF; @@ -40,15 +39,15 @@ void RtpTransmitter::Send(const std::vector &data) packet[5] = (m_timeStamp & 0x00FF0000) >> 16; packet[6] = (m_timeStamp & 0x0000FF00) >> 8; packet[7] = (m_timeStamp & 0x000000FF); - packet[8] = 0x13; // 4 byte SSRC (sychronization source identifier) - packet[9] = 0xf9; // we just an arbitrary number here to keep it simple - packet[10] = 0x7e; - packet[11] = 0x67; + packet[8] = (char)0x13; // 4 byte SSRC (sychronization source identifier) + packet[9] = (char)0xf9; // we just an arbitrary number here to keep it simple + packet[10] = (char)0x7e; + packet[11] = (char)0x67; packet.insert(packet.end(), data.begin(), data.end()); m_sequenceNumber++; m_timeStamp += m_timeStampIncrement; - m_socket->send_to(boost::asio::buffer(packet), m_clientEndpoint); + m_socket->send_to(boost::asio::buffer(packet), m_destination); } \ No newline at end of file From ad69dc6b83430bcbf83c470076bce97110ed178c Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Tue, 9 Dec 2014 21:40:12 +0545 Subject: [PATCH 10/54] created a class tcpconnection but zero members --- include/common/TCPConnection.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 include/common/TCPConnection.h diff --git a/include/common/TCPConnection.h b/include/common/TCPConnection.h new file mode 100644 index 0000000..d2d16e3 --- /dev/null +++ b/include/common/TCPConnection.h @@ -0,0 +1,17 @@ +#ifndef TCP_CONNECTION +#define TCP_CONNECTION + +using boost::asio::ip::tcp; + +class TCPConnection +{ + public: + TCPConnection(boost::asio::io_service&); + // member functions + + private: + tcp::endpoint m_clientEndpoint; + tcp::endpoint m_serverEndpoint; + + +#endif From be35cb3c5909bdcc10aa8bc689afe75044698422 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Wed, 10 Dec 2014 08:04:16 +0545 Subject: [PATCH 11/54] created class TcpTransmitter with very few functions --- include/common/TCPConnection.h | 17 ----------------- include/common/TcpTransmitter.h | 25 +++++++++++++++++++++++++ src/common/TcpTransmitter.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 17 deletions(-) delete mode 100644 include/common/TCPConnection.h create mode 100644 include/common/TcpTransmitter.h create mode 100644 src/common/TcpTransmitter.cpp diff --git a/include/common/TCPConnection.h b/include/common/TCPConnection.h deleted file mode 100644 index d2d16e3..0000000 --- a/include/common/TCPConnection.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TCP_CONNECTION -#define TCP_CONNECTION - -using boost::asio::ip::tcp; - -class TCPConnection -{ - public: - TCPConnection(boost::asio::io_service&); - // member functions - - private: - tcp::endpoint m_clientEndpoint; - tcp::endpoint m_serverEndpoint; - - -#endif diff --git a/include/common/TcpTransmitter.h b/include/common/TcpTransmitter.h new file mode 100644 index 0000000..9057e56 --- /dev/null +++ b/include/common/TcpTransmitter.h @@ -0,0 +1,25 @@ +#pragma once + +class TcpTransmissionException : public Exception +{ + public: + TcpTransmissionException(const std::string &errorString) + : Exception("TCP Transmission Error: " + errorString) + {} +}; + +class TcpTransmitter +{ + public: + TcpTransmitter(); + ~TcpTransmitter(); + + void Initialize(boost::shared_ptr socket, + const tcp::endpoint & destination); + + void Send(const std::vector &data); + + private: + boost::shared_ptr m_socket; + tcp::endpoint m_destination; +} diff --git a/src/common/TcpTransmitter.cpp b/src/common/TcpTransmitter.cpp new file mode 100644 index 0000000..5cb560b --- /dev/null +++ b/src/common/TcpTransmitter.cpp @@ -0,0 +1,25 @@ +#include +#include + +TcpTransmitter::TcpTransmitter() +{ +} + +TcpTransmitter::~TcpTransmitter() +{ +} + + +void TcpTransmitter::Initialize(boost::shared_ptr socket, const udp::endpoint &destination) +{ + m_socket = socket; + m_destination = destination; +} + +void TcpTransmitter::Send(const std::vector &data) +{ + if(!m_socket) + throw TcpTransmissionException("TCP Transmitter hasn't been properly initialized"); + + m_socket->send(boost::asio::buffer(data, data.size())); +} From 3b76e7721700e7645e136513eeecee9556b50e3c Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Wed, 10 Dec 2014 19:03:35 +0545 Subject: [PATCH 12/54] completed FrameRenderer class --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 11 ++- .../Mirror-Client.vcxproj.filters | 6 ++ include/client/FrameRenderer.h | 31 ++++++++ src/client/FrameRenderer.cpp | 39 +++++++++++ src/client/main.cpp | 70 +++++++++++++++---- 5 files changed, 141 insertions(+), 16 deletions(-) create mode 100644 include/client/FrameRenderer.h create mode 100644 src/client/FrameRenderer.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 5ae0ea3..0326494 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -38,17 +38,20 @@ - + + $(ExecutablePath) + $(VC_IncludePath);$(WindowsSDK_IncludePath);$( + Level3 Disabled true - $(SolutionDir)\..\include;$(BOOST_ROOT); + $(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo true - $(BOOST_ROOT)\stage\lib; + $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib; @@ -68,10 +71,12 @@ + + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index fc1f425..63e4562 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -21,10 +21,16 @@ include\common + + include + src + + src + \ No newline at end of file diff --git a/include/client/FrameRenderer.h b/include/client/FrameRenderer.h new file mode 100644 index 0000000..48bf765 --- /dev/null +++ b/include/client/FrameRenderer.h @@ -0,0 +1,31 @@ +#ifndef __FRAMERENDERER__ +#define __FRAMERENDERER__ + +#include +#include + +class FrameRenderer +{ +public: + FrameRenderer() :m_parentWindow(0), m_drawingArea(0), m_rgbData(0), m_rgbImage(0){} + void SetRGBData(unsigned char* rgbData); + void Initialize(GtkWidget* parentWindow, int x, int y, int fw, int fh); + static gboolean OnDraw(GtkWidget* widget, cairo_t* cr, gpointer frPointer); +private: + GtkWidget* m_parentWindow; + GtkWidget* m_fixed; + GtkWidget *m_drawingArea; + int m_x, m_y, m_fw, m_fh; + cairo_surface_t *m_rgbImage; + unsigned char* m_rgbData; +}; + + + + + + + + + +#endif \ No newline at end of file diff --git a/src/client/FrameRenderer.cpp b/src/client/FrameRenderer.cpp new file mode 100644 index 0000000..bd8c9d8 --- /dev/null +++ b/src/client/FrameRenderer.cpp @@ -0,0 +1,39 @@ +#include "client/FrameRenderer.h" + +#include + +void FrameRenderer::SetRGBData(unsigned char* rgbData) +{ + memcpy(m_rgbData, rgbData, m_fw*m_fh*4); +} + +gboolean FrameRenderer::OnDraw(GtkWidget* widget, cairo_t* cr, gpointer frPointer) +{ + FrameRenderer* fr = (FrameRenderer*)frPointer; + cairo_set_source_surface(cr, fr->m_rgbImage, 0, 0); + cairo_paint(cr); + return FALSE; +} + +void FrameRenderer::Initialize(GtkWidget* parentWindow, int x, int y, int fw, int fh) +{ + m_x = x; + m_y = y; + m_fw = fw; + m_fh = fh; + m_parentWindow = parentWindow; + + if (m_drawingArea) delete m_drawingArea; + if (m_rgbData) delete m_rgbData; + if (m_rgbImage) cairo_surface_destroy(m_rgbImage); + + + m_rgbData = new unsigned char[4*m_fw*m_fh]; + m_drawingArea = gtk_drawing_area_new(); + m_rgbImage = cairo_image_surface_create_for_data(m_rgbData, CAIRO_FORMAT_ARGB32, m_fw, m_fh, m_fw*4); + m_fixed = gtk_fixed_new(); + gtk_widget_set_size_request(m_drawingArea, fw, fh); + gtk_container_add(GTK_CONTAINER(m_parentWindow), m_fixed); + g_signal_connect(G_OBJECT(m_drawingArea), "draw", G_CALLBACK(this->OnDraw), this); + gtk_fixed_put(GTK_FIXED(m_fixed), m_drawingArea, m_x, m_y); +} \ No newline at end of file diff --git a/src/client/main.cpp b/src/client/main.cpp index 02ba4e1..f298db7 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -3,19 +3,63 @@ #include //boost test for separate thread -void Thread() -{ - int i=0; - while(i<100) - { - std::cout << "From Child Thread" << std::endl; - i++; - } -} -int main() +//void Thread() +//{ +// int i=0; +// while(i<100) +// { +// std::cout << "From Child Thread" << std::endl; +// i++; +// } +//} +//int main() +//{ +// boost::thread t(Thread); +// std::cout << "From Main Thread" << std::endl; +// t.join(); +// return 0; +//} + +#include + +#include "client/FrameRenderer.h" + +#ifdef _WIN32 +#pragma comment(lib, "gtk-win32-3.0.lib") +#pragma comment(lib, "gobject-2.0.lib") +#pragma comment(lib, "cairo.lib") +#endif + + +int main(int argc, char *argv[]) { - boost::thread t(Thread); - std::cout << "From Main Thread" << std::endl; - t.join(); + + GtkWidget *window; + + gtk_init(&argc, &argv); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + + gtk_window_set_default_size(GTK_WINDOW(window), 1000, 600); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_widget_set_size_request(window, 1000, 600); + + FrameRenderer fr; + fr.Initialize(window, 100, 100, 256, 256); + unsigned char* frameData = new unsigned char[256 * 256 * 4]; + for (int i = 0; i < 256 * 256; i++) + { + frameData[4 * i + 0] = i % 256; + frameData[4 * i + 1] = 0; + frameData[4 * i + 2] = 0; + frameData[4 * i + 3] = 255; + } + fr.SetRGBData(frameData); + delete frameData; + gtk_widget_show_all(window); + + gtk_main(); + return 0; } From 72e04284867165d00dda36045b2c4a342b95bb67 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Wed, 10 Dec 2014 20:17:20 +0545 Subject: [PATCH 13/54] created TcpTransmitter and TcpReceiver, and created header for TcpHandler(Chat) --- include/common/TcpHandler.h | 36 ++++++++++++++++++++++++++++++ include/common/TcpReceiver.h | 25 +++++++++++++++++++++ include/common/TcpTransmitter.h | 8 +++---- src/common/TcpReceiver.cpp | 39 +++++++++++++++++++++++++++++++++ src/common/TcpTransmitter.cpp | 2 +- 5 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 include/common/TcpHandler.h create mode 100644 include/common/TcpReceiver.h create mode 100644 src/common/TcpReceiver.cpp diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h new file mode 100644 index 0000000..e3b8bbd --- /dev/null +++ b/include/common/TcpHandler.h @@ -0,0 +1,36 @@ +#pragma once + +class TcpHandlerException : public Exception +{ + public: + TcpHandlerException(const std::string &errorString) : + Exception("TCP Handler Error: " + errorString) + {} +}; + + +class TcpHandler +{ + public: + TcpHandler(); + ~Tcphandler(); + + // initialize with a socket + Initialize(boost::shared_ptr socket); + // send tcp request to the endpoint and create socket + Initialize(tcp::endpoint &endpoint); + + // initial request for creating connection + Request(tcp::endpoint &endpoint); + + Send(const std::vector & data); + + Receive(const std::vector & data); + + private: + boost::shared_ptr m_socket; + boost::shared_ptr m_destination; + boost::shared_ptr m_transmitter; + boost::shared_ptr m_receiver; + const int m_port; +}; diff --git a/include/common/TcpReceiver.h b/include/common/TcpReceiver.h new file mode 100644 index 0000000..2f35b24 --- /dev/null +++ b/include/common/TcpReceiver.h @@ -0,0 +1,25 @@ +#pragma once + +class TcpReceiverException: public Exception +{ + public: + TcpReceiverException(const std::string &errorString) : + Exception("TCP Receiver Error: " + errorString) + {} +}; + +class TcpReceiver +{ + public: + TcpReceiver(); + ~TcpReceiver(); + + void Initialize(boost::shared_ptr socket, + const tcp::endpoint &destination); + + void Receive(std::vector &data); + + private: + boost::shared_ptr m_socket; + tcp::endpoint m_destination; +}; diff --git a/include/common/TcpTransmitter.h b/include/common/TcpTransmitter.h index 9057e56..030ff51 100644 --- a/include/common/TcpTransmitter.h +++ b/include/common/TcpTransmitter.h @@ -1,9 +1,9 @@ #pragma once -class TcpTransmissionException : public Exception +class TcpTransmitterException: public Exception { public: - TcpTransmissionException(const std::string &errorString) + TcpTransmitterException(const std::string &errorString) : Exception("TCP Transmission Error: " + errorString) {} }; @@ -15,11 +15,11 @@ class TcpTransmitter ~TcpTransmitter(); void Initialize(boost::shared_ptr socket, - const tcp::endpoint & destination); + const tcp::endpoint &destination); void Send(const std::vector &data); private: boost::shared_ptr m_socket; tcp::endpoint m_destination; -} +}; diff --git a/src/common/TcpReceiver.cpp b/src/common/TcpReceiver.cpp new file mode 100644 index 0000000..30f3f27 --- /dev/null +++ b/src/common/TcpReceiver.cpp @@ -0,0 +1,39 @@ +#include +#include + +TcpReceiver::TcpReceiver() +{ +} + +TcpReceiver::~TcpReceiver() +{ +} + +void TcpReceiver::Initialize(boost::shared_ptr socket, const udp::endpoint &destination) +{ + m_socket = socket; + m_destination = destination; +} + +void TcpReceiver::Receive(std::vector &data) +{ + if(!m_socket) + throw TcpReceiverException("TCP Receiver hasn't been properly initialized"); + + boost::system::error_code error; + size_t len = m_socket->read_some(boost::asio::buffer(data), error); + + if (error == boost::asio::error::eof) + { + // Connection closed by the peer + // do something + } + else if (error) // some other error + { + throw boost::system::system_error(error); + } + // do something with the data + // for now, just display the data + std::cout.write(data.data(), len); +} + diff --git a/src/common/TcpTransmitter.cpp b/src/common/TcpTransmitter.cpp index 5cb560b..11b728d 100644 --- a/src/common/TcpTransmitter.cpp +++ b/src/common/TcpTransmitter.cpp @@ -19,7 +19,7 @@ void TcpTransmitter::Initialize(boost::shared_ptr socket, const udp void TcpTransmitter::Send(const std::vector &data) { if(!m_socket) - throw TcpTransmissionException("TCP Transmitter hasn't been properly initialized"); + throw TcpTransmitterException("TCP Transmitter hasn't been properly initialized"); m_socket->send(boost::asio::buffer(data, data.size())); } From d684b590fb3e4a86cd98004953aa593adacfb222 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Wed, 10 Dec 2014 20:56:18 +0545 Subject: [PATCH 14/54] Set paths for GTK for Release Configuration --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 9580b4d..331a460 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -48,6 +48,7 @@ Disabled true $(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo + _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true @@ -61,14 +62,14 @@ true true true - $(SolutionDir)\..\include;$(BOOST_ROOT); + $(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true true true - $(BOOST_ROOT)\stage\lib; + $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib; From 2ba10329d45b380115c5675d972d8378685db315 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Wed, 10 Dec 2014 22:59:09 +0545 Subject: [PATCH 15/54] Added a makefile(empty) and TcpHandler.cpp(some code written) --- makefile | 0 src/common/TcpHandler.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 makefile create mode 100644 src/common/TcpHandler.cpp diff --git a/makefile b/makefile new file mode 100644 index 0000000..e69de29 diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp new file mode 100644 index 0000000..fc16e48 --- /dev/null +++ b/src/common/TcpHandler.cpp @@ -0,0 +1,27 @@ +#include +#include + +TcpHandler::TcpHandler() : +{ +} + +TcpHandler::~TcpHandler() +{ +} + +TcpHandler::Initialize(boost::shared_ptr socket) +{ + m_socket = socket; +} + +TcpHandler::Request(tcp::endpoint & destEndpoint) +{ + // since the host can have multiple addresses, we iterate + // through the endpoints + + // if the connect fails, and the socket was already opened, + // the socket is not returned to the closed state + + boost::system::error_code error; + socket->connect(destEndpoint, error); + From 600c8f8733061081cb8b6097edb9b97f14097598 Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Thu, 11 Dec 2014 17:51:32 +0545 Subject: [PATCH 16/54] Some work on Video Capturing --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 13 +++-- .../Mirror-Client.vcxproj.filters | 24 ++++---- include/client/FrameRenderer.h | 2 +- include/common/Exception.h | 6 +- include/common/common.h | 4 ++ src/client/FrameRenderer.cpp | 1 + src/client/VideoCapture.cpp | 33 +++++++++++ src/client/main.cpp | 56 +++++++------------ 8 files changed, 80 insertions(+), 59 deletions(-) create mode 100644 src/client/VideoCapture.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 9580b4d..f909234 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -40,18 +40,19 @@ $(ExecutablePath) - $(VC_IncludePath);$(WindowsSDK_IncludePath);$( + $(VC_IncludePath);$(WindowsSDK_IncludePath) Level3 Disabled true - $(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo + $(OPENCV_ROOT)\build\include;$(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo + _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true - $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib; + $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib;$(OPENCV_ROOT)\lib; @@ -61,18 +62,19 @@ true true true - $(SolutionDir)\..\include;$(BOOST_ROOT); + $(OPENCV_ROOT)\build\include;$(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true true true - $(BOOST_ROOT)\stage\lib; + $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib;$(OPENCV_ROOT)\lib; + @@ -82,6 +84,7 @@ + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 2547343..efdff33 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -24,28 +24,24 @@ include - - include\common - - - include\common - - - include\common + + + + + include src - - src\common - - - src\common - src + + + + src + \ No newline at end of file diff --git a/include/client/FrameRenderer.h b/include/client/FrameRenderer.h index 48bf765..0ab2834 100644 --- a/include/client/FrameRenderer.h +++ b/include/client/FrameRenderer.h @@ -1,7 +1,7 @@ #ifndef __FRAMERENDERER__ #define __FRAMERENDERER__ -#include +#include #include class FrameRenderer diff --git a/include/common/Exception.h b/include/common/Exception.h index 011fe05..6d32ac5 100644 --- a/include/common/Exception.h +++ b/include/common/Exception.h @@ -1,8 +1,10 @@ - +#ifndef __EXCEPTION__ +#define __EXCEPTION__ class Exception : public std::runtime_error { public: Exception(const std::string& errorString) : std::runtime_error(errorString) {} -}; \ No newline at end of file +}; +#endif \ No newline at end of file diff --git a/include/common/common.h b/include/common/common.h index 3e68e1d..4bef910 100644 --- a/include/common/common.h +++ b/include/common/common.h @@ -5,6 +5,10 @@ #include #include +#include +#include + + #include "Exception.h" using boost::asio::ip::udp; diff --git a/src/client/FrameRenderer.cpp b/src/client/FrameRenderer.cpp index bd8c9d8..0c38ed3 100644 --- a/src/client/FrameRenderer.cpp +++ b/src/client/FrameRenderer.cpp @@ -5,6 +5,7 @@ void FrameRenderer::SetRGBData(unsigned char* rgbData) { memcpy(m_rgbData, rgbData, m_fw*m_fh*4); + gtk_widget_queue_draw_area(m_drawingArea, 0, 0, m_fw, m_fh); } gboolean FrameRenderer::OnDraw(GtkWidget* widget, cairo_t* cr, gpointer frPointer) diff --git a/src/client/VideoCapture.cpp b/src/client/VideoCapture.cpp new file mode 100644 index 0000000..2db3124 --- /dev/null +++ b/src/client/VideoCapture.cpp @@ -0,0 +1,33 @@ +#include "client/VideoCapture.h" + + + +std::vector VideoCapture::Enumerate() +{ + + //Apparently it is not possible due to cross-flatform issue + //So, I guess we have to do it individually for different platforms + //without using opencv + + std::vector capDevices; + return capDevices; + +} + +void VideoCapture::Initialize(int deviceID) +{ + m_captureDevice.open(deviceID); + if (!m_captureDevice.isOpened()) throw Exception("error opening the video capturing device"); + m_fw = static_cast(m_captureDevice.get(CV_CAP_PROP_FRAME_WIDTH)); + m_fh = static_cast(m_captureDevice.get(CV_CAP_PROP_FRAME_HEIGHT)); +} + +int VideoCapture::GetFrameWidth(){ return m_fw; } +int VideoCapture::GetFrameHeight(){ return m_fh; } + +unsigned char* VideoCapture::GetBGRAFrame() +{ + m_captureDevice >> m_capturedFrame; + cv::cvtColor(m_capturedFrame, m_capturedFrame, CV_RGB2RGBA, 4); + return m_capturedFrame.data; +} \ No newline at end of file diff --git a/src/client/main.cpp b/src/client/main.cpp index f298db7..663f07e 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -1,62 +1,44 @@ /* Client - main.cpp */ #include - -//boost test for separate thread -//void Thread() -//{ -// int i=0; -// while(i<100) -// { -// std::cout << "From Child Thread" << std::endl; -// i++; -// } -//} -//int main() -//{ -// boost::thread t(Thread); -// std::cout << "From Main Thread" << std::endl; -// t.join(); -// return 0; -//} - -#include - #include "client/FrameRenderer.h" +#include "client/VideoCapture.h" #ifdef _WIN32 #pragma comment(lib, "gtk-win32-3.0.lib") #pragma comment(lib, "gobject-2.0.lib") #pragma comment(lib, "cairo.lib") +#pragma comment(lib, "glib-2.0.lib") +#pragma comment(lib, "opencv_highgui245.lib") +#pragma comment(lib, "opencv_core245.lib") +#pragma comment(lib, "opencv_imgproc245.lib") #endif +FrameRenderer fr; +VideoCapture vidCap; + + +gboolean IdleFunction(gpointer userData) +{ + fr.SetRGBData(vidCap.GetBGRAFrame()); + return TRUE; +} int main(int argc, char *argv[]) { - GtkWidget *window; - gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_default_size(GTK_WINDOW(window), 1000, 600); gtk_window_set_resizable(GTK_WINDOW(window), FALSE); gtk_widget_set_size_request(window, 1000, 600); - - FrameRenderer fr; - fr.Initialize(window, 100, 100, 256, 256); - unsigned char* frameData = new unsigned char[256 * 256 * 4]; - for (int i = 0; i < 256 * 256; i++) - { - frameData[4 * i + 0] = i % 256; - frameData[4 * i + 1] = 0; - frameData[4 * i + 2] = 0; - frameData[4 * i + 3] = 255; - } - fr.SetRGBData(frameData); - delete frameData; + g_signal_connect_swapped(G_OBJECT(window), "destroy", + G_CALLBACK(gtk_main_quit), NULL); + g_idle_add(IdleFunction, 0); + vidCap.Initialize(); + fr.Initialize(window, 10, 10, vidCap.GetFrameWidth(), vidCap.GetFrameHeight()); gtk_widget_show_all(window); gtk_main(); From 84e9e44f5f92e3e13394f1a1d9eae38bf20acd09 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Thu, 11 Dec 2014 18:10:22 +0545 Subject: [PATCH 17/54] Add TcpListener --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 7 ++++ .../Mirror-Client.vcxproj.filters | 21 +++++++++++ .../~AutoRecover.Mirror-Client.vcxproj} | 31 ++++++++++++---- MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 5 +++ .../Mirror-Server.vcxproj.filters | 15 ++++++++ include/common/TcpListener.h | 26 +++++++++++++ include/common/TcpTransmitter.h | 6 +-- src/common/TcpListener.cpp | 37 +++++++++++++++++++ src/common/TcpReceiver.cpp | 2 +- src/common/TcpTransmitter.cpp | 2 +- 10 files changed, 138 insertions(+), 14 deletions(-) rename MSVC_2013/{Mirror-Server/~AutoRecover.Mirror-Server.vcxproj => Mirror-Client/~AutoRecover.Mirror-Client.vcxproj} (64%) create mode 100644 include/common/TcpListener.h create mode 100644 src/common/TcpListener.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 331a460..2ef3707 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -79,12 +79,19 @@ + + + + + + + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 2547343..4e49b93 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -33,6 +33,18 @@ include\common + + include\common + + + include\common + + + include\common + + + include\common + @@ -47,5 +59,14 @@ src + + src\common + + + src\common + + + src\common + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/~AutoRecover.Mirror-Server.vcxproj b/MSVC_2013/Mirror-Client/~AutoRecover.Mirror-Client.vcxproj similarity index 64% rename from MSVC_2013/Mirror-Server/~AutoRecover.Mirror-Server.vcxproj rename to MSVC_2013/Mirror-Client/~AutoRecover.Mirror-Client.vcxproj index 088229a..a6d6fff 100644 --- a/MSVC_2013/Mirror-Server/~AutoRecover.Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Client/~AutoRecover.Mirror-Client.vcxproj @@ -11,8 +11,8 @@ - {A0F62CED-26B1-4E23-BFAD-DA6FFA974CEB} - MirrorServer + {C3B011C9-AB6A-467B-B72D-DBF24A92B057} + MirrorClient @@ -38,18 +38,21 @@ - + + $(ExecutablePath) + $(VC_IncludePath);$(WindowsSDK_IncludePath);$( + Level3 Disabled true - $(SolutionDir)\..\include;$(BOOST_ROOT); + $(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true - $(BOOST_ROOT)\stage\lib; + $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib; @@ -59,24 +62,36 @@ true true true - $(SolutionDir)\..\include;$(BOOST_ROOT); + $(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) true true true - $(BOOST_ROOT)\stage\lib; + $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib; + + + + + + + + + + - + + + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index 299baa5..fba3f02 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -75,10 +75,15 @@ + + + + + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index b1f8a5a..15b52ac 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -33,6 +33,15 @@ include\common + + include\common + + + include\common + + + include\common + @@ -44,5 +53,11 @@ src\common + + src\common + + + src\common + \ No newline at end of file diff --git a/include/common/TcpListener.h b/include/common/TcpListener.h new file mode 100644 index 0000000..b91cee8 --- /dev/null +++ b/include/common/TcpListener.h @@ -0,0 +1,26 @@ +#pragma once + +class TcpListenerException : public Exception +{ +public: + TcpListenerException(const std::string &errorString) + : Exception("TCP Listener Error: " + errorString) + {} +}; + +class TcpListener +{ +public: + TcpListener(boost::asio::io_service &io); + ~TcpListener(); + + void Initialize(const tcp::endpoint &localEndpoint); + void Listen(boost::function)> callback); + +private: + tcp::acceptor m_acceptor; + boost::function)> m_callback; + + void StartListening(); + void HandleAccept(boost::shared_ptr socket, const boost::system::error_code& error); +}; \ No newline at end of file diff --git a/include/common/TcpTransmitter.h b/include/common/TcpTransmitter.h index 030ff51..5f143be 100644 --- a/include/common/TcpTransmitter.h +++ b/include/common/TcpTransmitter.h @@ -14,10 +14,8 @@ class TcpTransmitter TcpTransmitter(); ~TcpTransmitter(); - void Initialize(boost::shared_ptr socket, - const tcp::endpoint &destination); - - void Send(const std::vector &data); + void Initialize(boost::shared_ptr socket, const tcp::endpoint &destination); + void Send(const std::vector &data); private: boost::shared_ptr m_socket; diff --git a/src/common/TcpListener.cpp b/src/common/TcpListener.cpp new file mode 100644 index 0000000..f17abdb --- /dev/null +++ b/src/common/TcpListener.cpp @@ -0,0 +1,37 @@ +#include +#include + +TcpListener::TcpListener(boost::asio::io_service &io) : m_acceptor(io) +{} + +TcpListener::~TcpListener() +{} + +void TcpListener::Initialize(const tcp::endpoint &localEndpoint) +{ + m_acceptor.bind(localEndpoint); +} + +void ListeningThread() +{ + +} + +void TcpListener::Listen(boost::function)> callback) +{ + m_callback = callback; + StartListening(); +} + +void TcpListener::StartListening() +{ + boost::shared_ptr socket(new tcp::socket(m_acceptor.get_io_service())); + m_acceptor.async_accept(*socket, boost::bind(&TcpListener::HandleAccept, this, socket, boost::asio::placeholders::error)); +} + +void TcpListener::HandleAccept(boost::shared_ptr socket, const boost::system::error_code& error) +{ + if (error) + m_callback(socket); + StartListening(); +} \ No newline at end of file diff --git a/src/common/TcpReceiver.cpp b/src/common/TcpReceiver.cpp index 30f3f27..af3734a 100644 --- a/src/common/TcpReceiver.cpp +++ b/src/common/TcpReceiver.cpp @@ -9,7 +9,7 @@ TcpReceiver::~TcpReceiver() { } -void TcpReceiver::Initialize(boost::shared_ptr socket, const udp::endpoint &destination) +void TcpReceiver::Initialize(boost::shared_ptr socket, const tcp::endpoint &destination) { m_socket = socket; m_destination = destination; diff --git a/src/common/TcpTransmitter.cpp b/src/common/TcpTransmitter.cpp index 11b728d..041f735 100644 --- a/src/common/TcpTransmitter.cpp +++ b/src/common/TcpTransmitter.cpp @@ -10,7 +10,7 @@ TcpTransmitter::~TcpTransmitter() } -void TcpTransmitter::Initialize(boost::shared_ptr socket, const udp::endpoint &destination) +void TcpTransmitter::Initialize(boost::shared_ptr socket, const tcp::endpoint &destination) { m_socket = socket; m_destination = destination; From 7ffef1dfaa5b6e7ff5495ea5a5761dc2609f677c Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Thu, 11 Dec 2014 18:44:39 +0545 Subject: [PATCH 18/54] for the sake of sync --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 6 +++ .../Mirror-Client.vcxproj.filters | 42 +++++++++++++++---- include/common/Button.h | 13 ++++++ include/common/Control.h | 19 +++++++++ include/common/Page.h | 16 +++++++ src/common/Button.cpp | 0 src/common/Control.cpp | 0 src/common/Page.cpp | 0 8 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 include/common/Button.h create mode 100644 include/common/Control.h create mode 100644 include/common/Page.h create mode 100644 src/common/Button.cpp create mode 100644 src/common/Control.cpp create mode 100644 src/common/Page.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index f909234..222902a 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -75,8 +75,11 @@ + + + @@ -85,6 +88,9 @@ + + + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index efdff33..4cd1b3f 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -24,24 +24,52 @@ include - - - include + + include\common + + + include\common + + + include\common + + + include\common + + + include\common + + + include\common + src - + src - - - + + src\common + + src + + src\common + + + src\common + + + src\common + + + src\common + \ No newline at end of file diff --git a/include/common/Button.h b/include/common/Button.h new file mode 100644 index 0000000..f63b3cc --- /dev/null +++ b/include/common/Button.h @@ -0,0 +1,13 @@ +#ifndef __BUTTON__ +#define __BUTTON__ + +#include "Control.h" + +class Button : public Control +{ +public: + Button(std::string label, int x, int y, int w, int h); +private: +}; + +#endif \ No newline at end of file diff --git a/include/common/Control.h b/include/common/Control.h new file mode 100644 index 0000000..cb70a2a --- /dev/null +++ b/include/common/Control.h @@ -0,0 +1,19 @@ +#ifndef __CONTROL__ +#define __CONTROL__ + +#include +#include + +class Control +{ +public: + Control() :m_handle(0){} + void PutFixedAt(GtkWidget *fixed, int x, int y); + void PutFixedRelativeTo(GtkWidget* fixed, int xOffset, int yOffset); +protected: + GtkWidget* m_handle; + int m_x, m_y, m_w, m_h; + std::string m_label; +}; + +#endif \ No newline at end of file diff --git a/include/common/Page.h b/include/common/Page.h new file mode 100644 index 0000000..4a46af6 --- /dev/null +++ b/include/common/Page.h @@ -0,0 +1,16 @@ +#ifndef __PAGE__ +#define __PAGE__ + +#include "Control.h" + +#include +#include + +class Page +{ +public: +private: + std::vector m_controls; +}; + +#endif \ No newline at end of file diff --git a/src/common/Button.cpp b/src/common/Button.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/common/Control.cpp b/src/common/Control.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/common/Page.cpp b/src/common/Page.cpp new file mode 100644 index 0000000..e69de29 From de0775087cf3c63d7d3f91e48970ca99a1a36fb5 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Thu, 11 Dec 2014 18:56:53 +0545 Subject: [PATCH 19/54] Add TcpClient --- .../Mirror-Client.vcxproj.filters | 3 + .../~AutoRecover.Mirror-Client.vcxproj | 99 ------------------- MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 1 + .../Mirror-Server.vcxproj.filters | 3 + include/client/TcpClient.h | 12 +++ src/client/TcpClient.cpp | 10 ++ 6 files changed, 29 insertions(+), 99 deletions(-) delete mode 100644 MSVC_2013/Mirror-Client/~AutoRecover.Mirror-Client.vcxproj create mode 100644 include/client/TcpClient.h create mode 100644 src/client/TcpClient.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 4e49b93..80d775f 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -45,6 +45,9 @@ include\common + + include + diff --git a/MSVC_2013/Mirror-Client/~AutoRecover.Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/~AutoRecover.Mirror-Client.vcxproj deleted file mode 100644 index a6d6fff..0000000 --- a/MSVC_2013/Mirror-Client/~AutoRecover.Mirror-Client.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {C3B011C9-AB6A-467B-B72D-DBF24A92B057} - MirrorClient - - - - Application - true - v120 - MultiByte - - - Application - false - v120 - true - MultiByte - - - - - - - - - - - - - $(ExecutablePath) - $(VC_IncludePath);$(WindowsSDK_IncludePath);$( - - - - Level3 - Disabled - true - $(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo - _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) - - - true - $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib; - - - - - Level3 - MaxSpeed - true - true - true - $(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo - _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) - - - true - true - true - $(BOOST_ROOT)\stage\lib;$(GTKDIR)\lib; - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index fba3f02..047454c 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -80,6 +80,7 @@ + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index 15b52ac..c1fa1bc 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -59,5 +59,8 @@ src\common + + include + \ No newline at end of file diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h new file mode 100644 index 0000000..639936f --- /dev/null +++ b/include/client/TcpClient.h @@ -0,0 +1,12 @@ +#pragma once +class TcpClient +{ +public: + TcpClient(boost::asio::io_service &io); + ~TcpClient(); + + void StartListening(); + +private: + boost::asio::io_service &m_io; +}; \ No newline at end of file diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp new file mode 100644 index 0000000..b8753c0 --- /dev/null +++ b/src/client/TcpClient.cpp @@ -0,0 +1,10 @@ +#include +#include + +TcpClient::TcpClient(boost::asio::io_service &io) +:m_io(io) +{} + +TcpClient::~TcpClient() +{} + From 9f33950e3fd6c6526ca22a39dcbad3e3fac42ef6 Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Thu, 11 Dec 2014 19:07:57 +0545 Subject: [PATCH 20/54] reinserted VideoCapture.h --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 2 +- .../Mirror-Client.vcxproj.filters | 6 ++-- include/client/VideoCapture.h | 34 +++++++++++++++++++ include/common/Button.h | 1 + include/common/Page.h | 4 +-- src/common/Control.cpp | 6 ++++ 6 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 include/client/VideoCapture.h diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 9583fde..48c9957 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -47,7 +47,7 @@ Level3 Disabled true - $(OPENCV_ROOT)\build\include;$(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo + $(OPENCV_ROOT)\include;$(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 889f757..627c2d4 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -36,9 +36,6 @@ include - - include - include\common\Network @@ -69,6 +66,9 @@ include\common\UI + + include + diff --git a/include/client/VideoCapture.h b/include/client/VideoCapture.h new file mode 100644 index 0000000..7530a4e --- /dev/null +++ b/include/client/VideoCapture.h @@ -0,0 +1,34 @@ +#ifndef __VIDEOCAPTURINGDEVICE__ +#define __VIDEOCAPTURINGDEVICE__ + +#include +#include +#include + +// TODO: Implement this class +class VideoCaptureDevice +{ + +}; + + + + +class VideoCapture +{ +public: + VideoCapture():m_fw(0), m_fh(0){} + std::vector Enumerate(); + unsigned char* GetBGRAFrame(); + int GetFrameWidth(); + int GetFrameHeight(); + void SetFrameWidth(); + void SetFrameHeight(); + void Initialize(int deviceID = 0); +private: + cv::VideoCapture m_captureDevice; + cv::Mat m_capturedFrame; + int m_fw, m_fh; +}; + +#endif \ No newline at end of file diff --git a/include/common/Button.h b/include/common/Button.h index f63b3cc..d8de677 100644 --- a/include/common/Button.h +++ b/include/common/Button.h @@ -6,6 +6,7 @@ class Button : public Control { public: + Button(); Button(std::string label, int x, int y, int w, int h); private: }; diff --git a/include/common/Page.h b/include/common/Page.h index 4a46af6..2b29682 100644 --- a/include/common/Page.h +++ b/include/common/Page.h @@ -9,8 +9,8 @@ class Page { public: -private: - std::vector m_controls; +protected: + std::vector m_controls; }; #endif \ No newline at end of file diff --git a/src/common/Control.cpp b/src/common/Control.cpp index e69de29..9ef74c9 100644 --- a/src/common/Control.cpp +++ b/src/common/Control.cpp @@ -0,0 +1,6 @@ +#include "common/Control.h" + +void Control::PutFixedAt(GtkWidget *fixed, int x, int y) +{ + +} \ No newline at end of file From bd6ada0f0a4ef333f822066f9de51f70d22c10e5 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Thu, 11 Dec 2014 19:47:39 +0545 Subject: [PATCH 21/54] Add TcpClient --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 2 ++ .../Mirror-Client/Mirror-Client.vcxproj.filters | 12 +++++++++--- MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 3 ++- .../Mirror-Server/Mirror-Server.vcxproj.filters | 3 +++ include/client/TcpClient.h | 7 ++++++- src/client/TcpClient.cpp | 14 +++++++++++++- 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 48c9957..17a3ea3 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -74,6 +74,7 @@ + @@ -91,6 +92,7 @@ + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 627c2d4..70f62c2 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -69,6 +69,9 @@ include + + include\common\Network + @@ -77,9 +80,6 @@ src\common\Network - - src\common\Network - src\common\Network @@ -104,5 +104,11 @@ src + + src\common\Network + + + src\common\Network + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index 047454c..35f8a09 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -44,7 +44,7 @@ Level3 Disabled true - $(SolutionDir)\..\include;$(BOOST_ROOT); + $(OPENCV_ROOT)\include;$(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) @@ -83,6 +83,7 @@ + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index c1fa1bc..eeaed3d 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -62,5 +62,8 @@ include + + src\common + \ No newline at end of file diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 639936f..9f01737 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -1,12 +1,17 @@ #pragma once +#include + class TcpClient { public: TcpClient(boost::asio::io_service &io); ~TcpClient(); - void StartListening(); + void StartListening(const tcp::endpoint& server); private: boost::asio::io_service &m_io; + TcpListener m_listener; + + void ListenerHandler(boost::shared_ptr socket); }; \ No newline at end of file diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index b8753c0..ece88cc 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -2,9 +2,21 @@ #include TcpClient::TcpClient(boost::asio::io_service &io) -:m_io(io) +:m_io(io), m_listener(io) {} TcpClient::~TcpClient() {} +void TcpClient::StartListening(const tcp::endpoint& server) +{ + m_listener.Initialize(server); + m_listener.Listen(boost::bind(&TcpClient::ListenerHandler, this, _1)); +} + + +void TcpClient::ListenerHandler(boost::shared_ptr socket) +{ + +} + From 7b038915c915b0fb3af82250fc631eb9c4653210 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Thu, 11 Dec 2014 20:58:04 +0545 Subject: [PATCH 22/54] almost done TcpHandler, rest by B. Dahal --- include/common/TcpHandler.h | 18 ++++++------- src/common/TcpHandler.cpp | 51 +++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index e3b8bbd..8ba957b 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -12,25 +12,25 @@ class TcpHandlerException : public Exception class TcpHandler { public: - TcpHandler(); + TcpHandler(boost::asio::io_service &ioService); ~Tcphandler(); // initialize with a socket - Initialize(boost::shared_ptr socket); + void Initialize(boost::shared_ptr socket); // send tcp request to the endpoint and create socket - Initialize(tcp::endpoint &endpoint); + void Initialize(tcp::endpoint &destEndpoint); // initial request for creating connection - Request(tcp::endpoint &endpoint); + void Request(tcp::endpoint &destEndpoint);// for normal tcp connect request - Send(const std::vector & data); + void Send(const std::vector &data); - Receive(const std::vector & data); + void Receive(const std::vector &data); private: boost::shared_ptr m_socket; boost::shared_ptr m_destination; - boost::shared_ptr m_transmitter; - boost::shared_ptr m_receiver; - const int m_port; + boost::asio::io_service &m_ioService; + std::vector m_lastSent; // may not be needed + std::vector m_lastReceived; // may not be needed }; diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index fc16e48..d6e3c16 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -1,7 +1,8 @@ #include #include -TcpHandler::TcpHandler() : +TcpHandler::TcpHandler(boost::asio::io_service &ioService) : + m_ioService(ioService) { } @@ -9,12 +10,47 @@ TcpHandler::~TcpHandler() { } -TcpHandler::Initialize(boost::shared_ptr socket) +// this is initialized by the Listener which accepts +// a connection from a peer and creates a socket +void TcpHandler::Initialize(boost::shared_ptr socket) { m_socket = socket; } -TcpHandler::Request(tcp::endpoint & destEndpoint) +// +void TcpHandler::Initialize(tcp::endpoint &destEndpoint) +{ + m_socket = boost::shared_ptr(new tcp::socket(m_ioService); +} + +// +void Send(const std::vector &data) +{ + boost::system::error_code error; + boost::asio::write(socket, boost::asio::buffer(data), + boost::asio::transfer_all(), error); + if (error) + throw TcpHandlerException("Error sending data : " + error.what()); + m_lastSent = data; +} + +// +void Receive(const std::vector &data) +{ + boost::system::error_code error; + size_t len = socket.read_some(boost::asio::buffer(data), error); + + if (error == boost::asio::error::eof) // connection closed by the peer + { + // + } + else if (error) + throw TcpHandlerException("Error receiving data : " + error.what()); + + // do something with the received data +} + +void TcpHandler::Request(tcp::endpoint &destEndpoint) { // since the host can have multiple addresses, we iterate // through the endpoints @@ -23,5 +59,10 @@ TcpHandler::Request(tcp::endpoint & destEndpoint) // the socket is not returned to the closed state boost::system::error_code error; - socket->connect(destEndpoint, error); - + m_socket->connect(destEndpoint, error); + + if (error) + throw TcpHandlerException("Error connecting to peer : " + error.what()); + // assign the destEndpoint to m_destination + m_destination = new tcp::endpoint(destEndpoint); +} From 3a4ac740635b2fa96d60585255275dba5ae6dc40 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Thu, 11 Dec 2014 21:14:18 +0545 Subject: [PATCH 23/54] TcpHandler corrected --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 1 + .../Mirror-Client.vcxproj.filters | 3 ++ include/common/TcpHandler.h | 4 +-- src/common/TcpHandler.cpp | 31 +++++-------------- src/common/TcpListener.cpp | 3 ++ 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 17a3ea3..e34eb22 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -99,6 +99,7 @@ + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 70f62c2..324cbe0 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -110,5 +110,8 @@ src\common\Network + + src\common\Network + \ No newline at end of file diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index b5906b9..8abfe79 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -15,7 +15,7 @@ class TcpHandler { public: TcpHandler(boost::asio::io_service &ioService); - ~Tcphandler(); + ~TcpHandler(); // initialize with a socket void Initialize(boost::shared_ptr socket); @@ -27,7 +27,7 @@ class TcpHandler void Send(const std::vector &data); - void Receive(const std::vector &data); + void Receive(std::vector &data); private: boost::shared_ptr m_socket; diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index d6e3c16..c4e0da7 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -20,49 +20,34 @@ void TcpHandler::Initialize(boost::shared_ptr socket) // void TcpHandler::Initialize(tcp::endpoint &destEndpoint) { - m_socket = boost::shared_ptr(new tcp::socket(m_ioService); + m_socket.reset(new tcp::socket(m_ioService)); } // -void Send(const std::vector &data) +void TcpHandler::Send(const std::vector &data) { - boost::system::error_code error; - boost::asio::write(socket, boost::asio::buffer(data), - boost::asio::transfer_all(), error); - if (error) - throw TcpHandlerException("Error sending data : " + error.what()); + boost::asio::write(*m_socket, boost::asio::buffer(data)); m_lastSent = data; } // -void Receive(const std::vector &data) +void TcpHandler::Receive(std::vector &data) { boost::system::error_code error; - size_t len = socket.read_some(boost::asio::buffer(data), error); + size_t len = m_socket->read_some(boost::asio::buffer(data), error); if (error == boost::asio::error::eof) // connection closed by the peer { // } else if (error) - throw TcpHandlerException("Error receiving data : " + error.what()); + throw TcpHandlerException("Error receiving data : " + error.message()); // do something with the received data } void TcpHandler::Request(tcp::endpoint &destEndpoint) { - // since the host can have multiple addresses, we iterate - // through the endpoints - - // if the connect fails, and the socket was already opened, - // the socket is not returned to the closed state - - boost::system::error_code error; - m_socket->connect(destEndpoint, error); - - if (error) - throw TcpHandlerException("Error connecting to peer : " + error.what()); - // assign the destEndpoint to m_destination - m_destination = new tcp::endpoint(destEndpoint); + m_socket->connect(destEndpoint); + m_destination.reset(new tcp::endpoint(destEndpoint)); } diff --git a/src/common/TcpListener.cpp b/src/common/TcpListener.cpp index f17abdb..3dc27a9 100644 --- a/src/common/TcpListener.cpp +++ b/src/common/TcpListener.cpp @@ -9,7 +9,10 @@ TcpListener::~TcpListener() void TcpListener::Initialize(const tcp::endpoint &localEndpoint) { + m_acceptor.open(localEndpoint.protocol()); + m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); m_acceptor.bind(localEndpoint); + m_acceptor.listen(); } void ListeningThread() From 214f5a784414bf1b3a88276d0644892970ee660b Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Fri, 12 Dec 2014 20:34:22 +0545 Subject: [PATCH 24/54] Tcp Send Receive Test --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 14 ----- .../Mirror-Client.vcxproj.filters | 56 ++--------------- MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 16 ----- .../Mirror-Server.vcxproj.filters | 50 --------------- include/client/TcpClient.h | 7 +++ include/common/Exception.h | 7 +-- include/common/RtpReceiver.h | 2 +- include/common/RtpTransmitter.h | 2 +- include/common/TcpHandler.h | 9 +-- include/common/TcpListener.h | 2 +- include/common/TcpReceiver.h | 25 -------- include/common/TcpTransmitter.h | 23 ------- include/common/common.h | 2 + src/client/TcpClient.cpp | 42 ++++++++++++- src/client/main.cpp | 62 +++++++------------ src/common/RtpReceiver.cpp | 7 ++- src/common/RtpTransmitter.cpp | 5 +- src/common/TcpHandler.cpp | 43 ++++++------- src/common/TcpListener.cpp | 12 ++-- src/common/TcpReceiver.cpp | 39 ------------ src/common/TcpTransmitter.cpp | 25 -------- 21 files changed, 120 insertions(+), 330 deletions(-) delete mode 100644 include/common/TcpReceiver.h delete mode 100644 include/common/TcpTransmitter.h delete mode 100644 src/common/TcpReceiver.cpp delete mode 100644 src/common/TcpTransmitter.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index e34eb22..e929f6f 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -73,36 +73,22 @@ - - - - - - - - - - - - - - diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 324cbe0..30c2ac7 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -16,15 +16,9 @@ {9c21b098-89eb-42ac-a5b6-3b2b2d9e0348} - - {94db33d3-f59b-4fce-bb19-26a69d1732e9} - {7ad2755b-7297-4301-a7ca-de728d42e37d} - - {d7763af7-b00e-423f-9925-8d714b2840f8} - @@ -33,9 +27,6 @@ include\common - - include - include\common\Network @@ -51,26 +42,8 @@ include\common\Network - - include\common\Network - - - include\common\Network - - - include\common\UI - - - include\common\UI - - - include\common\UI - - - include - - include\common\Network + include @@ -80,36 +53,15 @@ src\common\Network - - src\common\Network - - - src\common\Network - - - src\common\UI - - - src\common\UI - - - src\common\UI - - - src - src - - src - - - src\common\Network - src\common\Network + + src + src\common\Network diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index 35f8a09..e67b2b6 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -70,22 +70,6 @@ - - - - - - - - - - - - - - - - diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index eeaed3d..f721200 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -7,63 +7,13 @@ {901cda44-2609-4a20-bf53-fff3e0af9875} - - {468317da-7019-44a9-9204-ec31406d9baf} - {34d0d713-51c2-4570-b77a-9ac3a2176ef1} - - {66d0e2a1-f6cd-48cb-b33c-ec7dbdec73da} - - - - - include\common - - - include\common - - - include\common - - - include\common - - - include\common - - - include\common - - - include\common - - - include\common - src - - src\common - - - src\common - - - src\common - - - src\common - - - include - - - src\common - \ No newline at end of file diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 9f01737..249289f 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -1,5 +1,6 @@ #pragma once #include +#include class TcpClient { @@ -8,10 +9,16 @@ class TcpClient ~TcpClient(); void StartListening(const tcp::endpoint& server); + void Connect(const tcp::endpoint& server); + + // Test Method + void StartChatSession(); + TcpHandler& GetHandler() { return m_handler; } private: boost::asio::io_service &m_io; TcpListener m_listener; + TcpHandler m_handler; void ListenerHandler(boost::shared_ptr socket); }; \ No newline at end of file diff --git a/include/common/Exception.h b/include/common/Exception.h index 6d32ac5..9d5c39e 100644 --- a/include/common/Exception.h +++ b/include/common/Exception.h @@ -1,10 +1,9 @@ -#ifndef __EXCEPTION__ -#define __EXCEPTION__ +#pragma once + class Exception : public std::runtime_error { public: Exception(const std::string& errorString) : std::runtime_error(errorString) {} -}; -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/include/common/RtpReceiver.h b/include/common/RtpReceiver.h index e872ddb..6ef5c5c 100644 --- a/include/common/RtpReceiver.h +++ b/include/common/RtpReceiver.h @@ -29,7 +29,7 @@ class RtpReceiver const udp::endpoint& GetSource() const { return m_source; } - void Receive(std::vector &data, size_t maxSize = 1500); + void Receive(char* data, size_t maxSize = 1500); private: boost::shared_ptr m_socket; udp::endpoint m_source; diff --git a/include/common/RtpTransmitter.h b/include/common/RtpTransmitter.h index 282feed..777605f 100644 --- a/include/common/RtpTransmitter.h +++ b/include/common/RtpTransmitter.h @@ -30,7 +30,7 @@ class RtpTransmitter uint8_t GetPayloadType() const { return m_payloadType; } void SetPayloadType(uint8_t payloadType) { m_payloadType = payloadType; } - void Send(const std::vector &data); + void Send(const char *data, size_t size); private: boost::shared_ptr m_socket; udp::endpoint m_destination; diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index 8abfe79..ce29c4f 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -20,19 +20,16 @@ class TcpHandler // initialize with a socket void Initialize(boost::shared_ptr socket); // send tcp request to the endpoint and create socket - void Initialize(tcp::endpoint &destEndpoint); + void Initialize(const tcp::endpoint &destEndpoint); // initial request for creating connection void Request(tcp::endpoint &destEndpoint);// for normal tcp connect request - void Send(const std::vector &data); + void Send(const char* data, size_t size); - void Receive(std::vector &data); + void Receive(char* data, size_t max_size); private: boost::shared_ptr m_socket; - boost::shared_ptr m_destination; boost::asio::io_service &m_ioService; - std::vector m_lastSent; // may not be needed - std::vector m_lastReceived; // may not be needed }; diff --git a/include/common/TcpListener.h b/include/common/TcpListener.h index b91cee8..e27b10b 100644 --- a/include/common/TcpListener.h +++ b/include/common/TcpListener.h @@ -22,5 +22,5 @@ class TcpListener boost::function)> m_callback; void StartListening(); - void HandleAccept(boost::shared_ptr socket, const boost::system::error_code& error); + void NewThread(); }; \ No newline at end of file diff --git a/include/common/TcpReceiver.h b/include/common/TcpReceiver.h deleted file mode 100644 index 2f35b24..0000000 --- a/include/common/TcpReceiver.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -class TcpReceiverException: public Exception -{ - public: - TcpReceiverException(const std::string &errorString) : - Exception("TCP Receiver Error: " + errorString) - {} -}; - -class TcpReceiver -{ - public: - TcpReceiver(); - ~TcpReceiver(); - - void Initialize(boost::shared_ptr socket, - const tcp::endpoint &destination); - - void Receive(std::vector &data); - - private: - boost::shared_ptr m_socket; - tcp::endpoint m_destination; -}; diff --git a/include/common/TcpTransmitter.h b/include/common/TcpTransmitter.h deleted file mode 100644 index 5f143be..0000000 --- a/include/common/TcpTransmitter.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -class TcpTransmitterException: public Exception -{ - public: - TcpTransmitterException(const std::string &errorString) - : Exception("TCP Transmission Error: " + errorString) - {} -}; - -class TcpTransmitter -{ - public: - TcpTransmitter(); - ~TcpTransmitter(); - - void Initialize(boost::shared_ptr socket, const tcp::endpoint &destination); - void Send(const std::vector &data); - - private: - boost::shared_ptr m_socket; - tcp::endpoint m_destination; -}; diff --git a/include/common/common.h b/include/common/common.h index 4bef910..8e321e8 100644 --- a/include/common/common.h +++ b/include/common/common.h @@ -1,9 +1,11 @@ +#pragma once #include #include #include #include #include +#include #include #include diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index ece88cc..66ca47d 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -2,7 +2,7 @@ #include TcpClient::TcpClient(boost::asio::io_service &io) -:m_io(io), m_listener(io) +:m_io(io), m_listener(io), m_handler(io) {} TcpClient::~TcpClient() @@ -17,6 +17,46 @@ void TcpClient::StartListening(const tcp::endpoint& server) void TcpClient::ListenerHandler(boost::shared_ptr socket) { + m_handler.Initialize(socket); + for (long long i = 0; i < 100000000; ++i); + boost::thread t(boost::bind(&TcpClient::StartChatSession, this)); +} + +void TcpClient::Connect(const tcp::endpoint& server) +{ + m_handler.Initialize(server); + boost::thread t(boost::bind(&TcpClient::StartChatSession, this));; +} + +void TcpClient::StartChatSession() +{ + try + { + srand((unsigned int)time(NULL)); + + std::stringstream ss; + for (int i = 0; i < 10; ++i) + ss << rand()%20 << " "; + std::string str = ss.str(); + std::cout << "\n\nSending Data " << str << std::endl; + + m_handler.Send(str.c_str(), str.size()); + + while (true) + { + std::vector data; + data.resize(str.size() + 10); + m_handler.Receive(&data[0], data.size()); + std::cout << "\n\nReceived Data "; + for (unsigned i = 0; i < data.size(); ++i) + std::cout << data[i]; + std::cout << std::endl; + } + } + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; + } } diff --git a/src/client/main.cpp b/src/client/main.cpp index 663f07e..c700b2b 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -1,47 +1,33 @@ /* Client - main.cpp */ #include -#include "client/FrameRenderer.h" -#include "client/VideoCapture.h" +#include -#ifdef _WIN32 -#pragma comment(lib, "gtk-win32-3.0.lib") -#pragma comment(lib, "gobject-2.0.lib") -#pragma comment(lib, "cairo.lib") -#pragma comment(lib, "glib-2.0.lib") -#pragma comment(lib, "opencv_highgui245.lib") -#pragma comment(lib, "opencv_core245.lib") -#pragma comment(lib, "opencv_imgproc245.lib") -#endif - -FrameRenderer fr; -VideoCapture vidCap; - - -gboolean IdleFunction(gpointer userData) -{ - fr.SetRGBData(vidCap.GetBGRAFrame()); - return TRUE; -} int main(int argc, char *argv[]) { - GtkWidget *window; - gtk_init(&argc, &argv); - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - - gtk_window_set_default_size(GTK_WINDOW(window), 1000, 600); - gtk_window_set_resizable(GTK_WINDOW(window), FALSE); - gtk_widget_set_size_request(window, 1000, 600); - g_signal_connect_swapped(G_OBJECT(window), "destroy", - G_CALLBACK(gtk_main_quit), NULL); - g_idle_add(IdleFunction, 0); - vidCap.Initialize(); - fr.Initialize(window, 10, 10, vidCap.GetFrameWidth(), vidCap.GetFrameHeight()); - gtk_widget_show_all(window); - - gtk_main(); - + try + { + boost::asio::io_service io; + TcpClient client(io); + uint16_t port; + std::cout << "Enter port to listen to: "; + std::cin >> port; + client.StartListening(tcp::endpoint(tcp::v4(), port)); + + + { + std::cout << "Enter port to connect to: "; + std::cin >> port; + client.Connect(tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), port)); + while (true); + } + } + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; + std::cin.get(); + } + std::cin.get(); return 0; } diff --git a/src/common/RtpReceiver.cpp b/src/common/RtpReceiver.cpp index 780b394..abef4f7 100644 --- a/src/common/RtpReceiver.cpp +++ b/src/common/RtpReceiver.cpp @@ -20,14 +20,14 @@ void RtpReceiver::CleanUp() m_socket.reset(); } -void RtpReceiver::Receive(std::vector &data, size_t maxSize) +void RtpReceiver::Receive(char *data, size_t maxSize) { if (!m_socket) throw RtpReceptionError("RTP Receiver hasn't been properly initialized"); std::vector packet; packet.resize(maxSize); - size_t len = m_socket->receive_from(boost::asio::buffer(data), m_source); + size_t len = m_socket->receive_from(boost::asio::buffer(packet), m_source); packet.resize(len); char version = packet[0]; @@ -36,5 +36,6 @@ void RtpReceiver::Receive(std::vector &data, size_t maxSize) m_timeStamp = *((int*)&packet[4]); int SSRC = *((int*)&packet[8]); - data.assign(packet.begin() + RTP_HEADER_SIZE, packet.end()); + for (unsigned int i = 0; i < len - RTP_HEADER_SIZE; ++i) + data[i] = packet[i + RTP_HEADER_SIZE]; } \ No newline at end of file diff --git a/src/common/RtpTransmitter.cpp b/src/common/RtpTransmitter.cpp index 4f62659..c260b7f 100644 --- a/src/common/RtpTransmitter.cpp +++ b/src/common/RtpTransmitter.cpp @@ -24,7 +24,7 @@ void RtpTransmitter::CleanUp() m_destination = udp::endpoint(); } -void RtpTransmitter::Send(const std::vector &data) +void RtpTransmitter::Send(const char *data, size_t size) { if (!m_socket) throw RtpTransmissionException("RTP Transmitter hasn't been properly initialized"); @@ -44,7 +44,8 @@ void RtpTransmitter::Send(const std::vector &data) packet[10] = (char)0x7e; packet[11] = (char)0x67; - packet.insert(packet.end(), data.begin(), data.end()); + for (unsigned int i = 0; i < size; ++i) + packet[RTP_HEADER_SIZE + i] = data[i]; m_sequenceNumber++; m_timeStamp += m_timeStampIncrement; diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index c4e0da7..8f3de01 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -14,40 +14,35 @@ TcpHandler::~TcpHandler() // a connection from a peer and creates a socket void TcpHandler::Initialize(boost::shared_ptr socket) { - m_socket = socket; + m_socket = socket; + std::cout << "Connected to " << m_socket->remote_endpoint().address().to_string() << " " << m_socket->remote_endpoint().port() << std::endl; } // -void TcpHandler::Initialize(tcp::endpoint &destEndpoint) +void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) { m_socket.reset(new tcp::socket(m_ioService)); + m_socket->connect(destEndpoint); + std::cout << "Connected to " << m_socket->remote_endpoint().address().to_string() << " " << m_socket->remote_endpoint().port() << std::endl; } // -void TcpHandler::Send(const std::vector &data) +void TcpHandler::Send(const char* data, size_t size) { - boost::asio::write(*m_socket, boost::asio::buffer(data)); - m_lastSent = data; + if (!m_socket) return; + boost::asio::write(*m_socket, boost::asio::buffer(data, size)); } -// -void TcpHandler::Receive(std::vector &data) -{ - boost::system::error_code error; - size_t len = m_socket->read_some(boost::asio::buffer(data), error); - - if (error == boost::asio::error::eof) // connection closed by the peer - { - // - } - else if (error) - throw TcpHandlerException("Error receiving data : " + error.message()); - - // do something with the received data -} - -void TcpHandler::Request(tcp::endpoint &destEndpoint) +// +void TcpHandler::Receive(char* data, size_t max_size) { - m_socket->connect(destEndpoint); - m_destination.reset(new tcp::endpoint(destEndpoint)); + if (!m_socket) return; + boost::system::error_code error; + + m_socket->read_some(boost::asio::buffer(data, max_size), error); + + if (error == boost::asio::error::eof) + data[0] = 0; + else + throw Exception(error.message()); } diff --git a/src/common/TcpListener.cpp b/src/common/TcpListener.cpp index 3dc27a9..64d3529 100644 --- a/src/common/TcpListener.cpp +++ b/src/common/TcpListener.cpp @@ -28,13 +28,15 @@ void TcpListener::Listen(boost::function)> c void TcpListener::StartListening() { - boost::shared_ptr socket(new tcp::socket(m_acceptor.get_io_service())); - m_acceptor.async_accept(*socket, boost::bind(&TcpListener::HandleAccept, this, socket, boost::asio::placeholders::error)); + boost::thread t(boost::bind(&TcpListener::NewThread, this)); } -void TcpListener::HandleAccept(boost::shared_ptr socket, const boost::system::error_code& error) +void TcpListener::NewThread() { - if (error) + while (true) + { + boost::shared_ptr socket(new tcp::socket(m_acceptor.get_io_service())); + m_acceptor.accept(*socket); m_callback(socket); - StartListening(); + } } \ No newline at end of file diff --git a/src/common/TcpReceiver.cpp b/src/common/TcpReceiver.cpp deleted file mode 100644 index af3734a..0000000 --- a/src/common/TcpReceiver.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include - -TcpReceiver::TcpReceiver() -{ -} - -TcpReceiver::~TcpReceiver() -{ -} - -void TcpReceiver::Initialize(boost::shared_ptr socket, const tcp::endpoint &destination) -{ - m_socket = socket; - m_destination = destination; -} - -void TcpReceiver::Receive(std::vector &data) -{ - if(!m_socket) - throw TcpReceiverException("TCP Receiver hasn't been properly initialized"); - - boost::system::error_code error; - size_t len = m_socket->read_some(boost::asio::buffer(data), error); - - if (error == boost::asio::error::eof) - { - // Connection closed by the peer - // do something - } - else if (error) // some other error - { - throw boost::system::system_error(error); - } - // do something with the data - // for now, just display the data - std::cout.write(data.data(), len); -} - diff --git a/src/common/TcpTransmitter.cpp b/src/common/TcpTransmitter.cpp deleted file mode 100644 index 041f735..0000000 --- a/src/common/TcpTransmitter.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -TcpTransmitter::TcpTransmitter() -{ -} - -TcpTransmitter::~TcpTransmitter() -{ -} - - -void TcpTransmitter::Initialize(boost::shared_ptr socket, const tcp::endpoint &destination) -{ - m_socket = socket; - m_destination = destination; -} - -void TcpTransmitter::Send(const std::vector &data) -{ - if(!m_socket) - throw TcpTransmitterException("TCP Transmitter hasn't been properly initialized"); - - m_socket->send(boost::asio::buffer(data, data.size())); -} From 5e8890d4990bf1d2bc10619451cc6936b3e0167e Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Sat, 13 Dec 2014 09:19:46 +0545 Subject: [PATCH 25/54] nothing significant --- include/client/VideoCapture.h | 5 +++-- include/common/TcpHandler.h | 1 + main.cpp | 7 +++++++ makefile | 25 +++++++++++++++++++++++++ src/common/TcpHandler.cpp | 4 ++-- 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 main.cpp diff --git a/include/client/VideoCapture.h b/include/client/VideoCapture.h index 7530a4e..625cb07 100644 --- a/include/client/VideoCapture.h +++ b/include/client/VideoCapture.h @@ -12,7 +12,7 @@ class VideoCaptureDevice }; - +/* class VideoCapture { @@ -31,4 +31,5 @@ class VideoCapture int m_fw, m_fh; }; -#endif \ No newline at end of file +*/ +#endif diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index 8abfe79..09ea6c0 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -1,6 +1,7 @@ #pragma once //don't use pragma, use standard header guard technique +class Exception; class TcpHandlerException : public Exception { diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..4b2767a --- /dev/null +++ b/main.cpp @@ -0,0 +1,7 @@ +#include +#include + +int main() +{ + std::cout << " in main, compiled.. "; +} diff --git a/makefile b/makefile index e69de29..c8c5304 100644 --- a/makefile +++ b/makefile @@ -0,0 +1,25 @@ +CFLAGS = `pkg-config --cflags gtk+-3.0` +LIBFLAGS = `pkg-config --libs gtk+-3.0` + +INC_DIR = include +SRC_DIR = src + +CC = g++ + +#sources for common +SOURCES_COM = TcpHandler.cpp TcpListener.cpp +HEADERS_COM = TcpHandler.h TcpListener.h +OBJECTS_COM = $(SOURCES_COM:.CPP=.o) +FSOURCES_COM := $(addprefix $(SRC_DIR)/common/,$(SOURCES_COM)) +FHEADERS_COM := $(addprefix $(INC_DIR)/common/,$(HEADERS_FILE)) + +LDFLAGS_COM = -lboost_system -lboost_thread -lpthread + +client: tcphandler.o tcplistener.o + $(CC) main.cpp -o $@ + +tcphandler.o : $(FHEADERS_COM) + $(CC) -c $(SRC_DIR)/common/TcpHandler.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) + +tcplistner.o : $(FHEADERS_COM) + $(CC) -c $(SRC_DIR)/common/TcpListener.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index c4e0da7..2f119da 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -1,5 +1,5 @@ -#include -#include +#include "common/common.h" +#include "common/TcpHandler.h" TcpHandler::TcpHandler(boost::asio::io_service &ioService) : m_ioService(ioService) From 96467bdee85db4f5b8fdc2ec2556d4e770402eb9 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Sat, 13 Dec 2014 11:42:07 +0545 Subject: [PATCH 26/54] Add ChatMessage, Combine Rtp classes into single RtpHandler --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 9 ++-- .../Mirror-Client.vcxproj.filters | 27 ++++++------ .../Mirror-Server.vcxproj.filters | 3 -- include/client/TcpClient.h | 7 +-- include/common/ChatMessage.h | 19 ++++++++ include/common/RTPCommon.h | 3 -- .../common/{RtpTransmitter.h => RtpHandler.h} | 22 +++++++--- include/common/RtpReceiver.h | 40 ----------------- include/common/TcpHandler.h | 4 -- include/common/TcpListener.h | 5 ++- src/client/TcpClient.cpp | 43 +++++++++++++------ src/common/ChatMessage.cpp | 31 +++++++++++++ .../{RtpTransmitter.cpp => RtpHandler.cpp} | 39 +++++++++++++---- src/common/RtpReceiver.cpp | 41 ------------------ src/common/TcpHandler.cpp | 16 +++---- src/common/TcpListener.cpp | 15 ++++--- 16 files changed, 165 insertions(+), 159 deletions(-) create mode 100644 include/common/ChatMessage.h delete mode 100644 include/common/RTPCommon.h rename include/common/{RtpTransmitter.h => RtpHandler.h} (70%) delete mode 100644 include/common/RtpReceiver.h create mode 100644 src/common/ChatMessage.cpp rename src/common/{RtpTransmitter.cpp => RtpHandler.cpp} (55%) delete mode 100644 src/common/RtpReceiver.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index e929f6f..60c5ecf 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -74,19 +74,18 @@ + - - - + - - + + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 30c2ac7..74c4875 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -27,15 +27,6 @@ include\common - - include\common\Network - - - include\common\Network - - - include\common\Network - include\common\Network @@ -45,14 +36,14 @@ include + + include\common\Network + + + include\common\Network + - - src\common\Network - - - src\common\Network - src @@ -65,5 +56,11 @@ src\common\Network + + src\common\Network + + + src\common\Network + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index f721200..fa64609 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -1,9 +1,6 @@  - - {32677475-31fd-4fcd-8faf-e5c1b415d692} - {901cda44-2609-4a20-bf53-fff3e0af9875} diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 249289f..8cd230d 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -8,13 +8,14 @@ class TcpClient TcpClient(boost::asio::io_service &io); ~TcpClient(); - void StartListening(const tcp::endpoint& server); - void Connect(const tcp::endpoint& server); + // Start listening for incoming connections in a new thread + void StartListening(const tcp::endpoint& localEndPoint); + // Connect to a peer + void Connect(const tcp::endpoint& peer); // Test Method void StartChatSession(); - TcpHandler& GetHandler() { return m_handler; } private: boost::asio::io_service &m_io; TcpListener m_listener; diff --git a/include/common/ChatMessage.h b/include/common/ChatMessage.h new file mode 100644 index 0000000..4930c72 --- /dev/null +++ b/include/common/ChatMessage.h @@ -0,0 +1,19 @@ +#pragma once +#include "TcpHandler.h" + +// This class is used to send and receive chat messages with header through a TcpHandler +class ChatMessage +{ +public: + ChatMessage(); + ~ChatMessage(); + + const std::string& GetMessage() const { return m_body; } + void SetMessage(const std::string& message) { m_body = message; } + + void Send(TcpHandler &tcpHandler); + void Receive(TcpHandler &tcpHandler); + +private:; + std::string m_body; +}; \ No newline at end of file diff --git a/include/common/RTPCommon.h b/include/common/RTPCommon.h deleted file mode 100644 index 937283b..0000000 --- a/include/common/RTPCommon.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#define RTP_HEADER_SIZE 12 \ No newline at end of file diff --git a/include/common/RtpTransmitter.h b/include/common/RtpHandler.h similarity index 70% rename from include/common/RtpTransmitter.h rename to include/common/RtpHandler.h index 777605f..33555cd 100644 --- a/include/common/RtpTransmitter.h +++ b/include/common/RtpHandler.h @@ -1,5 +1,5 @@ #pragma once -#include "RTPCommon.h" +#define RTP_HEADER_SIZE 12 class RtpTransmissionException : public Exception { @@ -8,14 +8,21 @@ class RtpTransmissionException : public Exception : Exception("RTP Transmission Error: " + errorString) {} }; +class RtpReceptionError : public Exception +{ +public: + RtpReceptionError(std::string errorString) + : Exception("RTP Reception Error: " + errorString) + {} +}; -class RtpTransmitter +class RtpHandler { public: - RtpTransmitter(); - ~RtpTransmitter(); + RtpHandler(); + ~RtpHandler(); - void Initialize(boost::shared_ptr socket, const udp::endpoint &destination); + void Initialize(boost::shared_ptr socket, const udp::endpoint &remoteEndpoint); void CleanUp(); int GetTimeStamp() const { return m_timeStamp; } @@ -30,10 +37,13 @@ class RtpTransmitter uint8_t GetPayloadType() const { return m_payloadType; } void SetPayloadType(uint8_t payloadType) { m_payloadType = payloadType; } + const udp::endpoint& GetRemoteEndpoint() const { return m_remoteEndpoint; } + void Send(const char *data, size_t size); + void Receive(char* data, size_t maxSize = 1500); private: boost::shared_ptr m_socket; - udp::endpoint m_destination; + udp::endpoint m_remoteEndpoint; uint16_t m_sequenceNumber; int m_timeStamp, m_timeStampIncrement; diff --git a/include/common/RtpReceiver.h b/include/common/RtpReceiver.h deleted file mode 100644 index 6ef5c5c..0000000 --- a/include/common/RtpReceiver.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include "RTPCommon.h" - -class RtpReceptionError : public Exception -{ -public: - RtpReceptionError(std::string errorString) - : Exception("RTP Reception Error: " + errorString) - {} -}; - -class RtpReceiver -{ -public: - RtpReceiver(); - ~RtpReceiver(); - - void Initialize(boost::shared_ptr socket); - void CleanUp(); - - int GetTimeStamp() const { return m_timeStamp; } - void SetTimeStamp(int timeStamp) { m_timeStamp = timeStamp; } - - uint16_t GetSequenceNumber() const { return m_sequenceNumber; } - void SetSequenceNumber(uint16_t sequenceNumber) { m_sequenceNumber = sequenceNumber; } - - uint8_t GetPayloadType() const { return m_payloadType; } - void SetPayloadType(uint8_t payloadType) { m_payloadType = payloadType; } - - const udp::endpoint& GetSource() const { return m_source; } - - void Receive(char* data, size_t maxSize = 1500); -private: - boost::shared_ptr m_socket; - udp::endpoint m_source; - - uint16_t m_sequenceNumber; - int m_timeStamp; - uint8_t m_payloadType; -}; \ No newline at end of file diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index ce29c4f..51e24b1 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -22,11 +22,7 @@ class TcpHandler // send tcp request to the endpoint and create socket void Initialize(const tcp::endpoint &destEndpoint); - // initial request for creating connection - void Request(tcp::endpoint &destEndpoint);// for normal tcp connect request - void Send(const char* data, size_t size); - void Receive(char* data, size_t max_size); private: diff --git a/include/common/TcpListener.h b/include/common/TcpListener.h index e27b10b..092da6a 100644 --- a/include/common/TcpListener.h +++ b/include/common/TcpListener.h @@ -14,7 +14,10 @@ class TcpListener TcpListener(boost::asio::io_service &io); ~TcpListener(); + // Initialize the listener to listen at given local endpoint void Initialize(const tcp::endpoint &localEndpoint); + // Listen for incomming connections and call 'callback' when connected to any + // The callback is called on a new thread void Listen(boost::function)> callback); private: @@ -22,5 +25,5 @@ class TcpListener boost::function)> m_callback; void StartListening(); - void NewThread(); + void ListeningThread(); }; \ No newline at end of file diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index 66ca47d..45a4277 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -8,49 +8,66 @@ TcpClient::TcpClient(boost::asio::io_service &io) TcpClient::~TcpClient() {} -void TcpClient::StartListening(const tcp::endpoint& server) +void TcpClient::StartListening(const tcp::endpoint& localEndPoint) { - m_listener.Initialize(server); + m_listener.Initialize(localEndPoint); + // Listen method will start listening in new thread m_listener.Listen(boost::bind(&TcpClient::ListenerHandler, this, _1)); } - +// This is the handler called when the listener accepts a new connection void TcpClient::ListenerHandler(boost::shared_ptr socket) { m_handler.Initialize(socket); - for (long long i = 0; i < 100000000; ++i); + // For test purpose, we start a new session to send/receive random data + // The random data is seeded with current time + // Since two client applications are opened and run simultaneously + // both generate same random data as they are connected at same time + // To solve this issue, in the listening client, we make some delay + // In the client that sends the request for connection, + // we make no such delay (see below) + for (long long i = 0; i < 100000000; ++i) + ; + // Now start the new thread to send/receive data boost::thread t(boost::bind(&TcpClient::StartChatSession, this)); } -void TcpClient::Connect(const tcp::endpoint& server) +void TcpClient::Connect(const tcp::endpoint& peer) { - m_handler.Initialize(server); + m_handler.Initialize(peer); + + // For test purpose, start a new thread to send/receive data boost::thread t(boost::bind(&TcpClient::StartChatSession, this));; } +#include +// Send/Receive randome data void TcpClient::StartChatSession() { try { + // Generate a string with random numbers srand((unsigned int)time(NULL)); - std::stringstream ss; for (int i = 0; i < 10; ++i) ss << rand()%20 << " "; std::string str = ss.str(); std::cout << "\n\nSending Data " << str << std::endl; - m_handler.Send(str.c_str(), str.size()); + // Send the string as a chat message + ChatMessage message; + message.SetMessage(str); + message.Send(m_handler); + + + // keep receiving messages and print them while (true) { - std::vector data; - data.resize(str.size() + 10); - m_handler.Receive(&data[0], data.size()); + message.Receive(m_handler); std::cout << "\n\nReceived Data "; - for (unsigned i = 0; i < data.size(); ++i) - std::cout << data[i]; + std::cout << message.GetMessage(); std::cout << std::endl; } } diff --git a/src/common/ChatMessage.cpp b/src/common/ChatMessage.cpp new file mode 100644 index 0000000..a43572b --- /dev/null +++ b/src/common/ChatMessage.cpp @@ -0,0 +1,31 @@ +#include +#include + +ChatMessage::ChatMessage() +{} + +ChatMessage::~ChatMessage() +{} + +struct ChatHeader +{ + size_t body_size; +}; + +void ChatMessage::Send(TcpHandler &tcpHandler) +{ + ChatHeader header; + header.body_size = m_body.size() + 1; // add 1 to size to ensure null terminator + tcpHandler.Send((char*)&header, sizeof(ChatHeader)); + tcpHandler.Send(m_body.c_str(), header.body_size); +} + +void ChatMessage::Receive(TcpHandler &tcpHandler) +{ + ChatHeader header; + tcpHandler.Receive((char*)&header, sizeof(ChatHeader)); + char * data = new char[header.body_size]; + tcpHandler.Receive(data, header.body_size); + m_body = std::string(data); + delete[] data; +} \ No newline at end of file diff --git a/src/common/RtpTransmitter.cpp b/src/common/RtpHandler.cpp similarity index 55% rename from src/common/RtpTransmitter.cpp rename to src/common/RtpHandler.cpp index c260b7f..80a331f 100644 --- a/src/common/RtpTransmitter.cpp +++ b/src/common/RtpHandler.cpp @@ -1,30 +1,30 @@ #include -#include +#include -RtpTransmitter::RtpTransmitter() +RtpHandler::RtpHandler() { } -RtpTransmitter::~RtpTransmitter() +RtpHandler::~RtpHandler() { CleanUp(); } -void RtpTransmitter::Initialize(boost::shared_ptr socket, const udp::endpoint &destination) +void RtpHandler::Initialize(boost::shared_ptr socket, const udp::endpoint &remoteEndpoint) { m_socket = socket; - m_destination = destination; + m_remoteEndpoint = remoteEndpoint; m_sequenceNumber = m_timeStamp = 0; m_timeStampIncrement = 3600; // for 25fps video } -void RtpTransmitter::CleanUp() +void RtpHandler::CleanUp() { m_socket.reset(); - m_destination = udp::endpoint(); + m_remoteEndpoint = udp::endpoint(); } -void RtpTransmitter::Send(const char *data, size_t size) +void RtpHandler::Send(const char *data, size_t size) { if (!m_socket) throw RtpTransmissionException("RTP Transmitter hasn't been properly initialized"); @@ -50,5 +50,26 @@ void RtpTransmitter::Send(const char *data, size_t size) m_sequenceNumber++; m_timeStamp += m_timeStampIncrement; - m_socket->send_to(boost::asio::buffer(packet), m_destination); + m_socket->send_to(boost::asio::buffer(packet), m_remoteEndpoint); +} + + +void RtpHandler::Receive(char* data, size_t maxSize) +{ + if (!m_socket) + throw RtpReceptionError("RTP Receiver hasn't been properly initialized"); + + std::vector packet; + packet.resize(maxSize); + size_t len = m_socket->receive_from(boost::asio::buffer(packet), m_remoteEndpoint); + packet.resize(len); + + char version = packet[0]; + m_payloadType = packet[1]; + m_sequenceNumber = *((uint16_t*)&packet[2]); + m_timeStamp = *((int*)&packet[4]); + int SSRC = *((int*)&packet[8]); + + for (unsigned int i = 0; i < len - RTP_HEADER_SIZE; ++i) + data[i] = packet[i + RTP_HEADER_SIZE]; } \ No newline at end of file diff --git a/src/common/RtpReceiver.cpp b/src/common/RtpReceiver.cpp deleted file mode 100644 index abef4f7..0000000 --- a/src/common/RtpReceiver.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -RtpReceiver::RtpReceiver() -{} - -RtpReceiver::~RtpReceiver() -{ - CleanUp(); -} - -void RtpReceiver::Initialize(boost::shared_ptr socket) -{ - m_socket = socket; - m_sequenceNumber = m_timeStamp = 0; -} - -void RtpReceiver::CleanUp() -{ - m_socket.reset(); -} - -void RtpReceiver::Receive(char *data, size_t maxSize) -{ - if (!m_socket) - throw RtpReceptionError("RTP Receiver hasn't been properly initialized"); - - std::vector packet; - packet.resize(maxSize); - size_t len = m_socket->receive_from(boost::asio::buffer(packet), m_source); - packet.resize(len); - - char version = packet[0]; - m_payloadType = packet[1]; - m_sequenceNumber = *((uint16_t*)&packet[2]); - m_timeStamp = *((int*)&packet[4]); - int SSRC = *((int*)&packet[8]); - - for (unsigned int i = 0; i < len - RTP_HEADER_SIZE; ++i) - data[i] = packet[i + RTP_HEADER_SIZE]; -} \ No newline at end of file diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index 8f3de01..0dfad2b 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -14,13 +14,18 @@ TcpHandler::~TcpHandler() // a connection from a peer and creates a socket void TcpHandler::Initialize(boost::shared_ptr socket) { + if (m_socket) + throw TcpHandlerException("Socket alread created"); m_socket = socket; std::cout << "Connected to " << m_socket->remote_endpoint().address().to_string() << " " << m_socket->remote_endpoint().port() << std::endl; } -// +// this is used to create a new socket to connect to a peer at +// given endpoint void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) { + if (m_socket) + throw TcpHandlerException("Socket alread created"); m_socket.reset(new tcp::socket(m_ioService)); m_socket->connect(destEndpoint); std::cout << "Connected to " << m_socket->remote_endpoint().address().to_string() << " " << m_socket->remote_endpoint().port() << std::endl; @@ -37,12 +42,5 @@ void TcpHandler::Send(const char* data, size_t size) void TcpHandler::Receive(char* data, size_t max_size) { if (!m_socket) return; - boost::system::error_code error; - - m_socket->read_some(boost::asio::buffer(data, max_size), error); - - if (error == boost::asio::error::eof) - data[0] = 0; - else - throw Exception(error.message()); + m_socket->read_some(boost::asio::buffer(data, max_size));; } diff --git a/src/common/TcpListener.cpp b/src/common/TcpListener.cpp index 64d3529..08bf421 100644 --- a/src/common/TcpListener.cpp +++ b/src/common/TcpListener.cpp @@ -9,34 +9,35 @@ TcpListener::~TcpListener() void TcpListener::Initialize(const tcp::endpoint &localEndpoint) { + // Open and bind the tcp acceptor to the local endpoint m_acceptor.open(localEndpoint.protocol()); m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); m_acceptor.bind(localEndpoint); + // Prepare to listen m_acceptor.listen(); } -void ListeningThread() -{ - -} - void TcpListener::Listen(boost::function)> callback) { m_callback = callback; StartListening(); } +// Create new thread to listen for incomming connections void TcpListener::StartListening() { - boost::thread t(boost::bind(&TcpListener::NewThread, this)); + boost::thread t(boost::bind(&TcpListener::ListeningThread, this)); } -void TcpListener::NewThread() +void TcpListener::ListeningThread() { while (true) { + // Create a new socket to represent a new connection boost::shared_ptr socket(new tcp::socket(m_acceptor.get_io_service())); + // Wait for a connection and accept at the socket m_acceptor.accept(*socket); + // Send the socket to the handler m_callback(socket); } } \ No newline at end of file From 1975be183f8789284c4ee1dc8b04ba153442ac82 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Sat, 13 Dec 2014 15:50:49 +0545 Subject: [PATCH 27/54] Add group connection through server --- .gitignore | 1 + MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 2 + .../Mirror-Client.vcxproj.filters | 6 ++ MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 16 +++++ .../Mirror-Server.vcxproj.filters | 56 ++++++++++++++++++ include/client/TcpClient.h | 5 +- include/common/RequestHandler.h | 30 ++++++++++ include/common/RtpHandler.h | 2 +- include/common/TcpHandler.h | 3 + include/common/TcpListener.h | 4 +- include/common/common.h | 2 + include/server/ClientsManager.h | 37 ++++++++++++ src/client/TcpClient.cpp | 25 +++++--- src/client/main.cpp | 16 ++++- src/common/RequestHandler.cpp | 28 +++++++++ src/common/TcpHandler.cpp | 15 ++++- src/server/ClientsManager.cpp | 59 +++++++++++++++++++ src/server/main.cpp | 21 +++++-- 18 files changed, 304 insertions(+), 24 deletions(-) create mode 100644 include/common/RequestHandler.h create mode 100644 include/server/ClientsManager.h create mode 100644 src/common/RequestHandler.cpp create mode 100644 src/server/ClientsManager.cpp diff --git a/.gitignore b/.gitignore index 8fb39aa..fe70f20 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,4 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store +/MSVC_2013/Mirror-Client diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 60c5ecf..eb1c755 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -77,6 +77,7 @@ + @@ -85,6 +86,7 @@ + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 74c4875..91f3bca 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -42,6 +42,9 @@ include\common\Network + + include\common\Network + @@ -62,5 +65,8 @@ src\common\Network + + src\common\Network + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index e67b2b6..7554e45 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -70,8 +70,24 @@ + + + + + + + + + + + + + + + + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index fa64609..66be250 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -7,10 +7,66 @@ {34d0d713-51c2-4570-b77a-9ac3a2176ef1} + + {056a9fd5-2ff8-44a0-8d6a-2ec35c6def8f} + + + {c610381c-f408-42d5-b16b-e72a27a09f45} + + + {cbb7e2c4-afe4-4030-8839-a5c49b4b694d} + + + {61477837-06db-47c8-8f06-925c674e49f1} + src + + src\common\Network + + + src\common\Network + + + src\common\Network + + + src\common\Network + + + src + + + src\common\Network + + + + + include\common + + + include\common + + + include\common\Network + + + include\common\Network + + + include\common\Network + + + include\common\Network + + + include + + + include\common\Network + \ No newline at end of file diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 8cd230d..68c5ea8 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include class TcpClient { @@ -15,11 +16,13 @@ class TcpClient // Test Method void StartChatSession(); + void JoinGroup(uint32_t groupId); private: boost::asio::io_service &m_io; TcpListener m_listener; - TcpHandler m_handler; + TcpHandler m_tcpHandler; + RequestHandler m_requests; void ListenerHandler(boost::shared_ptr socket); }; \ No newline at end of file diff --git a/include/common/RequestHandler.h b/include/common/RequestHandler.h new file mode 100644 index 0000000..62f1ddc --- /dev/null +++ b/include/common/RequestHandler.h @@ -0,0 +1,30 @@ +#pragma once +#include "TcpHandler.h" + +const size_t REQUEST_MAX_SIZE = 150; +class RequestHandler +{ +public: + enum REQUEST_TYPE{ JOIN_GROUP = 0 }; + struct Request + { + REQUEST_TYPE type; + union + { + struct + { + uint32_t groupId; + } join; + } info; + }; + + RequestHandler(); + ~RequestHandler(); + + static void JoinGroup(TcpHandler &tcpHandler, uint32_t groupId); + + void ReceiveRequest(TcpHandler &tcpHandler); + const Request& GetLastRequest() { return m_request; } +private: + Request m_request; +}; \ No newline at end of file diff --git a/include/common/RtpHandler.h b/include/common/RtpHandler.h index 33555cd..9246ce3 100644 --- a/include/common/RtpHandler.h +++ b/include/common/RtpHandler.h @@ -1,5 +1,4 @@ #pragma once -#define RTP_HEADER_SIZE 12 class RtpTransmissionException : public Exception { @@ -16,6 +15,7 @@ class RtpReceptionError : public Exception {} }; +const size_t RTP_HEADER_SIZE = 12; class RtpHandler { public: diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index 51e24b1..90a6771 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -24,6 +24,9 @@ class TcpHandler void Send(const char* data, size_t size); void Receive(char* data, size_t max_size); + size_t Available(); + + std::string GetDestinationAddress() const; private: boost::shared_ptr m_socket; diff --git a/include/common/TcpListener.h b/include/common/TcpListener.h index 092da6a..a02d655 100644 --- a/include/common/TcpListener.h +++ b/include/common/TcpListener.h @@ -16,8 +16,8 @@ class TcpListener // Initialize the listener to listen at given local endpoint void Initialize(const tcp::endpoint &localEndpoint); - // Listen for incomming connections and call 'callback' when connected to any - // The callback is called on a new thread + // Listen for incomming connections in a new thread + // and call 'callback' for each connection void Listen(boost::function)> callback); private: diff --git a/include/common/common.h b/include/common/common.h index 8e321e8..4d0fc5b 100644 --- a/include/common/common.h +++ b/include/common/common.h @@ -1,6 +1,8 @@ #pragma once #include +#include + #include #include #include diff --git a/include/server/ClientsManager.h b/include/server/ClientsManager.h new file mode 100644 index 0000000..344c3e2 --- /dev/null +++ b/include/server/ClientsManager.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include + +struct ClientInfo +{ + ClientInfo(boost::asio::io_service& io) : connection(io) {} + TcpHandler connection; +}; + +class ClientsManager +{ +public: + ClientsManager(boost::asio::io_service& io); + ~ClientsManager(); + + // Start a new thread to listen to and accept clients + void StartListening(const tcp::endpoint &localEndpoint); + // Start another thread to process each client + void StartProcessing(); + std::vector& GetClients() { return m_clients; } + +private: + TcpListener m_listener; + boost::asio::io_service &m_ioService; + RequestHandler m_requests; + bool m_lock; + + // Clients List + std::vector m_clients; + std::unordered_map> m_groups; // map groupId to a list of id's of clients + // each client id is index of m_clients vector + + void HandleClient(boost::shared_ptr &socket); + void ProcessClients(); +}; \ No newline at end of file diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index 45a4277..c0124f1 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -2,7 +2,7 @@ #include TcpClient::TcpClient(boost::asio::io_service &io) -:m_io(io), m_listener(io), m_handler(io) +:m_io(io), m_listener(io), m_tcpHandler(io) {} TcpClient::~TcpClient() @@ -18,7 +18,8 @@ void TcpClient::StartListening(const tcp::endpoint& localEndPoint) // This is the handler called when the listener accepts a new connection void TcpClient::ListenerHandler(boost::shared_ptr socket) { - m_handler.Initialize(socket); + m_tcpHandler.Initialize(socket); + std::cout << "Connected: " << m_tcpHandler.GetDestinationAddress() << std::endl; // For test purpose, we start a new session to send/receive random data // The random data is seeded with current time @@ -27,18 +28,24 @@ void TcpClient::ListenerHandler(boost::shared_ptr socket) // To solve this issue, in the listening client, we make some delay // In the client that sends the request for connection, // we make no such delay (see below) - for (long long i = 0; i < 100000000; ++i) - ; + //for (long long i = 0; i < 100000000; ++i) + // ; // Now start the new thread to send/receive data - boost::thread t(boost::bind(&TcpClient::StartChatSession, this)); + //boost::thread t(boost::bind(&TcpClient::StartChatSession, this)); } void TcpClient::Connect(const tcp::endpoint& peer) { - m_handler.Initialize(peer); + m_tcpHandler.Initialize(peer); + std::cout << "Connected: " << m_tcpHandler.GetDestinationAddress() << std::endl; // For test purpose, start a new thread to send/receive data - boost::thread t(boost::bind(&TcpClient::StartChatSession, this));; + //boost::thread t(boost::bind(&TcpClient::StartChatSession, this));; +} + +void TcpClient::JoinGroup(uint32_t groupId) +{ + m_requests.JoinGroup(m_tcpHandler, groupId); } #include @@ -59,13 +66,13 @@ void TcpClient::StartChatSession() // Send the string as a chat message ChatMessage message; message.SetMessage(str); - message.Send(m_handler); + message.Send(m_tcpHandler); // keep receiving messages and print them while (true) { - message.Receive(m_handler); + message.Receive(m_tcpHandler); std::cout << "\n\nReceived Data "; std::cout << message.GetMessage(); std::cout << std::endl; diff --git a/src/client/main.cpp b/src/client/main.cpp index c700b2b..cd19052 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -11,16 +11,26 @@ int main(int argc, char *argv[]) boost::asio::io_service io; TcpClient client(io); uint16_t port; - std::cout << "Enter port to listen to: "; + std::cout << "Enter port of this client: "; std::cin >> port; client.StartListening(tcp::endpoint(tcp::v4(), port)); - - { + // Connect to another peer + /*{ std::cout << "Enter port to connect to: "; std::cin >> port; client.Connect(tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), port)); while (true); + }*/ + + // Connect to server + { + client.Connect(tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 8183)); + uint32_t groupId; + std::cout << "Enter group-id to join: "; + std::cin >> groupId; + client.JoinGroup(groupId); + while (true); } } catch (std::exception &ex) diff --git a/src/common/RequestHandler.cpp b/src/common/RequestHandler.cpp new file mode 100644 index 0000000..c6d3396 --- /dev/null +++ b/src/common/RequestHandler.cpp @@ -0,0 +1,28 @@ +#include +#include + +RequestHandler::RequestHandler() +{} + +RequestHandler::~RequestHandler() +{} + +void RequestHandler::JoinGroup(TcpHandler &tcpHandler, uint32_t groupId) +{ + std::string request; + request = "JOIN_REQUEST"; + request += "\t" + std::to_string(groupId); + tcpHandler.Send(request.c_str(), request.size() + 1); +} + +void RequestHandler::ReceiveRequest(TcpHandler &tcpHandler) +{ + char data[REQUEST_MAX_SIZE]; + tcpHandler.Receive(data, REQUEST_MAX_SIZE); + std::string request(data); + if (request.substr(0, 12) == "JOIN_REQUEST") + { + m_request.type = JOIN_GROUP; + m_request.info.join.groupId = std::stoul(request.substr(13).c_str()); + } +} \ No newline at end of file diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index 0dfad2b..2a92422 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -17,7 +17,6 @@ void TcpHandler::Initialize(boost::shared_ptr socket) if (m_socket) throw TcpHandlerException("Socket alread created"); m_socket = socket; - std::cout << "Connected to " << m_socket->remote_endpoint().address().to_string() << " " << m_socket->remote_endpoint().port() << std::endl; } // this is used to create a new socket to connect to a peer at @@ -28,7 +27,6 @@ void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) throw TcpHandlerException("Socket alread created"); m_socket.reset(new tcp::socket(m_ioService)); m_socket->connect(destEndpoint); - std::cout << "Connected to " << m_socket->remote_endpoint().address().to_string() << " " << m_socket->remote_endpoint().port() << std::endl; } // @@ -44,3 +42,16 @@ void TcpHandler::Receive(char* data, size_t max_size) if (!m_socket) return; m_socket->read_some(boost::asio::buffer(data, max_size));; } + +size_t TcpHandler::Available() +{ + if (!m_socket) return 0; + return m_socket->available(); +} + +std::string TcpHandler::GetDestinationAddress() const +{ + std::stringstream ss; + ss << "IP: " << m_socket->remote_endpoint().address().to_string() << " Port: " << m_socket->remote_endpoint().port(); + return ss.str(); +} diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp new file mode 100644 index 0000000..b5f1f56 --- /dev/null +++ b/src/server/ClientsManager.cpp @@ -0,0 +1,59 @@ +#include +#include + +ClientsManager::ClientsManager(boost::asio::io_service& io) +: m_listener(io), m_ioService(io) +{} + +ClientsManager::~ClientsManager() +{} + +void ClientsManager::StartListening(const tcp::endpoint &localEndpoint) +{ + m_listener.Initialize(localEndpoint); + m_listener.Listen(boost::bind(&ClientsManager::HandleClient, this, _1)); +} + +// Add any client that is connected to the clients list +void ClientsManager::HandleClient(boost::shared_ptr &socket) +{ + m_lock = true; + ClientInfo client(m_ioService); + client.connection.Initialize(socket); + m_clients.push_back(client); + m_lock = false; +} + +void ClientsManager::StartProcessing() +{ + m_lock = false; + boost::thread t(boost::bind(&ClientsManager::ProcessClients, this)); +} + +void ClientsManager::ProcessClients() +{ + while (true) + { + while (m_lock); + for (unsigned int i = 0; i < m_clients.size(); ++i) + { + while (m_lock); + try + { + size_t bytes = m_clients[i].connection.Available(); + if (bytes > 0) + { + m_requests.ReceiveRequest(m_clients[i].connection); + if (m_requests.GetLastRequest().type == RequestHandler::JOIN_GROUP) + { + uint32_t gid = m_requests.GetLastRequest().info.join.groupId; + m_groups[gid].push_back(i); // push the client id to the group + std::cout << "Connected client #" << i << " to group #" << gid << std::endl; + } + } + } + catch (std::exception &ex) + {} + } + } +} \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index 58028b3..56abb38 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -1,16 +1,25 @@ /* Server - main.cpp */ #include +#include + int main() { - try - { - throw Exception("Aafnai bariko exception, aina herera"); - } - catch (Exception &ex) + boost::asio::io_service io_service; + ClientsManager manager(io_service); + + size_t clientsNumber = 0; + // 8183 is the port of this server; 8183 == BIBE(K) :P + manager.StartListening(tcp::endpoint(tcp::v4(), 8183)); + manager.StartProcessing(); + + while (true) { - std::cout << ex.what() << std::endl; + // for every new connection, print its address + for (; clientsNumber < manager.GetClients().size(); ++clientsNumber) + std::cout << "Client Connected: " << manager.GetClients()[clientsNumber].connection.GetDestinationAddress() << std::endl; } + std::cin.get(); return 0; } \ No newline at end of file From c71a4ec953b01872d06c93767fdd95bcdb5f1a1a Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Sat, 13 Dec 2014 18:20:20 +0545 Subject: [PATCH 28/54] Add Group-Chat functionality --- include/client/TcpClient.h | 13 ++++-- include/common/RequestHandler.h | 24 ++++++++--- include/common/TcpHandler.h | 1 + include/server/ClientsManager.h | 8 +++- src/client/TcpClient.cpp | 73 ++++++++++++++++----------------- src/client/main.cpp | 1 + src/common/RequestHandler.cpp | 23 +++++------ src/server/ClientsManager.cpp | 73 +++++++++++++++++++++++++++++---- src/server/main.cpp | 12 +++--- 9 files changed, 154 insertions(+), 74 deletions(-) diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 68c5ea8..ba62d9e 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -11,12 +11,14 @@ class TcpClient // Start listening for incoming connections in a new thread void StartListening(const tcp::endpoint& localEndPoint); - // Connect to a peer + // Connect to a peer (server/client) void Connect(const tcp::endpoint& peer); - // Test Method - void StartChatSession(); + // Test Methods + // Join a group void JoinGroup(uint32_t groupId); + // Start a group chat + void StartChatSession(uint32_t groupId); private: boost::asio::io_service &m_io; @@ -25,4 +27,9 @@ class TcpClient RequestHandler m_requests; void ListenerHandler(boost::shared_ptr socket); + + // Group chat session to receive chat messages + void ChatSession(uint32_t groupId); + // For console, we use separate thread for inputting chat messages + void ChatInput(uint32_t groupId); }; \ No newline at end of file diff --git a/include/common/RequestHandler.h b/include/common/RequestHandler.h index 62f1ddc..f193d16 100644 --- a/include/common/RequestHandler.h +++ b/include/common/RequestHandler.h @@ -5,25 +5,37 @@ const size_t REQUEST_MAX_SIZE = 150; class RequestHandler { public: - enum REQUEST_TYPE{ JOIN_GROUP = 0 }; + enum REQUEST_TYPE{ JOIN_GROUP = 0, GROUP_CHAT }; struct Request { - REQUEST_TYPE type; - union + REQUEST_TYPE type; // Request type + union // Union of request data for each type { + // JOIN_GROUP struct { - uint32_t groupId; + uint32_t groupId; // group-id to join } join; + // GROUP_CHAT + struct + { + uint32_t groupId; // group-id to send chat message to + } groupChat; } info; }; RequestHandler(); ~RequestHandler(); - static void JoinGroup(TcpHandler &tcpHandler, uint32_t groupId); - + // Request the server to join a group + void JoinGroup(TcpHandler &tcpHandler, uint32_t groupId); + // Request the server to wait for incoming chat message for a group + void GroupChat(TcpHandler &tcpHandler, uint32_t groupId); + + + // Receive a request void ReceiveRequest(TcpHandler &tcpHandler); + // Get last sent/received request const Request& GetLastRequest() { return m_request; } private: Request m_request; diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index 90a6771..cdf2086 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -27,6 +27,7 @@ class TcpHandler size_t Available(); std::string GetDestinationAddress() const; + boost::shared_ptr GetSocket() const { return m_socket; } private: boost::shared_ptr m_socket; diff --git a/include/server/ClientsManager.h b/include/server/ClientsManager.h index 344c3e2..67a9c1b 100644 --- a/include/server/ClientsManager.h +++ b/include/server/ClientsManager.h @@ -19,11 +19,12 @@ class ClientsManager void StartListening(const tcp::endpoint &localEndpoint); // Start another thread to process each client void StartProcessing(); + // Get the connected clients std::vector& GetClients() { return m_clients; } private: - TcpListener m_listener; boost::asio::io_service &m_ioService; + TcpListener m_listener; RequestHandler m_requests; bool m_lock; @@ -32,6 +33,11 @@ class ClientsManager std::unordered_map> m_groups; // map groupId to a list of id's of clients // each client id is index of m_clients vector + // Handle a newly connected client void HandleClient(boost::shared_ptr &socket); + // Process each client void ProcessClients(); + + // Test Method to receive chat messages on GROUP_CHAT request + void ReceiveChat(unsigned int client, unsigned int group); }; \ No newline at end of file diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index c0124f1..e3ee07e 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -1,5 +1,6 @@ #include #include +#include TcpClient::TcpClient(boost::asio::io_service &io) :m_io(io), m_listener(io), m_tcpHandler(io) @@ -20,62 +21,41 @@ void TcpClient::ListenerHandler(boost::shared_ptr socket) { m_tcpHandler.Initialize(socket); std::cout << "Connected: " << m_tcpHandler.GetDestinationAddress() << std::endl; - - // For test purpose, we start a new session to send/receive random data - // The random data is seeded with current time - // Since two client applications are opened and run simultaneously - // both generate same random data as they are connected at same time - // To solve this issue, in the listening client, we make some delay - // In the client that sends the request for connection, - // we make no such delay (see below) - //for (long long i = 0; i < 100000000; ++i) - // ; - // Now start the new thread to send/receive data - //boost::thread t(boost::bind(&TcpClient::StartChatSession, this)); } +// Connect to some remote peer (server/client) void TcpClient::Connect(const tcp::endpoint& peer) { m_tcpHandler.Initialize(peer); std::cout << "Connected: " << m_tcpHandler.GetDestinationAddress() << std::endl; - - // For test purpose, start a new thread to send/receive data - //boost::thread t(boost::bind(&TcpClient::StartChatSession, this));; } +// Join group by sending request to server void TcpClient::JoinGroup(uint32_t groupId) { m_requests.JoinGroup(m_tcpHandler, groupId); } -#include -// Send/Receive randome data -void TcpClient::StartChatSession() +// Start group chat in a new thread +void TcpClient::StartChatSession(uint32_t groupId) +{ + boost::thread t(boost::bind(&TcpClient::ChatSession, this, _1), groupId); +} + +void TcpClient::ChatSession(uint32_t groupId) { try { - // Generate a string with random numbers - srand((unsigned int)time(NULL)); - std::stringstream ss; - for (int i = 0; i < 10; ++i) - ss << rand()%20 << " "; - std::string str = ss.str(); - std::cout << "\n\nSending Data " << str << std::endl; - + std::cout << "\n\n"; + // A new thread to input chat messages + boost::thread t(boost::bind(&TcpClient::ChatInput, this, _1), groupId); - // Send the string as a chat message - ChatMessage message; - message.SetMessage(str); - message.Send(m_tcpHandler); - - - // keep receiving messages and print them + // In this thread, keep receiving messages and print them + ChatMessage chat; while (true) { - message.Receive(m_tcpHandler); - std::cout << "\n\nReceived Data "; - std::cout << message.GetMessage(); - std::cout << std::endl; + chat.Receive(m_tcpHandler); + std::cout << "\n\n" << chat.GetMessage() << "\n\nYou: "; } } catch (std::exception &ex) @@ -84,3 +64,22 @@ void TcpClient::StartChatSession() } } +void TcpClient::ChatInput(uint32_t groupId) +{ + while (true) + { + // Take input + std::cout << "\nYou: "; + fflush(stdin); + char input[1024]; + + std::cin.getline(input, 1024); + std::string message = std::to_string(m_tcpHandler.GetSocket()->local_endpoint().port()) + ": " + input; + + // Send the chat message after a GROUP_CHAT request + m_requests.GroupChat(m_tcpHandler, groupId); + ChatMessage chat; + chat.SetMessage(message); + chat.Send(m_tcpHandler); + } +} diff --git a/src/client/main.cpp b/src/client/main.cpp index cd19052..46cd96e 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -30,6 +30,7 @@ int main(int argc, char *argv[]) std::cout << "Enter group-id to join: "; std::cin >> groupId; client.JoinGroup(groupId); + client.StartChatSession(groupId); while (true); } } diff --git a/src/common/RequestHandler.cpp b/src/common/RequestHandler.cpp index c6d3396..64f405b 100644 --- a/src/common/RequestHandler.cpp +++ b/src/common/RequestHandler.cpp @@ -9,20 +9,19 @@ RequestHandler::~RequestHandler() void RequestHandler::JoinGroup(TcpHandler &tcpHandler, uint32_t groupId) { - std::string request; - request = "JOIN_REQUEST"; - request += "\t" + std::to_string(groupId); - tcpHandler.Send(request.c_str(), request.size() + 1); + m_request.type = JOIN_GROUP; + m_request.info.join.groupId = groupId; + tcpHandler.Send((char*)&m_request, sizeof(m_request)); +} + +void RequestHandler::GroupChat(TcpHandler &tcpHandler, uint32_t groupId) +{ + m_request.type = GROUP_CHAT; + m_request.info.groupChat.groupId = groupId; + tcpHandler.Send((char*)&m_request, sizeof(m_request));; } void RequestHandler::ReceiveRequest(TcpHandler &tcpHandler) { - char data[REQUEST_MAX_SIZE]; - tcpHandler.Receive(data, REQUEST_MAX_SIZE); - std::string request(data); - if (request.substr(0, 12) == "JOIN_REQUEST") - { - m_request.type = JOIN_GROUP; - m_request.info.join.groupId = std::stoul(request.substr(13).c_str()); - } + tcpHandler.Receive((char*)&m_request, sizeof(m_request)); } \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index b5f1f56..2979f90 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -1,5 +1,6 @@ #include #include +#include ClientsManager::ClientsManager(boost::asio::io_service& io) : m_listener(io), m_ioService(io) @@ -8,6 +9,7 @@ ClientsManager::ClientsManager(boost::asio::io_service& io) ClientsManager::~ClientsManager() {} +// Use listener to listen to incoming clients void ClientsManager::StartListening(const tcp::endpoint &localEndpoint) { m_listener.Initialize(localEndpoint); @@ -17,43 +19,96 @@ void ClientsManager::StartListening(const tcp::endpoint &localEndpoint) // Add any client that is connected to the clients list void ClientsManager::HandleClient(boost::shared_ptr &socket) { - m_lock = true; + m_lock = true; // Lock to ensure "m_clients" vector isn't used while adding client to it + ClientInfo client(m_ioService); client.connection.Initialize(socket); m_clients.push_back(client); - m_lock = false; + std::cout << "Client Connected: " << m_clients[m_clients.size() - 1].connection.GetDestinationAddress() << std::endl; + + m_lock = false; // Un-lock } +// Start processing each client void ClientsManager::StartProcessing() { - m_lock = false; + m_lock = false; // Initially, the "m_clients" vector is un-locked boost::thread t(boost::bind(&ClientsManager::ProcessClients, this)); } +// Process each client void ClientsManager::ProcessClients() { while (true) { - while (m_lock); + while (m_lock) + ; // Don't continue while locked + + // Process each client in turn for (unsigned int i = 0; i < m_clients.size(); ++i) { - while (m_lock); + while (m_lock) + ; // Pause processing while locked try { + // See if any request is incomming for this client size_t bytes = m_clients[i].connection.Available(); + uint32_t gid; + // if so, process accordingly if (bytes > 0) { m_requests.ReceiveRequest(m_clients[i].connection); - if (m_requests.GetLastRequest().type == RequestHandler::JOIN_GROUP) + switch (m_requests.GetLastRequest().type) { - uint32_t gid = m_requests.GetLastRequest().info.join.groupId; - m_groups[gid].push_back(i); // push the client id to the group + case RequestHandler::JOIN_GROUP: + gid = m_requests.GetLastRequest().info.join.groupId; + // push the client id to the group + m_groups[gid].push_back(i); std::cout << "Connected client #" << i << " to group #" << gid << std::endl; + break; + + case RequestHandler::GROUP_CHAT: + gid = m_requests.GetLastRequest().info.groupChat.groupId; + // receive the chat message + ReceiveChat(i, gid); + break; + + default: + std::cout << "Invalid Request Got from client #" << i << std::endl; } } } catch (std::exception &ex) - {} + { + std::cout << ex.what() << std::endl; + } + } + } +} + +void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) +{ + ChatMessage chat; + chat.Receive(m_clients[client].connection); + while (m_lock) + ; // while locked, don't continue + + // Send the messsage to each client in the group + for (unsigned int i = 0; i < m_groups[group].size(); ++i) + { + try + { + if (m_groups[group][i] != client) + chat.Send(m_clients[m_groups[group][i]].connection); + while (m_lock) + ; // while locked, don't continue + } + // client may be disconnected and exception may be thrown + // we catch the exception inside the loop so that + // we can continue sending messages to other clients + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; } } } \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index 56abb38..94dee98 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -13,12 +13,12 @@ int main() manager.StartListening(tcp::endpoint(tcp::v4(), 8183)); manager.StartProcessing(); - while (true) - { - // for every new connection, print its address - for (; clientsNumber < manager.GetClients().size(); ++clientsNumber) - std::cout << "Client Connected: " << manager.GetClients()[clientsNumber].connection.GetDestinationAddress() << std::endl; - } + //while (true) + //{ + // // for every new connection, print its address + // for (; clientsNumber < manager.GetClients().size(); ++clientsNumber) + // std::cout << "Client Connected: " << manager.GetClients()[clientsNumber].connection.GetDestinationAddress() << std::endl; + //} std::cin.get(); return 0; From f882fd391c801c676fc0ab08260301d76047b4e6 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Sat, 13 Dec 2014 20:31:42 +0545 Subject: [PATCH 29/54] balla balla kaam garne makefile banyo --- include/common/RequestHandler.h | 2 +- include/common/common.h | 4 ++-- main.cpp | 7 ------- makefile | 15 ++++++++------- makefile_client | 28 ++++++++++++++++++++++++++++ output | Bin 0 -> 408519 bytes 6 files changed, 39 insertions(+), 17 deletions(-) delete mode 100644 main.cpp create mode 100644 makefile_client create mode 100755 output diff --git a/include/common/RequestHandler.h b/include/common/RequestHandler.h index 62f1ddc..65e9157 100644 --- a/include/common/RequestHandler.h +++ b/include/common/RequestHandler.h @@ -27,4 +27,4 @@ class RequestHandler const Request& GetLastRequest() { return m_request; } private: Request m_request; -}; \ No newline at end of file +}; diff --git a/include/common/common.h b/include/common/common.h index 4d0fc5b..22594c2 100644 --- a/include/common/common.h +++ b/include/common/common.h @@ -10,10 +10,10 @@ #include #include -#include +//#include #include "Exception.h" using boost::asio::ip::udp; -using boost::asio::ip::tcp; \ No newline at end of file +using boost::asio::ip::tcp; diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 4b2767a..0000000 --- a/main.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -int main() -{ - std::cout << " in main, compiled.. "; -} diff --git a/makefile b/makefile index c8c5304..96ea3e3 100644 --- a/makefile +++ b/makefile @@ -1,25 +1,26 @@ -CFLAGS = `pkg-config --cflags gtk+-3.0` LIBFLAGS = `pkg-config --libs gtk+-3.0` +CFLAG = `pkg-config --cflags gtk+-3.0` INC_DIR = include SRC_DIR = src CC = g++ +CFLAGS = --std=c++11 #sources for common SOURCES_COM = TcpHandler.cpp TcpListener.cpp HEADERS_COM = TcpHandler.h TcpListener.h -OBJECTS_COM = $(SOURCES_COM:.CPP=.o) +OBJECTS_COM = $(SOURCES_COM:.cpp=.o) FSOURCES_COM := $(addprefix $(SRC_DIR)/common/,$(SOURCES_COM)) FHEADERS_COM := $(addprefix $(INC_DIR)/common/,$(HEADERS_FILE)) LDFLAGS_COM = -lboost_system -lboost_thread -lpthread -client: tcphandler.o tcplistener.o - $(CC) main.cpp -o $@ +client: tcplistener.o tcphandler.o + $(CC) main.cpp -o $@ $(CFLAGS) tcphandler.o : $(FHEADERS_COM) - $(CC) -c $(SRC_DIR)/common/TcpHandler.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) + $(CC) -c $(CFLAGS) $(SRC_DIR)/common/TcpHandler.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) $(CFLAG) -tcplistner.o : $(FHEADERS_COM) - $(CC) -c $(SRC_DIR)/common/TcpListener.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) +tcplistener.o : $(FHEADERS_COM) + $(CC) -c $(CFLAGS) $(SRC_DIR)/common/TcpListener.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) $(CFLAG) diff --git a/makefile_client b/makefile_client new file mode 100644 index 0000000..00314c0 --- /dev/null +++ b/makefile_client @@ -0,0 +1,28 @@ +INC_DIR = include +SRC_DIR = src + +CFLAG = `pkg-config --cflags gtk+-3.0` +CFLAGS = --std=c++11 + +CC = g++ + +#for common +SOURCES_COMM = TcpHandler.cpp TcpListener.cpp RequestHandler.cpp ChatMessage.cpp +HEADERS_COMM = TcpHandler.h TcpListner.h RequestHandler.h +OBJECTS_COMM = $(SOURCES_COMM:.cpp=.o) +FSOURCES_COMM := $(addprefix $(SRC_DIR)/common/, $(SOURCES_COMM)) +FHEADERS_COMM := $(addprefix $(INC_DIR)/common/, $(HEADERS_COMM)) + +LDFLAGS_COMM = -lboost_system -lboost_thread -lpthread + +all: client.o common + $(CC) -o output $(SRC_DIR)/client/main.cpp *.o -Iinclude/ $(CFLAGS) $(CFLAG) $(LDFLAGS_COMM) + +common: $(OBJECTS_COMM) + $(CC) -c -o common.o $< + +%.o : src/common/%.cpp $(FHEADERS_COMM) + $(CC) -c -o $@ $< $(LDFLAGS_COMM) -Iinclude/ $(CFLAG) $(CFLAGS) + +client.o: src/client/TcpClient.cpp + $(CC) -c -o $@ $< -Iinclude/ $(CFLAG) $(CFLAGS) diff --git a/output b/output new file mode 100755 index 0000000000000000000000000000000000000000..9d592d5b7f796ecd59cc2ff7dacd377146290601 GIT binary patch literal 408519 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 Date: Sun, 14 Dec 2014 19:12:09 +0545 Subject: [PATCH 30/54] Add automatic free port detection and manual input of server IP --- include/client/TcpClient.h | 2 +- include/common/TcpListener.h | 2 +- include/server/ClientsManager.h | 4 ++-- src/client/TcpClient.cpp | 6 ++++-- src/client/main.cpp | 12 +++++++----- src/common/TcpListener.cpp | 24 ++++++++++++++++-------- src/server/ClientsManager.cpp | 24 +++++++++--------------- 7 files changed, 40 insertions(+), 34 deletions(-) diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index ba62d9e..152e077 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -10,7 +10,7 @@ class TcpClient ~TcpClient(); // Start listening for incoming connections in a new thread - void StartListening(const tcp::endpoint& localEndPoint); + void StartListening(/*const tcp::endpoint& localEndPoint*/); // Connect to a peer (server/client) void Connect(const tcp::endpoint& peer); diff --git a/include/common/TcpListener.h b/include/common/TcpListener.h index a02d655..85f0025 100644 --- a/include/common/TcpListener.h +++ b/include/common/TcpListener.h @@ -15,7 +15,7 @@ class TcpListener ~TcpListener(); // Initialize the listener to listen at given local endpoint - void Initialize(const tcp::endpoint &localEndpoint); + uint16_t Initialize(const tcp::endpoint &localEndpoint); // Listen for incomming connections in a new thread // and call 'callback' for each connection void Listen(boost::function)> callback); diff --git a/include/server/ClientsManager.h b/include/server/ClientsManager.h index 67a9c1b..b5f9f8f 100644 --- a/include/server/ClientsManager.h +++ b/include/server/ClientsManager.h @@ -17,7 +17,7 @@ class ClientsManager // Start a new thread to listen to and accept clients void StartListening(const tcp::endpoint &localEndpoint); - // Start another thread to process each client + // Start an infinite loop to process each client void StartProcessing(); // Get the connected clients std::vector& GetClients() { return m_clients; } @@ -26,7 +26,7 @@ class ClientsManager boost::asio::io_service &m_ioService; TcpListener m_listener; RequestHandler m_requests; - bool m_lock; + boost::mutex m_mutex; // Clients List std::vector m_clients; diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index e3ee07e..c4b6865 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -9,9 +9,11 @@ TcpClient::TcpClient(boost::asio::io_service &io) TcpClient::~TcpClient() {} -void TcpClient::StartListening(const tcp::endpoint& localEndPoint) +void TcpClient::StartListening(/*const tcp::endpoint& localEndPoint*/) { - m_listener.Initialize(localEndPoint); + uint16_t port = m_listener.Initialize(tcp::endpoint(tcp::v4(), 0)); + std::cout << "Listening at port: " << port << std::endl; + // Listen method will start listening in new thread m_listener.Listen(boost::bind(&TcpClient::ListenerHandler, this, _1)); } diff --git a/src/client/main.cpp b/src/client/main.cpp index 46cd96e..646e10f 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -10,10 +10,10 @@ int main(int argc, char *argv[]) { boost::asio::io_service io; TcpClient client(io); - uint16_t port; - std::cout << "Enter port of this client: "; - std::cin >> port; - client.StartListening(tcp::endpoint(tcp::v4(), port)); + //uint16_t port; + //std::cout << "Enter port of this client: "; + //std::cin >> port; + client.StartListening(/*tcp::endpoint(tcp::v4(), port)*/); // Connect to another peer /*{ @@ -25,7 +25,9 @@ int main(int argc, char *argv[]) // Connect to server { - client.Connect(tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 8183)); + std::string ip; + std::cout << "Enter ip of server: "; std::cin >> ip; + client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 8183)); uint32_t groupId; std::cout << "Enter group-id to join: "; std::cin >> groupId; diff --git a/src/common/TcpListener.cpp b/src/common/TcpListener.cpp index 08bf421..11ee73e 100644 --- a/src/common/TcpListener.cpp +++ b/src/common/TcpListener.cpp @@ -7,7 +7,7 @@ TcpListener::TcpListener(boost::asio::io_service &io) : m_acceptor(io) TcpListener::~TcpListener() {} -void TcpListener::Initialize(const tcp::endpoint &localEndpoint) +uint16_t TcpListener::Initialize(const tcp::endpoint &localEndpoint) { // Open and bind the tcp acceptor to the local endpoint m_acceptor.open(localEndpoint.protocol()); @@ -15,6 +15,7 @@ void TcpListener::Initialize(const tcp::endpoint &localEndpoint) m_acceptor.bind(localEndpoint); // Prepare to listen m_acceptor.listen(); + return m_acceptor.local_endpoint().port(); } void TcpListener::Listen(boost::function)> callback) @@ -31,13 +32,20 @@ void TcpListener::StartListening() void TcpListener::ListeningThread() { - while (true) + try { - // Create a new socket to represent a new connection - boost::shared_ptr socket(new tcp::socket(m_acceptor.get_io_service())); - // Wait for a connection and accept at the socket - m_acceptor.accept(*socket); - // Send the socket to the handler - m_callback(socket); + while (true) + { + // Create a new socket to represent a new connection + boost::shared_ptr socket(new tcp::socket(m_acceptor.get_io_service())); + // Wait for a connection and accept at the socket + m_acceptor.accept(*socket); + // Send the socket to the handler + m_callback(socket); + } + } + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; } } \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 2979f90..2cbb7ec 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -19,21 +19,18 @@ void ClientsManager::StartListening(const tcp::endpoint &localEndpoint) // Add any client that is connected to the clients list void ClientsManager::HandleClient(boost::shared_ptr &socket) { - m_lock = true; // Lock to ensure "m_clients" vector isn't used while adding client to it - + boost::lock_guard guard(m_mutex); ClientInfo client(m_ioService); client.connection.Initialize(socket); m_clients.push_back(client); std::cout << "Client Connected: " << m_clients[m_clients.size() - 1].connection.GetDestinationAddress() << std::endl; - - m_lock = false; // Un-lock } // Start processing each client void ClientsManager::StartProcessing() { - m_lock = false; // Initially, the "m_clients" vector is un-locked - boost::thread t(boost::bind(&ClientsManager::ProcessClients, this)); + //boost::thread t(boost::bind(&ClientsManager::ProcessClients, this)); + ProcessClients(); } // Process each client @@ -41,14 +38,12 @@ void ClientsManager::ProcessClients() { while (true) { - while (m_lock) - ; // Don't continue while locked - + while (m_clients.size() == 0) + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + m_mutex.lock(); // Process each client in turn for (unsigned int i = 0; i < m_clients.size(); ++i) { - while (m_lock) - ; // Pause processing while locked try { // See if any request is incomming for this client @@ -83,6 +78,7 @@ void ClientsManager::ProcessClients() std::cout << ex.what() << std::endl; } } + m_mutex.unlock(); } } @@ -90,8 +86,7 @@ void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) { ChatMessage chat; chat.Receive(m_clients[client].connection); - while (m_lock) - ; // while locked, don't continue + m_mutex.lock(); // Send the messsage to each client in the group for (unsigned int i = 0; i < m_groups[group].size(); ++i) @@ -100,8 +95,6 @@ void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) { if (m_groups[group][i] != client) chat.Send(m_clients[m_groups[group][i]].connection); - while (m_lock) - ; // while locked, don't continue } // client may be disconnected and exception may be thrown // we catch the exception inside the loop so that @@ -111,4 +104,5 @@ void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) std::cout << ex.what() << std::endl; } } + m_mutex.unlock(); } \ No newline at end of file From c4f1d78b271f1e6a54a8199a491624ad95f93212 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Sun, 14 Dec 2014 22:13:43 +0545 Subject: [PATCH 31/54] Server default port changed to 10011 --- src/client/main.cpp | 2 +- src/server/ClientsManager.cpp | 4 ++-- src/server/main.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/main.cpp b/src/client/main.cpp index 646e10f..57ebb20 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -27,7 +27,7 @@ int main(int argc, char *argv[]) { std::string ip; std::cout << "Enter ip of server: "; std::cin >> ip; - client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 8183)); + client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 10011/*8183*/)); // 10011 for Ankit's Server uint32_t groupId; std::cout << "Enter group-id to join: "; std::cin >> groupId; diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 2cbb7ec..f5ebaab 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -86,7 +86,7 @@ void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) { ChatMessage chat; chat.Receive(m_clients[client].connection); - m_mutex.lock(); + //m_mutex.lock(); // Send the messsage to each client in the group for (unsigned int i = 0; i < m_groups[group].size(); ++i) @@ -104,5 +104,5 @@ void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) std::cout << ex.what() << std::endl; } } - m_mutex.unlock(); + //m_mutex.unlock(); } \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index 94dee98..2711c33 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -8,9 +8,9 @@ int main() boost::asio::io_service io_service; ClientsManager manager(io_service); - size_t clientsNumber = 0; + //size_t clientsNumber = 0; // 8183 is the port of this server; 8183 == BIBE(K) :P - manager.StartListening(tcp::endpoint(tcp::v4(), 8183)); + manager.StartListening(tcp::endpoint(tcp::v4(), /*8183*/10011)); // 10011 for Ankit's Server manager.StartProcessing(); //while (true) From a0e7b94c2e2648ace878266e01afb5d7b560528f Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Mon, 15 Dec 2014 20:30:23 +0545 Subject: [PATCH 32/54] okie, some changes --- src/client/main.cpp | 2 +- src/server/ClientsManager.cpp | 4 ++-- src/server/main.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/main.cpp b/src/client/main.cpp index 646e10f..51bc769 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -27,7 +27,7 @@ int main(int argc, char *argv[]) { std::string ip; std::cout << "Enter ip of server: "; std::cin >> ip; - client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 8183)); + client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 10011)); uint32_t groupId; std::cout << "Enter group-id to join: "; std::cin >> groupId; diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 2cbb7ec..bf84d0d 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -86,7 +86,7 @@ void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) { ChatMessage chat; chat.Receive(m_clients[client].connection); - m_mutex.lock(); + //m_mutex.lock(); // Send the messsage to each client in the group for (unsigned int i = 0; i < m_groups[group].size(); ++i) @@ -104,5 +104,5 @@ void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) std::cout << ex.what() << std::endl; } } - m_mutex.unlock(); + // m_mutex.unlock(); } \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index 94dee98..880d009 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -10,7 +10,7 @@ int main() size_t clientsNumber = 0; // 8183 is the port of this server; 8183 == BIBE(K) :P - manager.StartListening(tcp::endpoint(tcp::v4(), 8183)); + manager.StartListening(tcp::endpoint(tcp::v4(), 10011)); manager.StartProcessing(); //while (true) From 2202ab82193fe25d3c5bc7f08e1ea2ead1e38df9 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Tue, 16 Dec 2014 19:08:35 +0545 Subject: [PATCH 33/54] Add rapidjson Requests in JSON format --- include/common/RequestHandler.h | 41 +- include/common/TcpHandler.h | 11 +- include/rapidjson/allocators.h | 245 +++ include/rapidjson/document.h | 1927 +++++++++++++++++++++++ include/rapidjson/encodedstream.h | 290 ++++ include/rapidjson/encodings.h | 630 ++++++++ include/rapidjson/error/en.h | 71 + include/rapidjson/error/error.h | 150 ++ include/rapidjson/filereadstream.h | 94 ++ include/rapidjson/filestream.h | 73 + include/rapidjson/filewritestream.h | 97 ++ include/rapidjson/internal/biginteger.h | 290 ++++ include/rapidjson/internal/diyfp.h | 268 ++++ include/rapidjson/internal/dtoa.h | 225 +++ include/rapidjson/internal/ieee754.h | 90 ++ include/rapidjson/internal/itoa.h | 306 ++++ include/rapidjson/internal/meta.h | 189 +++ include/rapidjson/internal/pow10.h | 59 + include/rapidjson/internal/stack.h | 183 +++ include/rapidjson/internal/strfunc.h | 43 + include/rapidjson/internal/strtod.h | 285 ++++ include/rapidjson/memorybuffer.h | 76 + include/rapidjson/memorystream.h | 67 + include/rapidjson/msinttypes/inttypes.h | 312 ++++ include/rapidjson/msinttypes/stdint.h | 296 ++++ include/rapidjson/prettywriter.h | 205 +++ include/rapidjson/rapidjson.h | 628 ++++++++ include/rapidjson/reader.h | 1446 +++++++++++++++++ include/rapidjson/stringbuffer.h | 99 ++ include/rapidjson/writer.h | 391 +++++ src/client/TcpClient.cpp | 3 + src/client/main.cpp | 5 +- src/common/RequestHandler.cpp | 79 +- src/common/TcpHandler.cpp | 10 +- src/server/ClientsManager.cpp | 6 +- 35 files changed, 9150 insertions(+), 40 deletions(-) create mode 100644 include/rapidjson/allocators.h create mode 100644 include/rapidjson/document.h create mode 100644 include/rapidjson/encodedstream.h create mode 100644 include/rapidjson/encodings.h create mode 100644 include/rapidjson/error/en.h create mode 100644 include/rapidjson/error/error.h create mode 100644 include/rapidjson/filereadstream.h create mode 100644 include/rapidjson/filestream.h create mode 100644 include/rapidjson/filewritestream.h create mode 100644 include/rapidjson/internal/biginteger.h create mode 100644 include/rapidjson/internal/diyfp.h create mode 100644 include/rapidjson/internal/dtoa.h create mode 100644 include/rapidjson/internal/ieee754.h create mode 100644 include/rapidjson/internal/itoa.h create mode 100644 include/rapidjson/internal/meta.h create mode 100644 include/rapidjson/internal/pow10.h create mode 100644 include/rapidjson/internal/stack.h create mode 100644 include/rapidjson/internal/strfunc.h create mode 100644 include/rapidjson/internal/strtod.h create mode 100644 include/rapidjson/memorybuffer.h create mode 100644 include/rapidjson/memorystream.h create mode 100644 include/rapidjson/msinttypes/inttypes.h create mode 100644 include/rapidjson/msinttypes/stdint.h create mode 100644 include/rapidjson/prettywriter.h create mode 100644 include/rapidjson/rapidjson.h create mode 100644 include/rapidjson/reader.h create mode 100644 include/rapidjson/stringbuffer.h create mode 100644 include/rapidjson/writer.h diff --git a/include/common/RequestHandler.h b/include/common/RequestHandler.h index bb8d7ad..7309069 100644 --- a/include/common/RequestHandler.h +++ b/include/common/RequestHandler.h @@ -1,28 +1,18 @@ #pragma once #include "TcpHandler.h" +#include "rapidjson/document.h" -const size_t REQUEST_MAX_SIZE = 150; +class RequestException : public Exception +{ +public: + RequestException(const std::string &errorString) + :Exception("Error processing request: "+errorString) + {} +}; class RequestHandler { public: - enum REQUEST_TYPE{ JOIN_GROUP = 0, GROUP_CHAT }; - struct Request - { - REQUEST_TYPE type; // Request type - union // Union of request data for each type - { - // JOIN_GROUP - struct - { - uint32_t groupId; // group-id to join - } join; - // GROUP_CHAT - struct - { - uint32_t groupId; // group-id to send chat message to - } groupChat; - } info; - }; + enum REQUEST_TYPE{ INAVLID_TYPE = 0, JOIN_GROUP, GROUP_CHAT }; RequestHandler(); ~RequestHandler(); @@ -31,12 +21,15 @@ class RequestHandler void JoinGroup(TcpHandler &tcpHandler, uint32_t groupId); // Request the server to wait for incoming chat message for a group void GroupChat(TcpHandler &tcpHandler, uint32_t groupId); - - // Receive a request void ReceiveRequest(TcpHandler &tcpHandler); - // Get last sent/received request - const Request& GetLastRequest() { return m_request; } + + // Request Data + REQUEST_TYPE GetRequestType(); + uint32_t GetGroupId(); private: - Request m_request; + // The Json Document that holds the request + rapidjson::Document m_document; + // Get string from the Json Doucment + std::string GetJsonString(); }; diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index 9fc4cfc..2a8b9b1 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -18,13 +18,18 @@ class TcpHandler TcpHandler(boost::asio::io_service &ioService); ~TcpHandler(); - // initialize with a socket + // Initialize with an already created socket void Initialize(boost::shared_ptr socket); - // send tcp request to the endpoint and create socket + // Connect to given endpoint and initialize by creating new socker void Initialize(const tcp::endpoint &destEndpoint); + // Send data of given size void Send(const char* data, size_t size); - void Receive(char* data, size_t max_size); + // Receive data of given size + void Receive(char* data, size_t size); + // Receive data of given size or less + void ReceiveSome(char* data, size_t max_size); + // Check if the socket has data to be read size_t Available(); std::string GetDestinationAddress() const; diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h new file mode 100644 index 0000000..0bd2d28 --- /dev/null +++ b/include/rapidjson/allocators.h @@ -0,0 +1,245 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { return std::malloc(size); } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return std::realloc(originalPtr, newSize); } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while(chunkHead_ != 0 && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); + + void *buffer = reinterpret_cast(chunkHead_ + 1) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + increment = RAPIDJSON_ALIGN(increment); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + void* newBuffer = Malloc(newSize); + RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. + return std::memcpy(newBuffer, originalPtr, originalSize); + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + */ + void AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h new file mode 100644 index 0000000..07e3fe3 --- /dev/null +++ b/include/rapidjson/document.h @@ -0,0 +1,1927 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#include +#endif // RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::iterator, std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +struct GenericMember { + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator + : public std::iterator >::Type> { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef std::iterator BaseType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + //! Pointer to (const) GenericMember + typedef typename BaseType::pointer Pointer; + //! Reference to (const) GenericMember + typedef typename BaseType::reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef typename BaseType::difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } + bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } + bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } + bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } + bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } + bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +struct GenericMemberIterator; + +//! non-const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } + + //! Create constant string reference from pointer and length + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow copy-assignment + GenericStringRef operator=(const GenericStringRef&); + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; +}; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template > +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { + rhs.flags_ = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { + static const unsigned defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kConstStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_ASSERT(type <= kNumberType); + flags_ = defaultFlags[type]; + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \see CopyFrom() + */ + template< typename SourceAllocator > + GenericValue(const GenericValue& rhs, Allocator & allocator); + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { + data_.n.i64 = i; + if (i >= 0) + flags_ |= kUintFlag | kUint64Flag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { + data_.n.u64 = u; + if (!(u & 0x80000000)) + flags_ |= kIntFlag | kInt64Flag; + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { + data_.n.i64 = i64; + if (i64 >= 0) { + flags_ |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { + data_.n.u64 = u64; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + flags_ |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(flags_) { + case kArrayFlag: + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(data_.a.elements); + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(data_.o.members); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(data_.s.str)); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + RAPIDJSON_ASSERT(this != &rhs); + this->~GenericValue(); + RawAssign(rhs); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) + return GetDouble() == rhs.GetDouble(); // May convert one operand from integer to double. + else + return data_.n.u64 == rhs.data_.n.u64; + + default: // kTrueType, kFalseType, kNullType + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(flags_ & kTypeMask); } + bool IsNull() const { return flags_ == kNullFlag; } + bool IsFalse() const { return flags_ == kFalseFlag; } + bool IsTrue() const { return flags_ == kTrueFlag; } + bool IsBool() const { return (flags_ & kBoolFlag) != 0; } + bool IsObject() const { return flags_ == kObjectFlag; } + bool IsArray() const { return flags_ == kArrayFlag; } + bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } + bool IsInt() const { return (flags_ & kIntFlag) != 0; } + bool IsUint() const { return (flags_ & kUintFlag) != 0; } + bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } + bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } + bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } + bool IsString() const { return (flags_ & kStringFlag) != 0; } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + static GenericValue NullValue; + return NullValue; + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + Object& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); + } + } + o.members[o.size].name.RawAssign(name); + o.members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + GenericValue v(value); + return AddMember(n, v, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(data_.o.members + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) { + // Move the last one to this place + *m = *last; + } + else { + // Only one left, just destroy + m->~Member(); + } + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member)); + data_.o.size -= (last - first); + return pos; + } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + for (SizeType i = 0; i < data_.a.size; ++i) + data_.a.elements[i].~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return data_.a.elements[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + data_.a.elements[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + data_.a.elements[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(data_.a.elements != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(pos, last, (End() - last) * sizeof(GenericValue)); + data_.a.size -= (last - first); + return pos; + } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } + + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision) + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), s.size(), allocator); } +#endif + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (!handler.StartObject()) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) + return false; + if (!m->value.Accept(handler)) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (!handler.StartArray()) + return false; + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + if (!v->Accept(handler)) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); + + case kNumberType: + if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else if (IsUint64()) return handler.Uint64(data_.n.u64); + else return handler.Double(data_.n.d); + + default: + RAPIDJSON_ASSERT(false); + } + return false; + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x100, + kNumberFlag = 0x200, + kIntFlag = 0x400, + kUintFlag = 0x800, + kInt64Flag = 0x1000, + kUint64Flag = 0x2000, + kDoubleFlag = 0x4000, + kStringFlag = 0x100000, + kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, + + // Initial flags of different types. + kNullFlag = kNullType, + kTrueFlag = kTrueType | kBoolFlag, + kFalseFlag = kFalseType | kBoolFlag, + kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, + kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, + kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, + kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, + kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, + kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, + kConstStringFlag = kStringType | kStringFlag, + kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct String { + const Ch* str; + SizeType length; + unsigned hashcode; //!< reserved + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode + // inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } + inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct Object { + Member* members; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct Array { + GenericValue* elements; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + Object o; + Array a; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + flags_ = kArrayFlag; + data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); + std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + flags_ = kObjectFlag; + data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); + std::memcpy(data_.o.members, members, count * sizeof(Member)); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + flags_ = kConstStringFlag; + data_.s.str = s; + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = NULL; + if(ShortString::Usable(s.length)) { + flags_ = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + flags_ = kCopyStringFlag; + data_.s.length = s.length; + str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); + data_.s.str = str; + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + flags_ = rhs.flags_; + rhs.flags_ = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; + unsigned flags_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template , typename StackAllocator = CrtAllocator> +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::move(rhs)), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + ValueType::SetNull(); // Remove existing root if exist + GenericReader reader(&GetAllocator()); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Transcoding from input Encoding + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { return *allocator_; } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + +// defined here due to the dependency on GenericDocument +template +template +inline +GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop(1)); + } + break; + case kStringType: + if (rhs.flags_ == kConstStringFlag) { + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: // kNumberType, kTrueType, kFalseType, kNullType + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } +} + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h new file mode 100644 index 0000000..ee8caa0 --- /dev/null +++ b/include/rapidjson/encodedstream.h @@ -0,0 +1,290 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); } + Ch Take() { RAPIDJSON_ASSERT(false); } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = (const unsigned char *)is_->Peek4(); + if (!c) + return; + + unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + switch (type_) { + case kUTF8: + // Do nothing + break; + case kUTF16LE: + case kUTF16BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + break; + case kUTF32LE: + case kUTF32BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + break; + default: + RAPIDJSON_ASSERT(false); // Invalid type + } + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam InputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. + switch (type_) { + case kUTF16LE: + case kUTF16BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + break; + case kUTF32LE: + case kUTF32BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + break; + case kUTF8: + // Do nothing + break; + default: + RAPIDJSON_ASSERT(false); // Invalid UTFType + } + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); } + Ch Take() { RAPIDJSON_ASSERT(false); } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h new file mode 100644 index 0000000..71595f7 --- /dev/null +++ b/include/rapidjson/encodings.h @@ -0,0 +1,630 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = (unsigned char)c; + return true; + } + + unsigned char type = GetRange((unsigned char)c); + *codepoint = (0xFF >> type) & (unsigned char)c; + bool result = true; + switch (type) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define COPY() os.Put(c = is.Take()) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange((unsigned char)c)) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + Ch c = Take(is); + if ((unsigned char)c != 0xEFu) return c; + c = is.Take(); + if ((unsigned char)c != 0xBBu) return c; + c = is.Take(); + if ((unsigned char)c != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return is.Take(); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put((v & 0x3FF) | 0xDC00); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = c; + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (c & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (c & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + Ch c; + os.Put(c = is.Take()); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned short)c == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFFu); os.Put(0xFEu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned short)c == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFEu); os.Put(0xFFu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put((c >> 8) & 0xFFu); + os.Put(c & 0xFFu); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 24; + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 24) & 0xFFu); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take() << 24; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put((c >> 24) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put(c & 0xFFu); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + unsigned char c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + unsigned char c = is.Take(); + os.Put(c); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + Ch c = Take(is); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return is.Take(); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSV_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h new file mode 100644 index 0000000..0171183 --- /dev/null +++ b/include/rapidjson/error/en.h @@ -0,0 +1,71 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ERROR_EN_H__ +#define RAPIDJSON_ERROR_EN_H__ + +#include "error.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: + return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ERROR_EN_H__ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h new file mode 100644 index 0000000..161ef87 --- /dev/null +++ b/include/rapidjson/error/error.h @@ -0,0 +1,150 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ERROR_ERROR_H__ +#define RAPIDJSON_ERROR_ERROR_H__ + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError, //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ERROR_ERROR_H__ diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h new file mode 100644 index 0000000..5af9be5 --- /dev/null +++ b/include/rapidjson/filereadstream.h @@ -0,0 +1,94 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "rapidjson.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filestream.h b/include/rapidjson/filestream.h new file mode 100644 index 0000000..a370c60 --- /dev/null +++ b/include/rapidjson/filestream.h @@ -0,0 +1,73 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_FILESTREAM_H_ +#define RAPIDJSON_FILESTREAM_H_ + +#include "rapidjson.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN + +//! (Deprecated) Wrapper of C file stream for input or output. +/*! + This simple wrapper does not check the validity of the stream. + \note implements Stream concept + \note deprecated: This was only for basic testing in version 0.1, it is found that the performance is very low by using fgetc(). Use FileReadStream instead. +*/ +class FileStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileStream(std::FILE* fp) : fp_(fp), current_('\0'), count_(0) { Read(); } + char Peek() const { return current_; } + char Take() { char c = current_; Read(); return c; } + size_t Tell() const { return count_; } + void Put(char c) { fputc(c, fp_); } + void Flush() { fflush(fp_); } + + // Not implemented + char* PutBegin() { return 0; } + size_t PutEnd(char*) { return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileStream(const FileStream&); + FileStream& operator=(const FileStream&); + + void Read() { + RAPIDJSON_ASSERT(fp_ != 0); + int c = fgetc(fp_); + if (c != EOF) { + current_ = (char)c; + count_++; + } + else if (current_ != '\0') + current_ = '\0'; + } + + std::FILE* fp_; + char current_; + size_t count_; +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filewritestream.h b/include/rapidjson/filewritestream.h new file mode 100644 index 0000000..4352c8f --- /dev/null +++ b/include/rapidjson/filewritestream.h @@ -0,0 +1,97 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "rapidjson.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h new file mode 100644 index 0000000..06a442b --- /dev/null +++ b/include/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint32_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= 1220703125u; // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Return false if this < rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + if (cmp == 0) { + *out = BigInteger(0); + return false; + } + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10 + (*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + unsigned __int128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = p >> 64; + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static Type FullAdd(Type a, Type b, bool inCarry, bool* outCarry) { + Type c = a + b + (inCarry ? 1 : 0); + *outCarry = c < a; + return c; + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h new file mode 100644 index 0000000..174b9fa --- /dev/null +++ b/include/rapidjson/internal/diyfp.h @@ -0,0 +1,268 @@ +// Copyright (C) 2011 Milo Yip +// +// 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. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#if defined(_MSC_VER) +#include +#if defined(_M_AMD64) +#pragma intrinsic(_BitScanReverse64) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +struct DiyFp { + DiyFp() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + unsigned __int128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = p >> 64; + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) && __GNUC__ >= 4 + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & (static_cast(1) << 63))) { + res.f <<= 1; + res.e--; + } + return res; +#endif + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + uint64_t significand = f; + int exponent = e; + while (significand > kDpHiddenBit + kDpSignificandMask) { + significand >>= 1; + exponent++; + } + while (exponent > kDpDenormalExponent && (significand & kDpHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + if (exponent >= kDpMaxExponent) { + u.u64 = kDpExponentMask; // Infinity + return u.d; + } + else if (exponent < kDpDenormalExponent) + return 0.0; + const uint64_t be = (exponent == kDpDenormalExponent && (significand & kDpHiddenBit) == 0) ? 0 : + static_cast(exponent + kDpExponentBias); + u.u64 = (significand & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (k != dk) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + unsigned index = (exp + 348) / 8; + *outExp = -348 + index * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h new file mode 100644 index 0000000..c0fa2b8 --- /dev/null +++ b/include/rapidjson/internal/dtoa.h @@ -0,0 +1,225 @@ +// Copyright (C) 2011 Milo Yip +// +// 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. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + if (n < 1000000000) return 9; + return 10; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); + *len = 0; + + while (kappa > 0) { + uint32_t d; + switch (kappa) { + case 10: d = p1 / 1000000000; p1 %= 1000000000; break; + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default: +#if defined(_MSC_VER) + __assume(0); +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + __builtin_unreachable(); +#else + d = 0; +#endif + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (length <= kk && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], length - kk); + buffer[kk] = '.'; + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], length); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + return &buffer[length + offset]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], length - 1); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer) { + if (value == 0) { + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h new file mode 100644 index 0000000..ab65cc9 --- /dev/null +++ b/include/rapidjson/internal/ieee754.h @@ -0,0 +1,90 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d(d) {} + Double(uint64_t u) : u(u) {} + + double Value() const { return d; } + uint64_t Uint64Value() const { return u; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u + 1).Value(); + } + + double PreviousPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + if (d == 0.0) + return 0.0; + else + return Double(u - 1).Value(); + } + + bool Sign() const { return (u & kSignMask) != 0; } + uint64_t Significand() const { return u & kSignificandMask; } + int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; } + + bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNormal() const { return (u & kExponentMask) != 0 || Significand() == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; } + + static unsigned EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d; + uint64_t u; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h new file mode 100644 index 0000000..3684f07 --- /dev/null +++ b/include/rapidjson/internal/itoa.h @@ -0,0 +1,306 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + + return u32toa(static_cast(value), buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + if (value >= kTen8) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + + return u64toa(static_cast(value), buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h new file mode 100644 index 0000000..c33f607 --- /dev/null +++ b/include/rapidjson/internal/meta.h @@ -0,0 +1,189 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#error not yet included. Do not include this file directly. +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/include/rapidjson/internal/pow10.h b/include/rapidjson/internal/pow10.h new file mode 100644 index 0000000..91cf647 --- /dev/null +++ b/include/rapidjson/internal/pow10.h @@ -0,0 +1,59 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h new file mode 100644 index 0000000..62ae7aa --- /dev/null +++ b/include/rapidjson/internal/stack.h @@ -0,0 +1,183 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + RAPIDJSON_ASSERT(stackCapacity > 0); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + // Expand the stack if needed + if (stackTop_ + sizeof(T) * count >= stackEnd_) + Expand(count); + + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* Bottom() { return (T*)stack_; } + + Allocator& GetAllocator() { return *allocator_; } + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STACK_H_ diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h new file mode 100644 index 0000000..734adc3 --- /dev/null +++ b/include/rapidjson/internal/strfunc.h @@ -0,0 +1,43 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h new file mode 100644 index 0000000..1fc6050 --- /dev/null +++ b/include/rapidjson/internal/strtod.h @@ -0,0 +1,285 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "../rapidjson.h" +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adjustToNegative) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2; + + BigInteger bS(bInt); + bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2; + + BigInteger hS(1); + hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; + + BigInteger delta(0); + *adjustToNegative = dS.Difference(bS, &delta); + + int cmp = delta.Compare(hS); + // If delta is within 1/2 ULP, check for special case when significand is power of two. + // In this case, need to compare with 1/2h in the lower bound. + if (cmp < 0 && *adjustToNegative && // within and dS < bS + db.IsNormal() && (bInt & (bInt - 1)) == 0 && // Power of 2 + db.Uint64Value() != RAPIDJSON_UINT64_C2(0x00100000, 0x00000000)) // minimum normal number must not do this + { + delta <<= 1; + return delta.Compare(hS); + } + return cmp; +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { + uint64_t significand = 0; + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < length; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10 + (decimals[i] - '0'); + } + + if (i < length && decimals[i] >= '5') // Rounding + significand++; + + size_t remaining = length - i; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = (int)decimalPosition - (int)i + exp; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp - 1; + RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); + v = v * kPow10[adjustment]; + if (length + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + unsigned scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + error) + rounded.f++; + + *result = rounded.ToDouble(); + + return halfWay - error >= precisionBits || precisionBits >= halfWay + error; +} + +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { + const BigInteger dInt(decimals, length); + const int dExp = (int)decimalPosition - (int)length + exp; + Double a(approx); + for (int i = 0; i < 10; i++) { + bool adjustToNegative; + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp, &adjustToNegative); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + a = adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); + } + + // This should not happen, but in case there is really a bug, break the infinite-loop + return a.Value(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result; + if (StrtodFast(d, p, &result)) + return result; + + // Trim leading zeros + while (*decimals == '0' && length > 1) { + length--; + decimals++; + decimalPosition--; + } + + // Trim trailing zeros + while (decimals[length - 1] == '0' && length > 1) { + length--; + decimalPosition--; + exp++; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 780; + if ((int)length > kMaxDecimalDigit) { + exp += (int(length) - kMaxDecimalDigit); + length = kMaxDecimalDigit; + } + + // If too small, underflow to zero + if (int(length) + exp < -324) + return 0.0; + + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/include/rapidjson/memorybuffer.h b/include/rapidjson/memorybuffer.h new file mode 100644 index 0000000..95c68a3 --- /dev/null +++ b/include/rapidjson/memorybuffer.h @@ -0,0 +1,76 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "rapidjson.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/memorystream.h b/include/rapidjson/memorystream.h new file mode 100644 index 0000000..f994a12 --- /dev/null +++ b/include/rapidjson/memorystream.h @@ -0,0 +1,67 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return (src_ == end_) ? '\0' : *src_; } + Ch Take() { return (src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/msinttypes/inttypes.h b/include/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000..af713c9 --- /dev/null +++ b/include/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,312 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/include/rapidjson/msinttypes/stdint.h b/include/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000..bbad95a --- /dev/null +++ b/include/rapidjson/msinttypes/stdint.h @@ -0,0 +1,296 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h new file mode 100644 index 0000000..ce2dac5 --- /dev/null +++ b/include/rapidjson/prettywriter.h @@ -0,0 +1,205 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of ouptut os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kStringType); + return Base::WriteString(str, length); + } + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + if (!Base::WriteEndObject()) + return false; + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + if (!Base::WriteEndArray()) + return false; + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + Base::os_->Put('\n'); + } + else + Base::os_->Put('\n'); + WriteIndent(); + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, indentChar_, count); + } + + Ch indentChar_; + unsigned indentCharCount_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h new file mode 100644 index 0000000..e9bfdba --- /dev/null +++ b/include/rapidjson/rapidjson.h @@ -0,0 +1,628 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +// Copyright (c) 2011 Milo Yip (miloyip@gmail.com) +// Version 0.1 + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overriden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#ifdef _MSC_VER +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#ifdef _MSC_VER +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || defined(_WIN64) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. Currently the default uses 4 bytes + alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2 optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. + + To enable these optimizations, two different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + \c RAPIDJSON_SSE42 takes precedence, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef RAPIDJSON_STATIC_ASSERT +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#if defined(__GNUC__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +//!@endcond + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(x) new x +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Allocators and Encodings + +#include "allocators.h" +#include "encodings.h" + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + for (size_t i = 0; i < n; i++) + stream.Put(c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +/////////////////////////////////////////////////////////////////////////////// +// Type + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h new file mode 100644 index 0000000..05c081d --- /dev/null +++ b/include/rapidjson/reader.h @@ -0,0 +1,1446 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "rapidjson.h" +#include "encodings.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (HasParseError()) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128((const __m128i *)&whitespace[0]); + + for (;; p += 16) { + const __m128i s = _mm_load_si128((const __m128i *)p); + const unsigned r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + static const char whitespaces[4][17] = { + " ", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"}; + + const __m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]); + const __m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]); + const __m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]); + const __m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]); + + for (;; p += 16) { + const __m128i s = _mm_load_si128((const __m128i *)p); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = (unsigned short)~_mm_movemask_epi8(x); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +#endif // RAPIDJSON_SSE2 + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespace(is); + + if (is.Peek() == '\0') { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespace(is); + + if (is.Peek() != '\0') { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (!handler.StartObject()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); + + if (is.Peek() == '}') { + is.Take(); + if (!handler.EndObject(0)) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (is.Peek() != '"') + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespace(is); + + if (is.Take() != ':') + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespace(is); + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespace(is); + + ++memberCount; + + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case '}': + if (!handler.EndObject(memberCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + else + return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (!handler.StartArray()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); + + if (is.Peek() == ']') { + is.Take(); + if (!handler.EndArray(0)) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespace(is); + + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case ']': + if (!handler.EndArray(elementCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + else + return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { + if (!handler.Null()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { + if (!handler.Bool(true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { + if (!handler.Bool(false)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Take(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + size_t Length() const { return length_; } + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (!success) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + RAPIDJSON_ASSERT(is.Peek() == '\"'); + is.Take(); // Skip '\"' + + for (;;) { + Ch c = is.Peek(); + if (c == '\\') { // Escape + is.Take(); + Ch e = is.Take(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { + os.Put(escape[(unsigned char)e]); + } + else if (e == 'u') { // Unicode + unsigned codepoint = ParseHex4(is); + if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { + // Handle UTF-16 surrogate pair + if (is.Take() != '\\' || is.Take() != 'u') + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + unsigned codepoint2 = ParseHex4(is); + if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + } + else if (c == '"') { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); + else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + else { + if (parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + } + } + + template + class NumberStream {}; + + template + class NumberStream { + public: + NumberStream(GenericReader& reader, InputStream& is) : is(is) { (void)reader; } + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : NumberStream(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put((char)Base::is.Peek()); + return Base::is.Take(); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + // Parse minus + bool minus = false; + if (s.Peek() == '-') { + minus = true; + s.Take(); + } + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (s.Peek() == '0') { + i = 0; + s.TakePush(); + } + else if (s.Peek() >= '1' && s.Peek() <= '9') { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 214748364) { // 2^31 = 2147483648 + if (i != 214748364 || s.Peek() > '8') { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 429496729) { // 2^32 - 1 = 4294967295 + if (i != 429496729 || s.Peek() > '5') { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + double d = 0.0; + if (use64bit) { + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 + if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { + d = i64; + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 + if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { + d = i64; + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (s.Peek() == '.') { + s.Take(); + decimalPosition = s.Length(); + + if (!(s.Peek() >= '0' && s.Peek() <= '9')) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = (double)i64; +#else + // Use double to store significand in 32-bit architecture + d = use64bit ? (double)i64 : (double)i; +#endif + useDouble = true; + } + + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (d != 0.0) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (s.Peek() == 'e' || s.Peek() == 'E') { + if (!useDouble) { + d = use64bit ? i64 : i; + useDouble = true; + } + s.Take(); + + bool expMinus = false; + if (s.Peek() == '+') + s.Take(); + else if (s.Peek() == '-') { + s.Take(); + expMinus = true; + } + + if (s.Peek() >= '0' && s.Peek() <= '9') { + exp = s.Take() - '0'; + while (s.Peek() >= '0' && s.Peek() <= '9') { + exp = exp * 10 + (s.Take() - '0'); + if (exp > 308 && !expMinus) // exp > 308 should be rare, so it should be checked first. + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(-(int64_t)i64); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(-(int)i); + else + cont = handler.Uint(i); + } + } + if (!cont) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : ParseNumber(is, handler); + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, + IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingElementDelimiterState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || unsigned(c) < 256) + return (Token)tokenMap[(unsigned char)c]; + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + } + }; // End of G + + return (IterativeParsingState)G[state][token]; + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + switch (dst) { + case IterativeParsingStartState: + RAPIDJSON_ASSERT(false); + return IterativeParsingErrorState; + + case IterativeParsingFinishState: + return dst; + + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + if (token == ColonToken) { + is.Take(); + return dst; + } + else + return IterativeParsingErrorState; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + + default: + RAPIDJSON_ASSERT(false); + return IterativeParsingErrorState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + } + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespace(is); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespace(is); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/include/rapidjson/stringbuffer.h b/include/rapidjson/stringbuffer.h new file mode 100644 index 0000000..009a518 --- /dev/null +++ b/include/rapidjson/stringbuffer.h @@ -0,0 +1,99 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "rapidjson.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h new file mode 100644 index 0000000..6daa783 --- /dev/null +++ b/include/rapidjson/writer.h @@ -0,0 +1,391 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "rapidjson.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return WriteNull(); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } + bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } + bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } + bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kStringType); + return WriteString(str, length); + } + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + bool ret = WriteEndObject(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + bool ret = WriteEndArray(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + static const size_t kDefaultLevelDepth = 32; + + bool WriteNull() { + os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); + } + else { + os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + for (char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteDouble(double d) { + char buffer[25]; + char* end = internal::dtoa(d, buffer); + for (char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + os_->Put('\"'); + GenericStringStream is(str); + while (is.Tell() < length) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + os_->Put('\\'); + os_->Put('u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + os_->Put(hexDigits[(codepoint >> 12) & 15]); + os_->Put(hexDigits[(codepoint >> 8) & 15]); + os_->Put(hexDigits[(codepoint >> 4) & 15]); + os_->Put(hexDigits[(codepoint ) & 15]); + } + else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) { + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + os_->Put(hexDigits[(lead >> 12) & 15]); + os_->Put(hexDigits[(lead >> 8) & 15]); + os_->Put(hexDigits[(lead >> 4) & 15]); + os_->Put(hexDigits[(lead ) & 15]); + os_->Put('\\'); + os_->Put('u'); + os_->Put(hexDigits[(trail >> 12) & 15]); + os_->Put(hexDigits[(trail >> 8) & 15]); + os_->Put(hexDigits[(trail >> 4) & 15]); + os_->Put(hexDigits[(trail ) & 15]); + } + else + return false; // invalid code point + } + else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { + is.Take(); + os_->Put('\\'); + os_->Put(escape[(unsigned char)c]); + if (escape[(unsigned char)c] == 'u') { + os_->Put('0'); + os_->Put('0'); + os_->Put(hexDigits[(unsigned char)c >> 4]); + os_->Put(hexDigits[(unsigned char)c & 0xF]); + } + } + else + Transcoder::Transcode(is, *os_); + } + os_->Put('\"'); + return true; + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + void Prefix(Type type) { + (void)type; + if (level_stack_.GetSize() != 0) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + OutputStream* os_; + internal::Stack level_stack_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(11 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(10 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(21 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(20 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer); + os_->Pop(25 - (end - buffer)); + return true; +} + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index c4b6865..9dce916 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -56,6 +56,9 @@ void TcpClient::ChatSession(uint32_t groupId) ChatMessage chat; while (true) { + while (!m_tcpHandler.Available()) + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + chat.Receive(m_tcpHandler); std::cout << "\n\n" << chat.GetMessage() << "\n\nYou: "; } diff --git a/src/client/main.cpp b/src/client/main.cpp index 57ebb20..a87c85b 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -33,7 +33,10 @@ int main(int argc, char *argv[]) std::cin >> groupId; client.JoinGroup(groupId); client.StartChatSession(groupId); - while (true); + + while (true) + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + ; } } catch (std::exception &ex) diff --git a/src/common/RequestHandler.cpp b/src/common/RequestHandler.cpp index 64f405b..bdb1b82 100644 --- a/src/common/RequestHandler.cpp +++ b/src/common/RequestHandler.cpp @@ -1,5 +1,14 @@ #include #include +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +using namespace rapidjson; + +// Requests are of fixed size (defined by REQUEST_MAX_SIZE) +// This makes it simpler to receive just the required request data +// from the socket and neither more nor less +const size_t REQUEST_MAX_SIZE = 150; RequestHandler::RequestHandler() {} @@ -7,21 +16,77 @@ RequestHandler::RequestHandler() RequestHandler::~RequestHandler() {} +std::string RequestHandler::GetJsonString() +{ + StringBuffer buffer; + Writer writer(buffer); + m_document.Accept(writer); + return buffer.GetString(); +} + void RequestHandler::JoinGroup(TcpHandler &tcpHandler, uint32_t groupId) { - m_request.type = JOIN_GROUP; - m_request.info.join.groupId = groupId; - tcpHandler.Send((char*)&m_request, sizeof(m_request)); + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 1 + Group-Id: 20 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)JOIN_GROUP), m_document.GetAllocator()); + m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy_s(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); } void RequestHandler::GroupChat(TcpHandler &tcpHandler, uint32_t groupId) { - m_request.type = GROUP_CHAT; - m_request.info.groupChat.groupId = groupId; - tcpHandler.Send((char*)&m_request, sizeof(m_request));; + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 2 + Group-Id: 20 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)GROUP_CHAT), m_document.GetAllocator()); + m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy_s(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); } void RequestHandler::ReceiveRequest(TcpHandler &tcpHandler) { - tcpHandler.Receive((char*)&m_request, sizeof(m_request)); + // Get the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + tcpHandler.Receive(request, REQUEST_MAX_SIZE); + + // Parse the request + m_document = Document(); + m_document.Parse(request); +} + +RequestHandler::REQUEST_TYPE RequestHandler::GetRequestType() +{ + // Return INVALID_TYPE when Request-Type value is absent or isn't integer + if (!m_document.HasMember("Request-Type") || !m_document["Request-Type"].IsInt()) + return INAVLID_TYPE; + return (REQUEST_TYPE)m_document["Request-Type"].GetInt(); +} + +uint32_t RequestHandler::GetGroupId() +{ + // Throw exception for absence of or invalid group-id + if (!m_document.HasMember("Group-Id") || !m_document["Group-Id"].IsInt()) + throw RequestException("Invalid group-id received"); + return m_document["Group-Id"].GetUint(); } \ No newline at end of file diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index 8678a1a..0b4d2a8 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -37,10 +37,16 @@ void TcpHandler::Send(const char* data, size_t size) } // -void TcpHandler::Receive(char* data, size_t max_size) +void TcpHandler::Receive(char* data, size_t size) { if (!m_socket) return; - m_socket->read_some(boost::asio::buffer(data, max_size));; + boost::asio::read(*m_socket, boost::asio::buffer(data, size)); +} + +void TcpHandler::ReceiveSome(char* data, size_t max_size) +{ + if (!m_socket) return; + m_socket->read_some(boost::asio::buffer(data, max_size)); } size_t TcpHandler::Available() diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index f5ebaab..8fdc916 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -53,17 +53,17 @@ void ClientsManager::ProcessClients() if (bytes > 0) { m_requests.ReceiveRequest(m_clients[i].connection); - switch (m_requests.GetLastRequest().type) + switch (m_requests.GetRequestType()) { case RequestHandler::JOIN_GROUP: - gid = m_requests.GetLastRequest().info.join.groupId; + gid = m_requests.GetGroupId(); // push the client id to the group m_groups[gid].push_back(i); std::cout << "Connected client #" << i << " to group #" << gid << std::endl; break; case RequestHandler::GROUP_CHAT: - gid = m_requests.GetLastRequest().info.groupChat.groupId; + gid = m_requests.GetGroupId(); // receive the chat message ReceiveChat(i, gid); break; From 536914af5e4d743f3f894479a69681dfe0b21dfd Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Tue, 16 Dec 2014 20:59:56 +0545 Subject: [PATCH 34/54] Bug found and fixed --- src/common/ChatMessage.cpp | 20 +++++++------------- src/common/RequestHandler.cpp | 3 ++- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/common/ChatMessage.cpp b/src/common/ChatMessage.cpp index a43572b..9047d6c 100644 --- a/src/common/ChatMessage.cpp +++ b/src/common/ChatMessage.cpp @@ -7,25 +7,19 @@ ChatMessage::ChatMessage() ChatMessage::~ChatMessage() {} -struct ChatHeader -{ - size_t body_size; -}; - void ChatMessage::Send(TcpHandler &tcpHandler) { - ChatHeader header; - header.body_size = m_body.size() + 1; // add 1 to size to ensure null terminator - tcpHandler.Send((char*)&header, sizeof(ChatHeader)); - tcpHandler.Send(m_body.c_str(), header.body_size); + uint32_t body_size = m_body.size() + 1; + tcpHandler.Send((char*)&body_size, sizeof(body_size)); + tcpHandler.Send(m_body.c_str(), body_size); } void ChatMessage::Receive(TcpHandler &tcpHandler) { - ChatHeader header; - tcpHandler.Receive((char*)&header, sizeof(ChatHeader)); - char * data = new char[header.body_size]; - tcpHandler.Receive(data, header.body_size); + uint32_t body_size; + tcpHandler.Receive((char*)&body_size, sizeof(body_size)); + char * data = new char[body_size]; + tcpHandler.Receive(data, body_size); m_body = std::string(data); delete[] data; } \ No newline at end of file diff --git a/src/common/RequestHandler.cpp b/src/common/RequestHandler.cpp index bdb1b82..f508245 100644 --- a/src/common/RequestHandler.cpp +++ b/src/common/RequestHandler.cpp @@ -69,6 +69,7 @@ void RequestHandler::ReceiveRequest(TcpHandler &tcpHandler) // Get the "fixed-size" request char request[REQUEST_MAX_SIZE]; tcpHandler.Receive(request, REQUEST_MAX_SIZE); + std::cout << request << std::endl; // Parse the request m_document = Document(); @@ -78,7 +79,7 @@ void RequestHandler::ReceiveRequest(TcpHandler &tcpHandler) RequestHandler::REQUEST_TYPE RequestHandler::GetRequestType() { // Return INVALID_TYPE when Request-Type value is absent or isn't integer - if (!m_document.HasMember("Request-Type") || !m_document["Request-Type"].IsInt()) + if (!m_document.IsObject() || !m_document.HasMember("Request-Type") || !m_document["Request-Type"].IsInt()) return INAVLID_TYPE; return (REQUEST_TYPE)m_document["Request-Type"].GetInt(); } From 77300aa519fe9ee473d1a2a5672c2ac5e541a8fd Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Tue, 16 Dec 2014 21:03:57 +0545 Subject: [PATCH 35/54] for sync --- src/server/ClientsManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index f5ebaab..e17e7b5 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -69,7 +69,7 @@ void ClientsManager::ProcessClients() break; default: - std::cout << "Invalid Request Got from client #" << i << std::endl; + std::cout << "Invalid Request " << m_requests.GetLastRequest().type << " from client #" << i << std::endl; } } } From 82eb9541ff8b747a7943ed3b1dd543b399b12a1b Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Tue, 16 Dec 2014 21:05:30 +0545 Subject: [PATCH 36/54] update makefile --- makefile_client | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile_client b/makefile_client index 00314c0..cc0c2d9 100644 --- a/makefile_client +++ b/makefile_client @@ -21,7 +21,7 @@ all: client.o common common: $(OBJECTS_COMM) $(CC) -c -o common.o $< -%.o : src/common/%.cpp $(FHEADERS_COMM) +%.o : src/common/%.cpp $(CC) -c -o $@ $< $(LDFLAGS_COMM) -Iinclude/ $(CFLAG) $(CFLAGS) client.o: src/client/TcpClient.cpp From 139a8f7f80b2ea9805e48259af8bdf79d7687136 Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Tue, 16 Dec 2014 21:09:08 +0545 Subject: [PATCH 37/54] for sync --- src/server/ClientsManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index c5f5d8d..30928dd 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -69,7 +69,7 @@ void ClientsManager::ProcessClients() break; default: - std::cout << "Invalid Request " << m_requests.GetLastRequest().type << " from client #" << i << std::endl; + std::cout << "Invalid Request " << m_requests.GetRequestType() << " from client #" << i << std::endl; } } } From b62359678d847f4695441c29d5fdc4fb75c64367 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Tue, 16 Dec 2014 21:10:30 +0545 Subject: [PATCH 38/54] renamed strcpy_s to strcpy --- src/common/RequestHandler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/RequestHandler.cpp b/src/common/RequestHandler.cpp index f508245..f5a53c4 100644 --- a/src/common/RequestHandler.cpp +++ b/src/common/RequestHandler.cpp @@ -40,7 +40,7 @@ void RequestHandler::JoinGroup(TcpHandler &tcpHandler, uint32_t groupId) // Send the "fixed-size" request char request[REQUEST_MAX_SIZE]; - strcpy_s(request, GetJsonString().c_str()); + strcpy(request, GetJsonString().c_str()); tcpHandler.Send(request, REQUEST_MAX_SIZE); } @@ -60,7 +60,7 @@ void RequestHandler::GroupChat(TcpHandler &tcpHandler, uint32_t groupId) // Send the "fixed-size" request char request[REQUEST_MAX_SIZE]; - strcpy_s(request, GetJsonString().c_str()); + strcpy(request, GetJsonString().c_str()); tcpHandler.Send(request, REQUEST_MAX_SIZE); } @@ -90,4 +90,4 @@ uint32_t RequestHandler::GetGroupId() if (!m_document.HasMember("Group-Id") || !m_document["Group-Id"].IsInt()) throw RequestException("Invalid group-id received"); return m_document["Group-Id"].GetUint(); -} \ No newline at end of file +} From 655037823ab84e775d758ac1d10f8bff9471d08a Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Tue, 16 Dec 2014 21:28:43 +0545 Subject: [PATCH 39/54] Add name input --- include/client/TcpClient.h | 3 ++- src/client/TcpClient.cpp | 5 +++-- src/client/main.cpp | 9 +++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 152e077..8db9f76 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -16,7 +16,7 @@ class TcpClient // Test Methods // Join a group - void JoinGroup(uint32_t groupId); + void JoinGroup(uint32_t groupId, const std::string &name); // Start a group chat void StartChatSession(uint32_t groupId); @@ -25,6 +25,7 @@ class TcpClient TcpListener m_listener; TcpHandler m_tcpHandler; RequestHandler m_requests; + std::string m_name; void ListenerHandler(boost::shared_ptr socket); diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index 9dce916..6eb550e 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -33,9 +33,10 @@ void TcpClient::Connect(const tcp::endpoint& peer) } // Join group by sending request to server -void TcpClient::JoinGroup(uint32_t groupId) +void TcpClient::JoinGroup(uint32_t groupId, const std::string &name) { m_requests.JoinGroup(m_tcpHandler, groupId); + m_name = name; } // Start group chat in a new thread @@ -79,7 +80,7 @@ void TcpClient::ChatInput(uint32_t groupId) char input[1024]; std::cin.getline(input, 1024); - std::string message = std::to_string(m_tcpHandler.GetSocket()->local_endpoint().port()) + ": " + input; + std::string message = m_name + ": " + input; // Send the chat message after a GROUP_CHAT request m_requests.GroupChat(m_tcpHandler, groupId); diff --git a/src/client/main.cpp b/src/client/main.cpp index a87c85b..04f6f1a 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -25,13 +25,18 @@ int main(int argc, char *argv[]) // Connect to server { - std::string ip; + std::string ip, name; std::cout << "Enter ip of server: "; std::cin >> ip; client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 10011/*8183*/)); // 10011 for Ankit's Server uint32_t groupId; std::cout << "Enter group-id to join: "; std::cin >> groupId; - client.JoinGroup(groupId); + std::cout << "Enter name: "; + std::fflush(stdin); + char namec[200]; + std::cin.getline(namec, 200); + name = std::string(namec); + client.JoinGroup(groupId, name); client.StartChatSession(groupId); while (true) From 904708fc82cf62f30007028538173559feea91d5 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Sat, 20 Dec 2014 11:40:57 +0545 Subject: [PATCH 40/54] P2P connection feature added --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 12 +- .../Mirror-Client.vcxproj.filters | 16 +- MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 12 +- .../Mirror-Server.vcxproj.filters | 40 ++--- include/client/TcpClient.h | 61 +++++-- include/common/ChatMessage.h | 2 +- include/common/RequestHandler.h | 35 ---- .../common/{TcpListener.h => TcpAcceptor.h} | 18 +- include/common/TcpHandler.h | 5 +- include/common/TcpRequest.h | 51 ++++++ include/server/ClientsManager.h | 8 +- src/client/TcpClient.cpp | 143 +++++++++++---- src/client/main.cpp | 40 +++-- src/common/ChatMessage.cpp | 12 +- src/common/RequestHandler.cpp | 93 ---------- .../{TcpListener.cpp => TcpAcceptor.cpp} | 16 +- src/common/TcpRequest.cpp | 165 ++++++++++++++++++ src/server/ClientsManager.cpp | 50 ++++-- 18 files changed, 490 insertions(+), 289 deletions(-) delete mode 100644 include/common/RequestHandler.h rename include/common/{TcpListener.h => TcpAcceptor.h} (57%) create mode 100644 include/common/TcpRequest.h delete mode 100644 src/common/RequestHandler.cpp rename src/common/{TcpListener.cpp => TcpAcceptor.cpp} (72%) create mode 100644 src/common/TcpRequest.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index eb1c755..05a5a23 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -48,7 +48,7 @@ Disabled true $(OPENCV_ROOT)\include;$(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo - _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) + _WIN32_WINNT=0x0501;_MBCS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) true @@ -63,7 +63,7 @@ true true $(OPENCV_ROOT)\build\include;$(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo - _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) + _WIN32_WINNT=0x0501;_MBCS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) true @@ -77,19 +77,19 @@ - + - + - + - + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 91f3bca..0aa54bb 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -30,9 +30,6 @@ include\common\Network - - include\common\Network - include @@ -42,7 +39,10 @@ include\common\Network - + + include\common\Network + + include\common\Network @@ -50,9 +50,6 @@ src - - src\common\Network - src @@ -65,7 +62,10 @@ src\common\Network - + + src\common\Network + + src\common\Network diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index 7554e45..7deb028 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -45,7 +45,7 @@ Disabled true $(OPENCV_ROOT)\include;$(GTKDIR)\include\gail-3.0;$(GTKDIR)\include\atk-1.0;$(GTKDIR)\include\gdk-pixbuf-2.0;$(SolutionDir)\..\include;$(BOOST_ROOT);$(GTKDIR)\include\gtk-3.0;$(GTKDIR)\include\glib-2.0;$(GTKDIR)\include\pango-1.0;$(GTKDIR)\lib\glib-2.0\include;$(GTKDIR)\include\cairo - _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) + _WIN32_WINNT=0x0501;_MBCS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) true @@ -60,7 +60,7 @@ true true $(SolutionDir)\..\include;$(BOOST_ROOT); - _WIN32_WINNT=0x0501;_MBCS;%(PreprocessorDefinitions) + _WIN32_WINNT=0x0501;_MBCS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) true @@ -71,10 +71,10 @@ - + - + @@ -82,10 +82,10 @@ - + - + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index 66be250..73f6865 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -8,64 +8,64 @@ {34d0d713-51c2-4570-b77a-9ac3a2176ef1} - {056a9fd5-2ff8-44a0-8d6a-2ec35c6def8f} + {bbe44c9a-c61f-4631-ae70-3fef070c8d54} - {c610381c-f408-42d5-b16b-e72a27a09f45} + {2ffe0197-9872-4f66-bdb0-39679890bd27} - {cbb7e2c4-afe4-4030-8839-a5c49b4b694d} + {455b913e-44bb-450f-a4f9-b338c96aeafe} - {61477837-06db-47c8-8f06-925c674e49f1} + {f47d1eec-76a8-4eff-b258-08e70341305e} src - - src\common\Network + + src - + src\common\Network - + src\common\Network - + src\common\Network - - src + + src\common\Network - + src\common\Network + + include + include\common include\common - + include\common\Network - + include\common\Network - + include\common\Network - + include\common\Network - - include - - + include\common\Network diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 8db9f76..b697b57 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -1,7 +1,8 @@ #pragma once -#include +#include #include -#include +#include + class TcpClient { @@ -10,27 +11,51 @@ class TcpClient ~TcpClient(); // Start listening for incoming connections in a new thread - void StartListening(/*const tcp::endpoint& localEndPoint*/); - // Connect to a peer (server/client) - void Connect(const tcp::endpoint& peer); - - // Test Methods - // Join a group - void JoinGroup(uint32_t groupId, const std::string &name); - // Start a group chat - void StartChatSession(uint32_t groupId); + void StartAccepting(); + // Connect to a peer/server + void Connect(const tcp::endpoint& peer, bool joinChat = false); + // Connect to a peer/server asynchronously + void ConnectAsync(const tcp::endpoint& peer, bool joinChat = false); + // Handle all requests that the client gets + void HandleRequests(); + // Connect to a peer through server + void Connect(uint32_t clientId); + + // Join chat + void JoinChat(uint32_t connectionId, uint32_t groupId); + // Set username to use while sending messages + void SetName(const std::string &name) { m_name = name; } + + // For console: + // Start Chat Input in a separate thread + void StartChatInput(uint32_t groupId); private: + // Connection representing a tcp-connection with a peer/server + struct Connection + { + Connection(boost::asio::io_service &io) + : tcpHandler(io) + {} + TcpHandler tcpHandler; + // maybe store userid and other stuffs here... + }; + boost::asio::io_service &m_io; - TcpListener m_listener; - TcpHandler m_tcpHandler; - RequestHandler m_requests; + TcpAcceptor m_acceptor; + + std::vector m_connections; + // Mutex to lock m_connections vector + boost::mutex m_connectionsMutex; + + TcpRequest m_request; std::string m_name; - void ListenerHandler(boost::shared_ptr socket); + void AcceptorHandler(boost::shared_ptr socket); + - // Group chat session to receive chat messages - void ChatSession(uint32_t groupId); - // For console, we use separate thread for inputting chat messages + // For console: + int m_currentConnection; + // Separate thread for inputting chat messages void ChatInput(uint32_t groupId); }; \ No newline at end of file diff --git a/include/common/ChatMessage.h b/include/common/ChatMessage.h index 4930c72..39bec04 100644 --- a/include/common/ChatMessage.h +++ b/include/common/ChatMessage.h @@ -12,7 +12,7 @@ class ChatMessage void SetMessage(const std::string& message) { m_body = message; } void Send(TcpHandler &tcpHandler); - void Receive(TcpHandler &tcpHandler); + void Receive(TcpHandler &tcpHandler, uint32_t size); private:; std::string m_body; diff --git a/include/common/RequestHandler.h b/include/common/RequestHandler.h deleted file mode 100644 index 7309069..0000000 --- a/include/common/RequestHandler.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "TcpHandler.h" -#include "rapidjson/document.h" - -class RequestException : public Exception -{ -public: - RequestException(const std::string &errorString) - :Exception("Error processing request: "+errorString) - {} -}; -class RequestHandler -{ -public: - enum REQUEST_TYPE{ INAVLID_TYPE = 0, JOIN_GROUP, GROUP_CHAT }; - - RequestHandler(); - ~RequestHandler(); - - // Request the server to join a group - void JoinGroup(TcpHandler &tcpHandler, uint32_t groupId); - // Request the server to wait for incoming chat message for a group - void GroupChat(TcpHandler &tcpHandler, uint32_t groupId); - // Receive a request - void ReceiveRequest(TcpHandler &tcpHandler); - - // Request Data - REQUEST_TYPE GetRequestType(); - uint32_t GetGroupId(); -private: - // The Json Document that holds the request - rapidjson::Document m_document; - // Get string from the Json Doucment - std::string GetJsonString(); -}; diff --git a/include/common/TcpListener.h b/include/common/TcpAcceptor.h similarity index 57% rename from include/common/TcpListener.h rename to include/common/TcpAcceptor.h index 85f0025..981f114 100644 --- a/include/common/TcpListener.h +++ b/include/common/TcpAcceptor.h @@ -1,25 +1,19 @@ #pragma once -class TcpListenerException : public Exception +class TcpAcceptor { public: - TcpListenerException(const std::string &errorString) - : Exception("TCP Listener Error: " + errorString) - {} -}; + TcpAcceptor(boost::asio::io_service &io); + ~TcpAcceptor(); -class TcpListener -{ -public: - TcpListener(boost::asio::io_service &io); - ~TcpListener(); - - // Initialize the listener to listen at given local endpoint + // Initialize the acceptor to listen at given local endpoint uint16_t Initialize(const tcp::endpoint &localEndpoint); // Listen for incomming connections in a new thread // and call 'callback' for each connection void Listen(boost::function)> callback); + tcp::acceptor &GetSocket() { return m_acceptor; } + private: tcp::acceptor m_acceptor; boost::function)> m_callback; diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index 2a8b9b1..a4d7860 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -1,8 +1,5 @@ #pragma once -//don't use pragma, use standard header guard technique -class Exception; - class TcpHandlerException : public Exception { public: @@ -33,6 +30,8 @@ class TcpHandler size_t Available(); std::string GetDestinationAddress() const; + std::string GetIp() const { return m_socket->remote_endpoint().address().to_string(); } + uint16_t GetPort() const { return m_socket->remote_endpoint().port(); } boost::shared_ptr GetSocket() const { return m_socket; } private: diff --git a/include/common/TcpRequest.h b/include/common/TcpRequest.h new file mode 100644 index 0000000..f323fba --- /dev/null +++ b/include/common/TcpRequest.h @@ -0,0 +1,51 @@ +#pragma once +#include "TcpHandler.h" +#include "rapidjson/document.h" + +class RequestException : public Exception +{ +public: + RequestException(const std::string &errorString) + :Exception("Error processing request: "+errorString) + {} +}; +class TcpRequest +{ +public: + enum REQUEST_TYPE{ + INAVLID_TYPE = 0, + JOIN_CHAT, CHAT_MESSAGE, + SEND_CLIENT_ADDR, RECEIVE_CLIENT_ADDR, + }; + + TcpRequest(); + ~TcpRequest(); + + // Request to join a chat + void JoinChat(TcpHandler &tcpHandler, uint32_t groupId = 0); + // Request to wait for incoming chat message + void ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId = 0); + // Request to send a client address (ip and port) + // For server, provide a valid clientId + void SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId); + // Request to receive a client address (ip and port) as response for a SEND_CLIENT_ADDR request + // For server, provide a valid clientId + void ReceiveClientAddr(TcpHandler &tcpHandler, uint32_t clientId, const std::string &ip, uint16_t port); + // Receive a request + void ReceiveRequest(TcpHandler &tcpHandler); + + // Request Data + REQUEST_TYPE GetRequestType(); + uint32_t GetGroupId(); + uint32_t GetMessageSize(); + uint32_t GetClientId(); + std::string GetIp(); + uint16_t GetPort(); +private: + // The Json Document that holds the request + rapidjson::Document m_document; + // Get string from the Json Doucment + std::string GetJsonString(); + // Helper function get a json value from a request + inline rapidjson::Value& GetValue(const std::string &key); +}; diff --git a/include/server/ClientsManager.h b/include/server/ClientsManager.h index b5f9f8f..4eaabf5 100644 --- a/include/server/ClientsManager.h +++ b/include/server/ClientsManager.h @@ -1,7 +1,7 @@ #pragma once -#include +#include #include -#include +#include struct ClientInfo { @@ -24,8 +24,8 @@ class ClientsManager private: boost::asio::io_service &m_ioService; - TcpListener m_listener; - RequestHandler m_requests; + TcpAcceptor m_acceptor; + TcpRequest m_requests; boost::mutex m_mutex; // Clients List diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index 6eb550e..2e7eaaa 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -3,77 +3,144 @@ #include TcpClient::TcpClient(boost::asio::io_service &io) -:m_io(io), m_listener(io), m_tcpHandler(io) +:m_io(io), m_acceptor(io), m_currentConnection(-1) {} TcpClient::~TcpClient() {} -void TcpClient::StartListening(/*const tcp::endpoint& localEndPoint*/) +void TcpClient::StartAccepting() { - uint16_t port = m_listener.Initialize(tcp::endpoint(tcp::v4(), 0)); - std::cout << "Listening at port: " << port << std::endl; + uint16_t port = m_acceptor.Initialize(tcp::endpoint(tcp::v4(), 0)); + std::cout << "Listening at port: " << port << std::endl; // Listen method will start listening in new thread - m_listener.Listen(boost::bind(&TcpClient::ListenerHandler, this, _1)); + m_acceptor.Listen(boost::bind(&TcpClient::AcceptorHandler, this, _1)); } -// This is the handler called when the listener accepts a new connection -void TcpClient::ListenerHandler(boost::shared_ptr socket) +// This is the handler called when the acceptor accepts a new connection +void TcpClient::AcceptorHandler(boost::shared_ptr socket) { - m_tcpHandler.Initialize(socket); - std::cout << "Connected: " << m_tcpHandler.GetDestinationAddress() << std::endl; + // Lock the connections list not to process it while adding a connection + boost::lock_guard guard(m_connectionsMutex); + + // Add new connection + m_connections.push_back(Connection(m_io)); + TcpHandler &handler = m_connections[m_connections.size() - 1].tcpHandler; + handler.Initialize(socket); + + std::cout << "Connected to: " << handler.GetDestinationAddress() << std::endl; } -// Connect to some remote peer (server/client) -void TcpClient::Connect(const tcp::endpoint& peer) +// Connecting to some remote peer (server/client) +void TcpClient::Connect(const tcp::endpoint& peer, bool joinChat) { - m_tcpHandler.Initialize(peer); - std::cout << "Connected: " << m_tcpHandler.GetDestinationAddress() << std::endl; + // Lock the connections list not to process it while adding a connection + boost::lock_guard guard(m_connectionsMutex); + + // Add new connection + m_connections.push_back(Connection(m_io)); + TcpHandler &handler = m_connections[m_connections.size() - 1].tcpHandler; + handler.Initialize(peer); + + std::cout << "Connected to: " << handler.GetDestinationAddress() << std::endl; + // Join chat in the new connection + if (joinChat) + JoinChat(m_connections.size() - 1, 0); } -// Join group by sending request to server -void TcpClient::JoinGroup(uint32_t groupId, const std::string &name) +// Perform above function asynchronously +void TcpClient::ConnectAsync(const tcp::endpoint& peer, bool joinChat) { - m_requests.JoinGroup(m_tcpHandler, groupId); - m_name = name; + boost::thread t(boost::bind((void(TcpClient::*)(const tcp::endpoint&, bool))&TcpClient::Connect, this, _1, _2), peer, joinChat); } -// Start group chat in a new thread -void TcpClient::StartChatSession(uint32_t groupId) +// Use server to connect to a client (for P2P) +void TcpClient::Connect(uint32_t clientId) { - boost::thread t(boost::bind(&TcpClient::ChatSession, this, _1), groupId); + // Send a request to get a client-address; assuming server id first element in the connections-list + m_request.SendClientAddr(m_connections[0].tcpHandler, clientId); + + // Receive for the response and use it to connect to the client + m_request.ReceiveRequest(m_connections[0].tcpHandler); + if (m_request.GetRequestType() != TcpRequest::RECEIVE_CLIENT_ADDR) + throw Exception("Expecting a client address but getting some other request"); + Connect(tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetIp()), m_request.GetPort()), true); } -void TcpClient::ChatSession(uint32_t groupId) +// An infinite loop to handle incoming requests +void TcpClient::HandleRequests() { - try + while (true) { - std::cout << "\n\n"; - // A new thread to input chat messages - boost::thread t(boost::bind(&TcpClient::ChatInput, this, _1), groupId); + // Sleep for some time if there is not connection + while (m_connections.size() == 0) + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - // In this thread, keep receiving messages and print them - ChatMessage chat; - while (true) + // While processing connections, lock the list so no new connection is added at the time + m_connectionsMutex.lock(); + for (unsigned int i = 0; i < m_connections.size(); ++i) { - while (!m_tcpHandler.Available()) - boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + try + { + // See if any request is incomming for this connection + size_t bytes = m_connections[i].tcpHandler.Available(); + ChatMessage chat; + // if so, process accordingly + if (bytes > 0) + { + m_request.ReceiveRequest(m_connections[i].tcpHandler); + switch (m_request.GetRequestType()) + { + // Request to join chat conversation + case TcpRequest::JOIN_CHAT: + m_currentConnection = i; + break; + // Request to wait for incoming chat message + case TcpRequest::CHAT_MESSAGE: + chat.Receive(m_connections[i].tcpHandler, m_request.GetMessageSize()); + m_currentConnection = i; + std::cout << "\n\n" << chat.GetMessage() << "\n\nYou: "; + break; + // Request to send the client address + case TcpRequest::SEND_CLIENT_ADDR: + // Since this IS the client, the other connection already has an IP + // There is no need to send ip, just the port + m_request.ReceiveClientAddr(m_connections[i].tcpHandler, m_request.GetClientId(), "", m_acceptor.GetSocket().local_endpoint().port()); + break; - chat.Receive(m_tcpHandler); - std::cout << "\n\n" << chat.GetMessage() << "\n\nYou: "; + default: + std::cout << "Invalid Request " << m_request.GetRequestType() << std::endl; + } + } + } + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; + } } + m_connectionsMutex.unlock(); } - catch (std::exception &ex) - { - std::cout << ex.what() << std::endl; - } +} + +void TcpClient::StartChatInput(uint32_t groupId) +{ + boost::thread t(boost::bind(&TcpClient::ChatInput, this, _1), groupId); +} + +void TcpClient::JoinChat(uint32_t connectionId, uint32_t groupId) +{ + m_request.JoinChat(m_connections[connectionId].tcpHandler, groupId); + m_currentConnection = connectionId; } void TcpClient::ChatInput(uint32_t groupId) { while (true) { + while (m_currentConnection == -1) + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + // Take input std::cout << "\nYou: "; fflush(stdin); @@ -83,9 +150,9 @@ void TcpClient::ChatInput(uint32_t groupId) std::string message = m_name + ": " + input; // Send the chat message after a GROUP_CHAT request - m_requests.GroupChat(m_tcpHandler, groupId); + m_request.ChatMessage(m_connections[m_currentConnection].tcpHandler, message.size() + 1, groupId); ChatMessage chat; chat.SetMessage(message); - chat.Send(m_tcpHandler); + chat.Send(m_connections[m_currentConnection].tcpHandler); } } diff --git a/src/client/main.cpp b/src/client/main.cpp index 04f6f1a..500e687 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -10,10 +10,7 @@ int main(int argc, char *argv[]) { boost::asio::io_service io; TcpClient client(io); - //uint16_t port; - //std::cout << "Enter port of this client: "; - //std::cin >> port; - client.StartListening(/*tcp::endpoint(tcp::v4(), port)*/); + client.StartAccepting(); // Connect to another peer /*{ @@ -26,22 +23,37 @@ int main(int argc, char *argv[]) // Connect to server { std::string ip, name; - std::cout << "Enter ip of server: "; std::cin >> ip; - client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 10011/*8183*/)); // 10011 for Ankit's Server - uint32_t groupId; - std::cout << "Enter group-id to join: "; - std::cin >> groupId; std::cout << "Enter name: "; std::fflush(stdin); char namec[200]; std::cin.getline(namec, 200); name = std::string(namec); - client.JoinGroup(groupId, name); - client.StartChatSession(groupId); + client.SetName(name); + + + std::cout << "Enter ip of server: "; std::cin >> ip; + client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 10011)); + + int choice; + std::cout << "Enter 0 for group chat, 1 for P2P, 2 to just wait: "; + std::cin >> choice; + + uint32_t groupId = 0; + if (choice == 0) + { + std::cout << "Enter group-id to join: "; + std::cin >> groupId; + client.JoinChat(0, groupId); + } + else if (choice == 1) + { + uint32_t cid; + std::cout << "Enter client-id of peer: "; std::cin >> cid; + client.Connect(cid); + } + client.StartChatInput(groupId); + client.HandleRequests(); - while (true) - boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - ; } } catch (std::exception &ex) diff --git a/src/common/ChatMessage.cpp b/src/common/ChatMessage.cpp index 9047d6c..d6b2cfe 100644 --- a/src/common/ChatMessage.cpp +++ b/src/common/ChatMessage.cpp @@ -9,17 +9,13 @@ ChatMessage::~ChatMessage() void ChatMessage::Send(TcpHandler &tcpHandler) { - uint32_t body_size = m_body.size() + 1; - tcpHandler.Send((char*)&body_size, sizeof(body_size)); - tcpHandler.Send(m_body.c_str(), body_size); + tcpHandler.Send(m_body.c_str(), m_body.size() + 1); } -void ChatMessage::Receive(TcpHandler &tcpHandler) +void ChatMessage::Receive(TcpHandler &tcpHandler, uint32_t size) { - uint32_t body_size; - tcpHandler.Receive((char*)&body_size, sizeof(body_size)); - char * data = new char[body_size]; - tcpHandler.Receive(data, body_size); + char * data = new char[size]; + tcpHandler.Receive(data, size); m_body = std::string(data); delete[] data; } \ No newline at end of file diff --git a/src/common/RequestHandler.cpp b/src/common/RequestHandler.cpp deleted file mode 100644 index f508245..0000000 --- a/src/common/RequestHandler.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include "rapidjson/document.h" -#include "rapidjson/writer.h" -#include "rapidjson/stringbuffer.h" -using namespace rapidjson; - -// Requests are of fixed size (defined by REQUEST_MAX_SIZE) -// This makes it simpler to receive just the required request data -// from the socket and neither more nor less -const size_t REQUEST_MAX_SIZE = 150; - -RequestHandler::RequestHandler() -{} - -RequestHandler::~RequestHandler() -{} - -std::string RequestHandler::GetJsonString() -{ - StringBuffer buffer; - Writer writer(buffer); - m_document.Accept(writer); - return buffer.GetString(); -} - -void RequestHandler::JoinGroup(TcpHandler &tcpHandler, uint32_t groupId) -{ - m_document = Document(); // New Document - /* - Example: - { - Request-Type: 1 - Group-Id: 20 - } - */ - m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)JOIN_GROUP), m_document.GetAllocator()); - m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy_s(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); -} - -void RequestHandler::GroupChat(TcpHandler &tcpHandler, uint32_t groupId) -{ - m_document = Document(); // New Document - /* - Example: - { - Request-Type: 2 - Group-Id: 20 - } - */ - m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)GROUP_CHAT), m_document.GetAllocator()); - m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy_s(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); -} - -void RequestHandler::ReceiveRequest(TcpHandler &tcpHandler) -{ - // Get the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - tcpHandler.Receive(request, REQUEST_MAX_SIZE); - std::cout << request << std::endl; - - // Parse the request - m_document = Document(); - m_document.Parse(request); -} - -RequestHandler::REQUEST_TYPE RequestHandler::GetRequestType() -{ - // Return INVALID_TYPE when Request-Type value is absent or isn't integer - if (!m_document.IsObject() || !m_document.HasMember("Request-Type") || !m_document["Request-Type"].IsInt()) - return INAVLID_TYPE; - return (REQUEST_TYPE)m_document["Request-Type"].GetInt(); -} - -uint32_t RequestHandler::GetGroupId() -{ - // Throw exception for absence of or invalid group-id - if (!m_document.HasMember("Group-Id") || !m_document["Group-Id"].IsInt()) - throw RequestException("Invalid group-id received"); - return m_document["Group-Id"].GetUint(); -} \ No newline at end of file diff --git a/src/common/TcpListener.cpp b/src/common/TcpAcceptor.cpp similarity index 72% rename from src/common/TcpListener.cpp rename to src/common/TcpAcceptor.cpp index 11ee73e..23d9272 100644 --- a/src/common/TcpListener.cpp +++ b/src/common/TcpAcceptor.cpp @@ -1,13 +1,13 @@ #include -#include +#include -TcpListener::TcpListener(boost::asio::io_service &io) : m_acceptor(io) +TcpAcceptor::TcpAcceptor(boost::asio::io_service &io) : m_acceptor(io) {} -TcpListener::~TcpListener() +TcpAcceptor::~TcpAcceptor() {} -uint16_t TcpListener::Initialize(const tcp::endpoint &localEndpoint) +uint16_t TcpAcceptor::Initialize(const tcp::endpoint &localEndpoint) { // Open and bind the tcp acceptor to the local endpoint m_acceptor.open(localEndpoint.protocol()); @@ -18,19 +18,19 @@ uint16_t TcpListener::Initialize(const tcp::endpoint &localEndpoint) return m_acceptor.local_endpoint().port(); } -void TcpListener::Listen(boost::function)> callback) +void TcpAcceptor::Listen(boost::function)> callback) { m_callback = callback; StartListening(); } // Create new thread to listen for incomming connections -void TcpListener::StartListening() +void TcpAcceptor::StartListening() { - boost::thread t(boost::bind(&TcpListener::ListeningThread, this)); + boost::thread t(boost::bind(&TcpAcceptor::ListeningThread, this)); } -void TcpListener::ListeningThread() +void TcpAcceptor::ListeningThread() { try { diff --git a/src/common/TcpRequest.cpp b/src/common/TcpRequest.cpp new file mode 100644 index 0000000..5e37bab --- /dev/null +++ b/src/common/TcpRequest.cpp @@ -0,0 +1,165 @@ +#include +#include +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +using namespace rapidjson; + +// Requests are of fixed size (defined by REQUEST_MAX_SIZE) +// This makes it simpler to receive just the required request data +// from the socket and neither more nor less +const size_t REQUEST_MAX_SIZE = 150; + +TcpRequest::TcpRequest() +{} + +TcpRequest::~TcpRequest() +{} + +std::string TcpRequest::GetJsonString() +{ + StringBuffer buffer; + Writer writer(buffer); + m_document.Accept(writer); + return buffer.GetString(); +} + +void TcpRequest::JoinChat(TcpHandler &tcpHandler, uint32_t groupId) +{ + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 1 + Group-Id: 20 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)JOIN_CHAT), m_document.GetAllocator()); + m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} + +void TcpRequest::ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId) +{ + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 2 + Group-Id: 20 + Message-Size: 50 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)CHAT_MESSAGE), m_document.GetAllocator()); + m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); + m_document.AddMember("Message-Size", Value(messageSize), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} + +void TcpRequest::SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId) +{ + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 3 + Client-Id: 5 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)SEND_CLIENT_ADDR), m_document.GetAllocator()); + m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} + +void TcpRequest::ReceiveClientAddr(TcpHandler &tcpHandler, uint32_t clientId, const std::string &ip, uint16_t port) +{ + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 4 + Client-Id: 5 + IP: 23.123.2.3 + Port: 23456 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)RECEIVE_CLIENT_ADDR), m_document.GetAllocator()); + m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); + m_document.AddMember("IP", Value(ip.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); + m_document.AddMember("Port", Value((unsigned int)port), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} + +void TcpRequest::ReceiveRequest(TcpHandler &tcpHandler) +{ + // Get the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + tcpHandler.Receive(request, REQUEST_MAX_SIZE); + + // Parse the request + m_document = Document(); + m_document.Parse(request); +} + +TcpRequest::REQUEST_TYPE TcpRequest::GetRequestType() +{ + auto value = m_document.FindMember("Request-Type"); + if (value == m_document.MemberEnd() || !value->value.IsInt()) + return INAVLID_TYPE; + return (REQUEST_TYPE)value->value.GetInt(); +} + +inline Value& TcpRequest::GetValue(const std::string &key) +{ + static Value sval; + auto value = m_document.FindMember(key.c_str()); + if (value == m_document.MemberEnd()) + throw RequestException("Invalid value for '" + key + "' received in the request"); + sval = value->value; + return sval; +} + +uint32_t TcpRequest::GetGroupId() +{ + return GetValue("Group-Id").GetUint(); +} + +uint32_t TcpRequest::GetMessageSize() +{ + return GetValue("Message-Size").GetUint();; +} + +uint16_t TcpRequest::GetPort() +{ + return GetValue("Port").GetUint(); +} + +uint32_t TcpRequest::GetClientId() +{ + return GetValue("Client-Id").GetUint(); +} + +std::string TcpRequest::GetIp() +{ + return GetValue("IP").GetString(); +} \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 30928dd..7da71ed 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -3,7 +3,7 @@ #include ClientsManager::ClientsManager(boost::asio::io_service& io) -: m_listener(io), m_ioService(io) +: m_acceptor(io), m_ioService(io) {} ClientsManager::~ClientsManager() @@ -12,8 +12,8 @@ ClientsManager::~ClientsManager() // Use listener to listen to incoming clients void ClientsManager::StartListening(const tcp::endpoint &localEndpoint) { - m_listener.Initialize(localEndpoint); - m_listener.Listen(boost::bind(&ClientsManager::HandleClient, this, _1)); + m_acceptor.Initialize(localEndpoint); + m_acceptor.Listen(boost::bind(&ClientsManager::HandleClient, this, _1)); } // Add any client that is connected to the clients list @@ -23,7 +23,7 @@ void ClientsManager::HandleClient(boost::shared_ptr &socket) ClientInfo client(m_ioService); client.connection.Initialize(socket); m_clients.push_back(client); - std::cout << "Client Connected: " << m_clients[m_clients.size() - 1].connection.GetDestinationAddress() << std::endl; + std::cout << "Client #" << m_clients.size() - 1 << " Connected: " << m_clients[m_clients.size() - 1].connection.GetDestinationAddress() << std::endl; } // Start processing each client @@ -38,34 +38,51 @@ void ClientsManager::ProcessClients() { while (true) { + // Sleep for some time if no client is available while (m_clients.size() == 0) boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + + // Lock the clients-list while processing the clients m_mutex.lock(); // Process each client in turn for (unsigned int i = 0; i < m_clients.size(); ++i) { try { + uint32_t id; // See if any request is incomming for this client size_t bytes = m_clients[i].connection.Available(); - uint32_t gid; - // if so, process accordingly if (bytes > 0) { + // if so receive the request and process accordingly m_requests.ReceiveRequest(m_clients[i].connection); + switch (m_requests.GetRequestType()) { - case RequestHandler::JOIN_GROUP: - gid = m_requests.GetGroupId(); + // Request to join chat, since this is the sever, this is a request to join a group chat + case TcpRequest::JOIN_CHAT: + id = m_requests.GetGroupId(); // push the client id to the group - m_groups[gid].push_back(i); - std::cout << "Connected client #" << i << " to group #" << gid << std::endl; + m_groups[id].push_back(i); + std::cout << "Connected client #" << i << " to group #" << id << std::endl; break; - - case RequestHandler::GROUP_CHAT: - gid = m_requests.GetGroupId(); + // Request to receive an incoming chat message + case TcpRequest::CHAT_MESSAGE: + id = m_requests.GetGroupId(); // receive the chat message - ReceiveChat(i, gid); + ReceiveChat(i, id); + break; + // Request to send a client address + case TcpRequest::SEND_CLIENT_ADDR: + id = m_requests.GetClientId(); + if (id <= m_clients.size()) + { + // Request the client for the address + m_requests.SendClientAddr(m_clients[id].connection, id); + m_requests.ReceiveRequest(m_clients[id].connection); + // Send back a request filled with required info + m_requests.ReceiveClientAddr(m_clients[i].connection, id, m_clients[id].connection.GetIp(), m_requests.GetPort()); + } break; default: @@ -85,7 +102,7 @@ void ClientsManager::ProcessClients() void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) { ChatMessage chat; - chat.Receive(m_clients[client].connection); + chat.Receive(m_clients[client].connection, m_requests.GetMessageSize()); //m_mutex.lock(); // Send the messsage to each client in the group @@ -94,7 +111,10 @@ void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) try { if (m_groups[group][i] != client) + { + m_requests.ChatMessage(m_clients[i].connection, chat.GetMessage().size() + 1); chat.Send(m_clients[m_groups[group][i]].connection); + } } // client may be disconnected and exception may be thrown // we catch the exception inside the loop so that From 7a6b0e54de379019f0937b7f7fd7bc7ca6c9c802 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Thu, 25 Dec 2014 14:23:38 +0545 Subject: [PATCH 41/54] Simple TCP hole punching... remains to be tested --- include/client/TcpClient.h | 21 +++--- include/common/TcpRequest.h | 18 ++--- src/client/TcpClient.cpp | 134 +++++++++++++++++++++++----------- src/client/main.cpp | 2 +- src/common/TcpAcceptor.cpp | 2 +- src/common/TcpHandler.cpp | 3 +- src/common/TcpRequest.cpp | 59 +++++++-------- src/server/ClientsManager.cpp | 16 ++-- 8 files changed, 155 insertions(+), 100 deletions(-) diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index b697b57..67c962a 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -10,16 +10,14 @@ class TcpClient TcpClient(boost::asio::io_service &io); ~TcpClient(); - // Start listening for incoming connections in a new thread - void StartAccepting(); // Connect to a peer/server void Connect(const tcp::endpoint& peer, bool joinChat = false); // Connect to a peer/server asynchronously void ConnectAsync(const tcp::endpoint& peer, bool joinChat = false); - // Handle all requests that the client gets - void HandleRequests(); // Connect to a peer through server void Connect(uint32_t clientId); + // Start handling all requests that the client gets + void HandleRequests(); // Join chat void JoinChat(uint32_t connectionId, uint32_t groupId); @@ -42,17 +40,22 @@ class TcpClient }; boost::asio::io_service &m_io; - TcpAcceptor m_acceptor; + // List of the connections std::vector m_connections; - // Mutex to lock m_connections vector - boost::mutex m_connectionsMutex; + // Mutex to lock 'm_connections' list as well as 'm_request' request-processor + boost::mutex m_mutex; TcpRequest m_request; std::string m_name; - void AcceptorHandler(boost::shared_ptr socket); - + // For P2P: + bool m_p2pConnected; + void HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); + void HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); + boost::shared_ptr m_acceptor; + void P2PListen(const tcp::endpoint &localEndpoint); + void P2PConnect(tcp::endpoint &remoteEndpoint); // For console: int m_currentConnection; diff --git a/include/common/TcpRequest.h b/include/common/TcpRequest.h index f323fba..d21c375 100644 --- a/include/common/TcpRequest.h +++ b/include/common/TcpRequest.h @@ -15,7 +15,7 @@ class TcpRequest enum REQUEST_TYPE{ INAVLID_TYPE = 0, JOIN_CHAT, CHAT_MESSAGE, - SEND_CLIENT_ADDR, RECEIVE_CLIENT_ADDR, + P2P_TCP, }; TcpRequest(); @@ -25,12 +25,10 @@ class TcpRequest void JoinChat(TcpHandler &tcpHandler, uint32_t groupId = 0); // Request to wait for incoming chat message void ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId = 0); - // Request to send a client address (ip and port) - // For server, provide a valid clientId - void SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId); - // Request to receive a client address (ip and port) as response for a SEND_CLIENT_ADDR request - // For server, provide a valid clientId - void ReceiveClientAddr(TcpHandler &tcpHandler, uint32_t clientId, const std::string &ip, uint16_t port); + // Request to establish a P2P TCP connection with a client + // Contains private and public addess-port pairs and client-id + void P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp = "", uint16_t publicPort = 0); + // Receive a request void ReceiveRequest(TcpHandler &tcpHandler); @@ -39,8 +37,10 @@ class TcpRequest uint32_t GetGroupId(); uint32_t GetMessageSize(); uint32_t GetClientId(); - std::string GetIp(); - uint16_t GetPort(); + std::string GetPrivateIp(); + uint16_t GetPrivatePort(); + std::string GetPublicIp(); + uint16_t GetPublicPort(); private: // The Json Document that holds the request rapidjson::Document m_document; diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index 2e7eaaa..0a6731d 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -3,40 +3,17 @@ #include TcpClient::TcpClient(boost::asio::io_service &io) -:m_io(io), m_acceptor(io), m_currentConnection(-1) +:m_io(io), /*m_acceptor(io),*/ m_currentConnection(-1) {} TcpClient::~TcpClient() {} -void TcpClient::StartAccepting() -{ - uint16_t port = m_acceptor.Initialize(tcp::endpoint(tcp::v4(), 0)); - std::cout << "Listening at port: " << port << std::endl; - - // Listen method will start listening in new thread - m_acceptor.Listen(boost::bind(&TcpClient::AcceptorHandler, this, _1)); -} - -// This is the handler called when the acceptor accepts a new connection -void TcpClient::AcceptorHandler(boost::shared_ptr socket) -{ - // Lock the connections list not to process it while adding a connection - boost::lock_guard guard(m_connectionsMutex); - - // Add new connection - m_connections.push_back(Connection(m_io)); - TcpHandler &handler = m_connections[m_connections.size() - 1].tcpHandler; - handler.Initialize(socket); - - std::cout << "Connected to: " << handler.GetDestinationAddress() << std::endl; -} - // Connecting to some remote peer (server/client) void TcpClient::Connect(const tcp::endpoint& peer, bool joinChat) { // Lock the connections list not to process it while adding a connection - boost::lock_guard guard(m_connectionsMutex); + boost::lock_guard guard(m_mutex); // Add new connection m_connections.push_back(Connection(m_io)); @@ -58,14 +35,86 @@ void TcpClient::ConnectAsync(const tcp::endpoint& peer, bool joinChat) // Use server to connect to a client (for P2P) void TcpClient::Connect(uint32_t clientId) { - // Send a request to get a client-address; assuming server id first element in the connections-list - m_request.SendClientAddr(m_connections[0].tcpHandler, clientId); - - // Receive for the response and use it to connect to the client - m_request.ReceiveRequest(m_connections[0].tcpHandler); - if (m_request.GetRequestType() != TcpRequest::RECEIVE_CLIENT_ADDR) - throw Exception("Expecting a client address but getting some other request"); - Connect(tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetIp()), m_request.GetPort()), true); + // make sure no requests are being processed during this time + boost::lock_guard guard(m_mutex); + + // First send a P2P tcp connection request to server and receive back the returning request + TcpHandler& handler = m_connections[0].tcpHandler; + m_request.P2PTcp(handler, clientId, handler.GetIp(), handler.GetPort()); + m_request.ReceiveRequest(handler); + + HandleP2PRequest(m_request.GetClientId(), + tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPrivateIp()), m_request.GetPrivatePort()), + tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort())); +} + +void TcpClient::HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint) +{ + TcpHandler& handler = m_connections[0].tcpHandler; + // Start listening at same local endpoint as which is connected to the server + boost::thread t1(boost::bind(&TcpClient::P2PListen, this, _1), handler.GetSocket()->local_endpoint()); + // Try connecting to both private and public endpoints of other peer + boost::thread t2(boost::bind(&TcpClient::P2PConnect, this, _1), privateEndpoint); + boost::thread t3(boost::bind(&TcpClient::P2PConnect, this, _1), publicEndpoint); + + while (!m_p2pConnected); + if (m_acceptor) + m_acceptor->cancel(); +} + +void TcpClient::HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint) +{ + boost::thread t([this, clientId, privateEndpoint, publicEndpoint]() + { + boost::lock_guard guard(m_mutex); + HandleP2PRequest(clientId, privateEndpoint, publicEndpoint); + }); +} + +void TcpClient::P2PListen(const tcp::endpoint &localEndpoint) +{ + try + { + m_acceptor.reset(new tcp::acceptor(m_io, localEndpoint, true)); + // Create a new socket to represent a new connection + boost::shared_ptr socket(new tcp::socket(m_acceptor->get_io_service())); + // Wait for a connection and accept at the socket + m_acceptor->accept(*socket); + + /* + TODO: Need to verify if this is the required peer + */ + Connection c(m_io); + c.tcpHandler.Initialize(socket); + m_connections.push_back(c); + m_p2pConnected = true; + + m_acceptor.reset(); + } + catch (std::exception &ex) + { + //std::cout << ex.what() << std::endl; + } +} +; +void TcpClient::P2PConnect(tcp::endpoint &remoteEndpoint) +{ + while (!m_p2pConnected) + { + try + { + // Try connecting at the given remote endpoint + Connection c(m_io); + c.tcpHandler.Initialize(remoteEndpoint); + m_connections.push_back(c); + m_p2pConnected = true; + JoinChat(m_connections.size() - 1, 0); + } + catch (std::exception &ex) + { + //std::cout << ex.what() << std::endl; + } + } } // An infinite loop to handle incoming requests @@ -78,7 +127,7 @@ void TcpClient::HandleRequests() boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); // While processing connections, lock the list so no new connection is added at the time - m_connectionsMutex.lock(); + m_mutex.lock(); for (unsigned int i = 0; i < m_connections.size(); ++i) { try @@ -86,6 +135,7 @@ void TcpClient::HandleRequests() // See if any request is incomming for this connection size_t bytes = m_connections[i].tcpHandler.Available(); ChatMessage chat; + uint32_t id; // if so, process accordingly if (bytes > 0) { @@ -102,13 +152,15 @@ void TcpClient::HandleRequests() m_currentConnection = i; std::cout << "\n\n" << chat.GetMessage() << "\n\nYou: "; break; - // Request to send the client address - case TcpRequest::SEND_CLIENT_ADDR: - // Since this IS the client, the other connection already has an IP - // There is no need to send ip, just the port - m_request.ReceiveClientAddr(m_connections[i].tcpHandler, m_request.GetClientId(), "", m_acceptor.GetSocket().local_endpoint().port()); + // Request for a p2p tcp connection + case TcpRequest::P2P_TCP: + id = m_request.GetClientId(); + HandleP2PRequestAsync(id, + tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPrivateIp()), m_request.GetPrivatePort()), + tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort())); + m_request.P2PTcp(m_connections[0].tcpHandler, id, + m_connections[0].tcpHandler.GetIp(), m_connections[0].tcpHandler.GetPort()); break; - default: std::cout << "Invalid Request " << m_request.GetRequestType() << std::endl; } @@ -119,7 +171,7 @@ void TcpClient::HandleRequests() std::cout << ex.what() << std::endl; } } - m_connectionsMutex.unlock(); + m_mutex.unlock(); } } diff --git a/src/client/main.cpp b/src/client/main.cpp index 500e687..0dca2a5 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { boost::asio::io_service io; TcpClient client(io); - client.StartAccepting(); + //client.StartAccepting(); // Connect to another peer /*{ diff --git a/src/common/TcpAcceptor.cpp b/src/common/TcpAcceptor.cpp index 23d9272..c4d51af 100644 --- a/src/common/TcpAcceptor.cpp +++ b/src/common/TcpAcceptor.cpp @@ -11,7 +11,7 @@ uint16_t TcpAcceptor::Initialize(const tcp::endpoint &localEndpoint) { // Open and bind the tcp acceptor to the local endpoint m_acceptor.open(localEndpoint.protocol()); - m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + //m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); m_acceptor.bind(localEndpoint); // Prepare to listen m_acceptor.listen(); diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index 0b4d2a8..c4ebc18 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -25,7 +25,8 @@ void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) { if (m_socket) throw TcpHandlerException("Socket alread created"); - m_socket.reset(new tcp::socket(m_ioService)); + m_socket.reset(new tcp::socket(m_ioService, destEndpoint.protocol())); + m_socket->set_option(tcp::socket::reuse_address(true)); m_socket->connect(destEndpoint); } diff --git a/src/common/TcpRequest.cpp b/src/common/TcpRequest.cpp index 5e37bab..d4432ec 100644 --- a/src/common/TcpRequest.cpp +++ b/src/common/TcpRequest.cpp @@ -66,19 +66,27 @@ void TcpRequest::ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint3 tcpHandler.Send(request, REQUEST_MAX_SIZE); } -void TcpRequest::SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId) +void TcpRequest::P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp, uint16_t publicPort) { m_document = Document(); // New Document /* Example: { Request-Type: 3 - Client-Id: 5 + Client-Id: 32 + Private-IP: 1.2.3.4 + Private-Port: 1234 + Public-IP: 2.3.5.6 + Public-Port: 2356 } */ m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)SEND_CLIENT_ADDR), m_document.GetAllocator()); + m_document.AddMember("Request-Type", Value((int)P2P_TCP), m_document.GetAllocator()); m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); + m_document.AddMember("Private-IP", Value(privateIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); + m_document.AddMember("Private-Port", Value((unsigned int)privatePort), m_document.GetAllocator()); + m_document.AddMember("Public-IP", Value(publicIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); + m_document.AddMember("Public-Port", Value((unsigned int)publicPort), m_document.GetAllocator()); // Send the "fixed-size" request char request[REQUEST_MAX_SIZE]; @@ -86,29 +94,6 @@ void TcpRequest::SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId) tcpHandler.Send(request, REQUEST_MAX_SIZE); } -void TcpRequest::ReceiveClientAddr(TcpHandler &tcpHandler, uint32_t clientId, const std::string &ip, uint16_t port) -{ - m_document = Document(); // New Document - /* - Example: - { - Request-Type: 4 - Client-Id: 5 - IP: 23.123.2.3 - Port: 23456 - } - */ - m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)RECEIVE_CLIENT_ADDR), m_document.GetAllocator()); - m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); - m_document.AddMember("IP", Value(ip.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); - m_document.AddMember("Port", Value((unsigned int)port), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); -} void TcpRequest::ReceiveRequest(TcpHandler &tcpHandler) { @@ -149,17 +134,27 @@ uint32_t TcpRequest::GetMessageSize() return GetValue("Message-Size").GetUint();; } -uint16_t TcpRequest::GetPort() +uint32_t TcpRequest::GetClientId() { - return GetValue("Port").GetUint(); + return GetValue("Client-Id").GetUint(); } -uint32_t TcpRequest::GetClientId() +std::string TcpRequest::GetPrivateIp() { - return GetValue("Client-Id").GetUint(); + return GetValue("Private-IP").GetString(); +} + +uint16_t TcpRequest::GetPrivatePort() +{ + return GetValue("Private-Port").GetUint(); +} + +std::string TcpRequest::GetPublicIp() +{ + return GetValue("Public-IP").GetString(); } -std::string TcpRequest::GetIp() +uint16_t TcpRequest::GetPublicPort() { - return GetValue("IP").GetString(); + return GetValue("Public-Port").GetUint(); } \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 7da71ed..97e4fa7 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -72,16 +72,20 @@ void ClientsManager::ProcessClients() // receive the chat message ReceiveChat(i, id); break; - // Request to send a client address - case TcpRequest::SEND_CLIENT_ADDR: + + // Request for a p2p tcp connection to another client + case TcpRequest::P2P_TCP: id = m_requests.GetClientId(); if (id <= m_clients.size()) { - // Request the client for the address - m_requests.SendClientAddr(m_clients[id].connection, id); + // Send request to second client + m_requests.P2PTcp(m_clients[id].connection, i, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), + m_clients[i].connection.GetIp(), m_clients[i].connection.GetPort()); + // Receive the return request m_requests.ReceiveRequest(m_clients[id].connection); - // Send back a request filled with required info - m_requests.ReceiveClientAddr(m_clients[i].connection, id, m_clients[id].connection.GetIp(), m_requests.GetPort()); + // Send back the return request to first client + m_requests.P2PTcp(m_clients[i].connection, id, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), + m_clients[id].connection.GetIp(), m_clients[id].connection.GetPort()); } break; From 3d57559a62617753a6888f08337cbf697a1edaa1 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Thu, 25 Dec 2014 15:18:14 +0545 Subject: [PATCH 42/54] Few Bugs Fixed --- include/common/TcpHandler.h | 2 +- src/client/TcpClient.cpp | 32 ++++++++++++++++++++++---------- src/common/TcpHandler.cpp | 3 +-- src/server/ClientsManager.cpp | 2 +- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index a4d7860..a53f5ec 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -17,7 +17,7 @@ class TcpHandler // Initialize with an already created socket void Initialize(boost::shared_ptr socket); - // Connect to given endpoint and initialize by creating new socker + // Connect to given endpoint and initialize by creating new socket void Initialize(const tcp::endpoint &destEndpoint); // Send data of given size diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index 0a6731d..6f9eb4a 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -35,12 +35,13 @@ void TcpClient::ConnectAsync(const tcp::endpoint& peer, bool joinChat) // Use server to connect to a client (for P2P) void TcpClient::Connect(uint32_t clientId) { + m_p2pConnected = false; // make sure no requests are being processed during this time boost::lock_guard guard(m_mutex); // First send a P2P tcp connection request to server and receive back the returning request TcpHandler& handler = m_connections[0].tcpHandler; - m_request.P2PTcp(handler, clientId, handler.GetIp(), handler.GetPort()); + m_request.P2PTcp(handler, clientId, handler.GetSocket()->local_endpoint().address().to_string(), handler.GetSocket()->local_endpoint().port()); m_request.ReceiveRequest(handler); HandleP2PRequest(m_request.GetClientId(), @@ -54,7 +55,8 @@ void TcpClient::HandleP2PRequest(uint32_t clientId, const tcp::endpoint &private // Start listening at same local endpoint as which is connected to the server boost::thread t1(boost::bind(&TcpClient::P2PListen, this, _1), handler.GetSocket()->local_endpoint()); // Try connecting to both private and public endpoints of other peer - boost::thread t2(boost::bind(&TcpClient::P2PConnect, this, _1), privateEndpoint); + if (privateEndpoint!=publicEndpoint) + boost::thread t2(boost::bind(&TcpClient::P2PConnect, this, _1), privateEndpoint); boost::thread t3(boost::bind(&TcpClient::P2PConnect, this, _1), publicEndpoint); while (!m_p2pConnected); @@ -66,8 +68,15 @@ void TcpClient::HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &pr { boost::thread t([this, clientId, privateEndpoint, publicEndpoint]() { - boost::lock_guard guard(m_mutex); - HandleP2PRequest(clientId, privateEndpoint, publicEndpoint); + try + { + boost::lock_guard guard(m_mutex); + HandleP2PRequest(clientId, privateEndpoint, publicEndpoint); + } + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; + } }); } @@ -80,7 +89,8 @@ void TcpClient::P2PListen(const tcp::endpoint &localEndpoint) boost::shared_ptr socket(new tcp::socket(m_acceptor->get_io_service())); // Wait for a connection and accept at the socket m_acceptor->accept(*socket); - + if (m_p2pConnected) return; + /* TODO: Need to verify if this is the required peer */ @@ -88,12 +98,10 @@ void TcpClient::P2PListen(const tcp::endpoint &localEndpoint) c.tcpHandler.Initialize(socket); m_connections.push_back(c); m_p2pConnected = true; - - m_acceptor.reset(); } catch (std::exception &ex) { - //std::cout << ex.what() << std::endl; + std::cout << ex.what() << std::endl; } } ; @@ -106,13 +114,15 @@ void TcpClient::P2PConnect(tcp::endpoint &remoteEndpoint) // Try connecting at the given remote endpoint Connection c(m_io); c.tcpHandler.Initialize(remoteEndpoint); + if (m_p2pConnected) return; + m_connections.push_back(c); m_p2pConnected = true; JoinChat(m_connections.size() - 1, 0); } catch (std::exception &ex) { - //std::cout << ex.what() << std::endl; + std::cout << ex.what() << std::endl; } } } @@ -155,11 +165,13 @@ void TcpClient::HandleRequests() // Request for a p2p tcp connection case TcpRequest::P2P_TCP: id = m_request.GetClientId(); + m_p2pConnected = false; HandleP2PRequestAsync(id, tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPrivateIp()), m_request.GetPrivatePort()), tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort())); m_request.P2PTcp(m_connections[0].tcpHandler, id, - m_connections[0].tcpHandler.GetIp(), m_connections[0].tcpHandler.GetPort()); + m_connections[0].tcpHandler.GetSocket()->local_endpoint().address().to_string(), m_connections[0].tcpHandler.GetSocket()->local_endpoint().port() + ); break; default: std::cout << "Invalid Request " << m_request.GetRequestType() << std::endl; diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index c4ebc18..0b4d2a8 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -25,8 +25,7 @@ void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) { if (m_socket) throw TcpHandlerException("Socket alread created"); - m_socket.reset(new tcp::socket(m_ioService, destEndpoint.protocol())); - m_socket->set_option(tcp::socket::reuse_address(true)); + m_socket.reset(new tcp::socket(m_ioService)); m_socket->connect(destEndpoint); } diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 97e4fa7..4af492e 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -59,7 +59,7 @@ void ClientsManager::ProcessClients() switch (m_requests.GetRequestType()) { - // Request to join chat, since this is the sever, this is a request to join a group chat + // Request to join chat, since this is the server, this is a request to join a group chat case TcpRequest::JOIN_CHAT: id = m_requests.GetGroupId(); // push the client id to the group From 599dd15bf8beae9cbe61e708cdff698b018f687a Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Thu, 25 Dec 2014 21:13:33 +0545 Subject: [PATCH 43/54] okie what is this!!! zzz --- .gitignore | 1 - include/client/FrameRenderer.h | 2 +- include/client/TcpClient.h | 55 +- include/{common => client/UI}/Button.h | 4 +- include/client/UI/Control.h | 31 + include/client/VideoCapture.h | 12 +- include/common/ChatMessage.h | 19 - include/common/Control.h | 19 - include/common/Page.h | 16 - include/common/RtpHandler.h | 51 - include/common/TcpAcceptor.h | 23 - include/common/TcpRequest.h | 51 - include/rapidjson/allocators.h | 245 --- include/rapidjson/document.h | 1927 ----------------------- include/rapidjson/encodedstream.h | 290 ---- include/rapidjson/encodings.h | 630 -------- include/rapidjson/error/en.h | 71 - include/rapidjson/error/error.h | 150 -- include/rapidjson/filereadstream.h | 94 -- include/rapidjson/filestream.h | 73 - include/rapidjson/filewritestream.h | 97 -- include/rapidjson/internal/biginteger.h | 290 ---- include/rapidjson/internal/diyfp.h | 268 ---- include/rapidjson/internal/dtoa.h | 225 --- include/rapidjson/internal/ieee754.h | 90 -- include/rapidjson/internal/itoa.h | 306 ---- include/rapidjson/internal/meta.h | 189 --- include/rapidjson/internal/pow10.h | 59 - include/rapidjson/internal/stack.h | 183 --- include/rapidjson/internal/strfunc.h | 43 - include/rapidjson/internal/strtod.h | 285 ---- include/rapidjson/memorybuffer.h | 76 - include/rapidjson/memorystream.h | 67 - include/rapidjson/msinttypes/inttypes.h | 312 ---- include/rapidjson/msinttypes/stdint.h | 296 ---- include/rapidjson/prettywriter.h | 205 --- include/rapidjson/rapidjson.h | 628 -------- include/rapidjson/reader.h | 1446 ----------------- include/rapidjson/stringbuffer.h | 99 -- include/rapidjson/writer.h | 391 ----- include/server/ClientsManager.h | 43 - makefile_client | 28 - output | Bin 408519 -> 0 bytes src/common/Button.cpp | 0 src/common/ChatMessage.cpp | 21 - src/common/Control.cpp | 6 - src/common/Page.cpp | 0 src/common/RtpHandler.cpp | 75 - src/common/TcpAcceptor.cpp | 51 - src/common/TcpRequest.cpp | 160 -- src/server/ClientsManager.cpp | 132 -- 51 files changed, 46 insertions(+), 9789 deletions(-) rename include/{common => client/UI}/Button.h (50%) create mode 100644 include/client/UI/Control.h delete mode 100644 include/common/ChatMessage.h delete mode 100644 include/common/Control.h delete mode 100644 include/common/Page.h delete mode 100644 include/common/RtpHandler.h delete mode 100644 include/common/TcpAcceptor.h delete mode 100644 include/common/TcpRequest.h delete mode 100644 include/rapidjson/allocators.h delete mode 100644 include/rapidjson/document.h delete mode 100644 include/rapidjson/encodedstream.h delete mode 100644 include/rapidjson/encodings.h delete mode 100644 include/rapidjson/error/en.h delete mode 100644 include/rapidjson/error/error.h delete mode 100644 include/rapidjson/filereadstream.h delete mode 100644 include/rapidjson/filestream.h delete mode 100644 include/rapidjson/filewritestream.h delete mode 100644 include/rapidjson/internal/biginteger.h delete mode 100644 include/rapidjson/internal/diyfp.h delete mode 100644 include/rapidjson/internal/dtoa.h delete mode 100644 include/rapidjson/internal/ieee754.h delete mode 100644 include/rapidjson/internal/itoa.h delete mode 100644 include/rapidjson/internal/meta.h delete mode 100644 include/rapidjson/internal/pow10.h delete mode 100644 include/rapidjson/internal/stack.h delete mode 100644 include/rapidjson/internal/strfunc.h delete mode 100644 include/rapidjson/internal/strtod.h delete mode 100644 include/rapidjson/memorybuffer.h delete mode 100644 include/rapidjson/memorystream.h delete mode 100644 include/rapidjson/msinttypes/inttypes.h delete mode 100644 include/rapidjson/msinttypes/stdint.h delete mode 100644 include/rapidjson/prettywriter.h delete mode 100644 include/rapidjson/rapidjson.h delete mode 100644 include/rapidjson/reader.h delete mode 100644 include/rapidjson/stringbuffer.h delete mode 100644 include/rapidjson/writer.h delete mode 100644 include/server/ClientsManager.h delete mode 100644 makefile_client delete mode 100755 output delete mode 100644 src/common/Button.cpp delete mode 100644 src/common/ChatMessage.cpp delete mode 100644 src/common/Control.cpp delete mode 100644 src/common/Page.cpp delete mode 100644 src/common/RtpHandler.cpp delete mode 100644 src/common/TcpAcceptor.cpp delete mode 100644 src/common/TcpRequest.cpp delete mode 100644 src/server/ClientsManager.cpp diff --git a/.gitignore b/.gitignore index fe70f20..8fb39aa 100644 --- a/.gitignore +++ b/.gitignore @@ -158,4 +158,3 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store -/MSVC_2013/Mirror-Client diff --git a/include/client/FrameRenderer.h b/include/client/FrameRenderer.h index 0ab2834..02c1017 100644 --- a/include/client/FrameRenderer.h +++ b/include/client/FrameRenderer.h @@ -9,7 +9,7 @@ class FrameRenderer public: FrameRenderer() :m_parentWindow(0), m_drawingArea(0), m_rgbData(0), m_rgbImage(0){} void SetRGBData(unsigned char* rgbData); - void Initialize(GtkWidget* parentWindow, int x, int y, int fw, int fh); + void Initialize(GtkWidget* parentWindow, GtkWidget* fixed, int x, int y, int fw, int fh); static gboolean OnDraw(GtkWidget* widget, cairo_t* cr, gpointer frPointer); private: GtkWidget* m_parentWindow; diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 67c962a..9f01737 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -1,8 +1,5 @@ #pragma once -#include -#include -#include - +#include class TcpClient { @@ -10,55 +7,11 @@ class TcpClient TcpClient(boost::asio::io_service &io); ~TcpClient(); - // Connect to a peer/server - void Connect(const tcp::endpoint& peer, bool joinChat = false); - // Connect to a peer/server asynchronously - void ConnectAsync(const tcp::endpoint& peer, bool joinChat = false); - // Connect to a peer through server - void Connect(uint32_t clientId); - // Start handling all requests that the client gets - void HandleRequests(); - - // Join chat - void JoinChat(uint32_t connectionId, uint32_t groupId); - // Set username to use while sending messages - void SetName(const std::string &name) { m_name = name; } - - // For console: - // Start Chat Input in a separate thread - void StartChatInput(uint32_t groupId); + void StartListening(const tcp::endpoint& server); private: - // Connection representing a tcp-connection with a peer/server - struct Connection - { - Connection(boost::asio::io_service &io) - : tcpHandler(io) - {} - TcpHandler tcpHandler; - // maybe store userid and other stuffs here... - }; - boost::asio::io_service &m_io; + TcpListener m_listener; - // List of the connections - std::vector m_connections; - // Mutex to lock 'm_connections' list as well as 'm_request' request-processor - boost::mutex m_mutex; - - TcpRequest m_request; - std::string m_name; - - // For P2P: - bool m_p2pConnected; - void HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); - void HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); - boost::shared_ptr m_acceptor; - void P2PListen(const tcp::endpoint &localEndpoint); - void P2PConnect(tcp::endpoint &remoteEndpoint); - - // For console: - int m_currentConnection; - // Separate thread for inputting chat messages - void ChatInput(uint32_t groupId); + void ListenerHandler(boost::shared_ptr socket); }; \ No newline at end of file diff --git a/include/common/Button.h b/include/client/UI/Button.h similarity index 50% rename from include/common/Button.h rename to include/client/UI/Button.h index d8de677..aac23a9 100644 --- a/include/common/Button.h +++ b/include/client/UI/Button.h @@ -1,13 +1,13 @@ #ifndef __BUTTON__ #define __BUTTON__ -#include "Control.h" +#include "client/UI/Control.h" class Button : public Control { public: Button(); - Button(std::string label, int x, int y, int w, int h); + void Set(std::string label, GtkWidget* fixed, int x, int y, int w, int h); private: }; diff --git a/include/client/UI/Control.h b/include/client/UI/Control.h new file mode 100644 index 0000000..2c13638 --- /dev/null +++ b/include/client/UI/Control.h @@ -0,0 +1,31 @@ +#ifndef __CONTROL__ +#define __CONTROL__ + +#include +#include + +enum CONTROL{ BUTTON, LABEL, TEXTEDIT, STATUSBAR}; + +// base class for UI control elements +class Control +{ +public: + + Control() :m_handle(0){} + void SetID(int id); + int GetID(); + void PutFixedAt(GtkWidget *fixed, int x, int y); + void PutFixedRelativeTo(Control* obj, GtkWidget* fixed, int xOffset, int yOffset); + void Show(); + void Hide(); + GtkWidget* GetHandle(); + // void SetEventHandler(); +protected: + GtkWidget* m_handle; + int m_x, m_y, m_w, m_h; + std::string m_label; + int m_type; + int m_id; +}; + +#endif \ No newline at end of file diff --git a/include/client/VideoCapture.h b/include/client/VideoCapture.h index 625cb07..7c6a4e2 100644 --- a/include/client/VideoCapture.h +++ b/include/client/VideoCapture.h @@ -12,24 +12,28 @@ class VideoCaptureDevice }; -/* + class VideoCapture { public: - VideoCapture():m_fw(0), m_fh(0){} + VideoCapture():m_fw(0), m_fh(0), m_fps(0){} std::vector Enumerate(); + void CaptureFrame(); unsigned char* GetBGRAFrame(); int GetFrameWidth(); int GetFrameHeight(); void SetFrameWidth(); void SetFrameHeight(); void Initialize(int deviceID = 0); + double GetFPS(); + ~VideoCapture(); private: cv::VideoCapture m_captureDevice; + cv::VideoWriter m_videoWriter; cv::Mat m_capturedFrame; int m_fw, m_fh; + double m_fps; }; -*/ -#endif +#endif \ No newline at end of file diff --git a/include/common/ChatMessage.h b/include/common/ChatMessage.h deleted file mode 100644 index 39bec04..0000000 --- a/include/common/ChatMessage.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include "TcpHandler.h" - -// This class is used to send and receive chat messages with header through a TcpHandler -class ChatMessage -{ -public: - ChatMessage(); - ~ChatMessage(); - - const std::string& GetMessage() const { return m_body; } - void SetMessage(const std::string& message) { m_body = message; } - - void Send(TcpHandler &tcpHandler); - void Receive(TcpHandler &tcpHandler, uint32_t size); - -private:; - std::string m_body; -}; \ No newline at end of file diff --git a/include/common/Control.h b/include/common/Control.h deleted file mode 100644 index cb70a2a..0000000 --- a/include/common/Control.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __CONTROL__ -#define __CONTROL__ - -#include -#include - -class Control -{ -public: - Control() :m_handle(0){} - void PutFixedAt(GtkWidget *fixed, int x, int y); - void PutFixedRelativeTo(GtkWidget* fixed, int xOffset, int yOffset); -protected: - GtkWidget* m_handle; - int m_x, m_y, m_w, m_h; - std::string m_label; -}; - -#endif \ No newline at end of file diff --git a/include/common/Page.h b/include/common/Page.h deleted file mode 100644 index 2b29682..0000000 --- a/include/common/Page.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __PAGE__ -#define __PAGE__ - -#include "Control.h" - -#include -#include - -class Page -{ -public: -protected: - std::vector m_controls; -}; - -#endif \ No newline at end of file diff --git a/include/common/RtpHandler.h b/include/common/RtpHandler.h deleted file mode 100644 index 9246ce3..0000000 --- a/include/common/RtpHandler.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -class RtpTransmissionException : public Exception -{ -public: - RtpTransmissionException(const std::string &errorString) - : Exception("RTP Transmission Error: " + errorString) - {} -}; -class RtpReceptionError : public Exception -{ -public: - RtpReceptionError(std::string errorString) - : Exception("RTP Reception Error: " + errorString) - {} -}; - -const size_t RTP_HEADER_SIZE = 12; -class RtpHandler -{ -public: - RtpHandler(); - ~RtpHandler(); - - void Initialize(boost::shared_ptr socket, const udp::endpoint &remoteEndpoint); - void CleanUp(); - - int GetTimeStamp() const { return m_timeStamp; } - void SetTimeStamp(int timeStamp) { m_timeStamp = timeStamp; } - - int GetTimeStampIncrement() const { return m_timeStampIncrement; } - void SetTimeStampIncrement(int timeStampIncrement) { m_timeStampIncrement = timeStampIncrement; } - - uint16_t GetSequenceNumber() const { return m_sequenceNumber; } - void SetSequenceNumber(uint16_t sequenceNumber) { m_sequenceNumber = sequenceNumber; } - - uint8_t GetPayloadType() const { return m_payloadType; } - void SetPayloadType(uint8_t payloadType) { m_payloadType = payloadType; } - - const udp::endpoint& GetRemoteEndpoint() const { return m_remoteEndpoint; } - - void Send(const char *data, size_t size); - void Receive(char* data, size_t maxSize = 1500); -private: - boost::shared_ptr m_socket; - udp::endpoint m_remoteEndpoint; - - uint16_t m_sequenceNumber; - int m_timeStamp, m_timeStampIncrement; - uint8_t m_payloadType; -}; \ No newline at end of file diff --git a/include/common/TcpAcceptor.h b/include/common/TcpAcceptor.h deleted file mode 100644 index 981f114..0000000 --- a/include/common/TcpAcceptor.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -class TcpAcceptor -{ -public: - TcpAcceptor(boost::asio::io_service &io); - ~TcpAcceptor(); - - // Initialize the acceptor to listen at given local endpoint - uint16_t Initialize(const tcp::endpoint &localEndpoint); - // Listen for incomming connections in a new thread - // and call 'callback' for each connection - void Listen(boost::function)> callback); - - tcp::acceptor &GetSocket() { return m_acceptor; } - -private: - tcp::acceptor m_acceptor; - boost::function)> m_callback; - - void StartListening(); - void ListeningThread(); -}; \ No newline at end of file diff --git a/include/common/TcpRequest.h b/include/common/TcpRequest.h deleted file mode 100644 index d21c375..0000000 --- a/include/common/TcpRequest.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include "TcpHandler.h" -#include "rapidjson/document.h" - -class RequestException : public Exception -{ -public: - RequestException(const std::string &errorString) - :Exception("Error processing request: "+errorString) - {} -}; -class TcpRequest -{ -public: - enum REQUEST_TYPE{ - INAVLID_TYPE = 0, - JOIN_CHAT, CHAT_MESSAGE, - P2P_TCP, - }; - - TcpRequest(); - ~TcpRequest(); - - // Request to join a chat - void JoinChat(TcpHandler &tcpHandler, uint32_t groupId = 0); - // Request to wait for incoming chat message - void ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId = 0); - // Request to establish a P2P TCP connection with a client - // Contains private and public addess-port pairs and client-id - void P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp = "", uint16_t publicPort = 0); - - // Receive a request - void ReceiveRequest(TcpHandler &tcpHandler); - - // Request Data - REQUEST_TYPE GetRequestType(); - uint32_t GetGroupId(); - uint32_t GetMessageSize(); - uint32_t GetClientId(); - std::string GetPrivateIp(); - uint16_t GetPrivatePort(); - std::string GetPublicIp(); - uint16_t GetPublicPort(); -private: - // The Json Document that holds the request - rapidjson::Document m_document; - // Get string from the Json Doucment - std::string GetJsonString(); - // Helper function get a json value from a request - inline rapidjson::Value& GetValue(const std::string &key); -}; diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h deleted file mode 100644 index 0bd2d28..0000000 --- a/include/rapidjson/allocators.h +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_ALLOCATORS_H_ -#define RAPIDJSON_ALLOCATORS_H_ - -#include "rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Allocator - -/*! \class rapidjson::Allocator - \brief Concept for allocating, resizing and freeing memory block. - - Note that Malloc() and Realloc() are non-static but Free() is static. - - So if an allocator need to support Free(), it needs to put its pointer in - the header of memory block. - -\code -concept Allocator { - static const bool kNeedFree; //!< Whether this allocator needs to call Free(). - - // Allocate a memory block. - // \param size of the memory block in bytes. - // \returns pointer to the memory block. - void* Malloc(size_t size); - - // Resize a memory block. - // \param originalPtr The pointer to current memory block. Null pointer is permitted. - // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) - // \param newSize the new size in bytes. - void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); - - // Free a memory block. - // \param pointer to the memory block. Null pointer is permitted. - static void Free(void *ptr); -}; -\endcode -*/ - -/////////////////////////////////////////////////////////////////////////////// -// CrtAllocator - -//! C-runtime library allocator. -/*! This class is just wrapper for standard C library memory routines. - \note implements Allocator concept -*/ -class CrtAllocator { -public: - static const bool kNeedFree = true; - void* Malloc(size_t size) { return std::malloc(size); } - void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return std::realloc(originalPtr, newSize); } - static void Free(void *ptr) { std::free(ptr); } -}; - -/////////////////////////////////////////////////////////////////////////////// -// MemoryPoolAllocator - -//! Default memory allocator used by the parser and DOM. -/*! This allocator allocate memory blocks from pre-allocated memory chunks. - - It does not free memory blocks. And Realloc() only allocate new memory. - - The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. - - User may also supply a buffer as the first chunk. - - If the user-buffer is full then additional chunks are allocated by BaseAllocator. - - The user-buffer is not deallocated by this allocator. - - \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. - \note implements Allocator concept -*/ -template -class MemoryPoolAllocator { -public: - static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) - - //! Constructor with chunkSize. - /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. - \param baseAllocator The allocator for allocating memory chunks. - */ - MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : - chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) - { - } - - //! Constructor with user-supplied buffer. - /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. - - The user buffer will not be deallocated when this allocator is destructed. - - \param buffer User supplied buffer. - \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). - \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. - \param baseAllocator The allocator for allocating memory chunks. - */ - MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : - chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) - { - RAPIDJSON_ASSERT(buffer != 0); - RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); - chunkHead_ = reinterpret_cast(buffer); - chunkHead_->capacity = size - sizeof(ChunkHeader); - chunkHead_->size = 0; - chunkHead_->next = 0; - } - - //! Destructor. - /*! This deallocates all memory chunks, excluding the user-supplied buffer. - */ - ~MemoryPoolAllocator() { - Clear(); - RAPIDJSON_DELETE(ownBaseAllocator_); - } - - //! Deallocates all memory chunks, excluding the user-supplied buffer. - void Clear() { - while(chunkHead_ != 0 && chunkHead_ != userBuffer_) { - ChunkHeader* next = chunkHead_->next; - baseAllocator_->Free(chunkHead_); - chunkHead_ = next; - } - } - - //! Computes the total capacity of allocated memory chunks. - /*! \return total capacity in bytes. - */ - size_t Capacity() const { - size_t capacity = 0; - for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) - capacity += c->capacity; - return capacity; - } - - //! Computes the memory blocks allocated. - /*! \return total used bytes. - */ - size_t Size() const { - size_t size = 0; - for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) - size += c->size; - return size; - } - - //! Allocates a memory block. (concept Allocator) - void* Malloc(size_t size) { - size = RAPIDJSON_ALIGN(size); - if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) - AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); - - void *buffer = reinterpret_cast(chunkHead_ + 1) + chunkHead_->size; - chunkHead_->size += size; - return buffer; - } - - //! Resizes a memory block (concept Allocator) - void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { - if (originalPtr == 0) - return Malloc(newSize); - - // Do not shrink if new size is smaller than original - if (originalSize >= newSize) - return originalPtr; - - // Simply expand it if it is the last allocation and there is sufficient space - if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { - size_t increment = static_cast(newSize - originalSize); - increment = RAPIDJSON_ALIGN(increment); - if (chunkHead_->size + increment <= chunkHead_->capacity) { - chunkHead_->size += increment; - return originalPtr; - } - } - - // Realloc process: allocate and copy memory, do not free original buffer. - void* newBuffer = Malloc(newSize); - RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. - return std::memcpy(newBuffer, originalPtr, originalSize); - } - - //! Frees a memory block (concept Allocator) - static void Free(void *ptr) { (void)ptr; } // Do nothing - -private: - //! Copy constructor is not permitted. - MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; - //! Copy assignment operator is not permitted. - MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; - - //! Creates a new chunk. - /*! \param capacity Capacity of the chunk in bytes. - */ - void AddChunk(size_t capacity) { - if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); - ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); - chunk->capacity = capacity; - chunk->size = 0; - chunk->next = chunkHead_; - chunkHead_ = chunk; - } - - static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. - - //! Chunk header for perpending to each chunk. - /*! Chunks are stored as a singly linked list. - */ - struct ChunkHeader { - size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). - size_t size; //!< Current size of allocated memory in bytes. - ChunkHeader *next; //!< Next chunk in the linked list. - }; - - ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. - size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. - void *userBuffer_; //!< User supplied buffer. - BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. - BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. -}; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h deleted file mode 100644 index 07e3fe3..0000000 --- a/include/rapidjson/document.h +++ /dev/null @@ -1,1927 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_DOCUMENT_H_ -#define RAPIDJSON_DOCUMENT_H_ - -/*! \file document.h */ - -#include "reader.h" -#include "internal/meta.h" -#include "internal/strfunc.h" -#include // placement new - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -#elif defined(__GNUC__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_HAS_STDSTRING - -#ifndef RAPIDJSON_HAS_STDSTRING -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation -#else -#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default -#endif -/*! \def RAPIDJSON_HAS_STDSTRING - \ingroup RAPIDJSON_CONFIG - \brief Enable RapidJSON support for \c std::string - - By defining this preprocessor symbol to \c 1, several convenience functions for using - \ref rapidjson::GenericValue with \c std::string are enabled, especially - for construction and comparison. - - \hideinitializer -*/ -#include -#endif // RAPIDJSON_HAS_STDSTRING - -#ifndef RAPIDJSON_NOMEMBERITERATORCLASS -#include // std::iterator, std::random_access_iterator_tag -#endif - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS -#include // std::move -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -// Forward declaration. -template -class GenericValue; - -//! Name-value pair in a JSON object value. -/*! - This class was internal to GenericValue. It used to be a inner struct. - But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. - https://code.google.com/p/rapidjson/issues/detail?id=64 -*/ -template -struct GenericMember { - GenericValue name; //!< name of member (must be a string) - GenericValue value; //!< value of member. -}; - -/////////////////////////////////////////////////////////////////////////////// -// GenericMemberIterator - -#ifndef RAPIDJSON_NOMEMBERITERATORCLASS - -//! (Constant) member iterator for a JSON object value -/*! - \tparam Const Is this a constant iterator? - \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) - \tparam Allocator Allocator type for allocating memory of object, array and string. - - This class implements a Random Access Iterator for GenericMember elements - of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. - - \note This iterator implementation is mainly intended to avoid implicit - conversions from iterator values to \c NULL, - e.g. from GenericValue::FindMember. - - \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a - pointer-based implementation, if your platform doesn't provide - the C++ header. - - \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator - */ -template -class GenericMemberIterator - : public std::iterator >::Type> { - - friend class GenericValue; - template friend class GenericMemberIterator; - - typedef GenericMember PlainType; - typedef typename internal::MaybeAddConst::Type ValueType; - typedef std::iterator BaseType; - -public: - //! Iterator type itself - typedef GenericMemberIterator Iterator; - //! Constant iterator type - typedef GenericMemberIterator ConstIterator; - //! Non-constant iterator type - typedef GenericMemberIterator NonConstIterator; - - //! Pointer to (const) GenericMember - typedef typename BaseType::pointer Pointer; - //! Reference to (const) GenericMember - typedef typename BaseType::reference Reference; - //! Signed integer type (e.g. \c ptrdiff_t) - typedef typename BaseType::difference_type DifferenceType; - - //! Default constructor (singular value) - /*! Creates an iterator pointing to no element. - \note All operations, except for comparisons, are undefined on such values. - */ - GenericMemberIterator() : ptr_() {} - - //! Iterator conversions to more const - /*! - \param it (Non-const) iterator to copy from - - Allows the creation of an iterator from another GenericMemberIterator - that is "less const". Especially, creating a non-constant iterator - from a constant iterator are disabled: - \li const -> non-const (not ok) - \li const -> const (ok) - \li non-const -> const (ok) - \li non-const -> non-const (ok) - - \note If the \c Const template parameter is already \c false, this - constructor effectively defines a regular copy-constructor. - Otherwise, the copy constructor is implicitly defined. - */ - GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} - - //! @name stepping - //@{ - Iterator& operator++(){ ++ptr_; return *this; } - Iterator& operator--(){ --ptr_; return *this; } - Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } - Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } - //@} - - //! @name increment/decrement - //@{ - Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } - Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } - - Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } - Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } - //@} - - //! @name relations - //@{ - bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } - bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } - bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } - bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } - bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } - bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } - //@} - - //! @name dereference - //@{ - Reference operator*() const { return *ptr_; } - Pointer operator->() const { return ptr_; } - Reference operator[](DifferenceType n) const { return ptr_[n]; } - //@} - - //! Distance - DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } - -private: - //! Internal constructor from plain pointer - explicit GenericMemberIterator(Pointer p) : ptr_(p) {} - - Pointer ptr_; //!< raw pointer -}; - -#else // RAPIDJSON_NOMEMBERITERATORCLASS - -// class-based member iterator implementation disabled, use plain pointers - -template -struct GenericMemberIterator; - -//! non-const GenericMemberIterator -template -struct GenericMemberIterator { - //! use plain pointer as iterator type - typedef GenericMember* Iterator; -}; -//! const GenericMemberIterator -template -struct GenericMemberIterator { - //! use plain const pointer as iterator type - typedef const GenericMember* Iterator; -}; - -#endif // RAPIDJSON_NOMEMBERITERATORCLASS - -/////////////////////////////////////////////////////////////////////////////// -// GenericStringRef - -//! Reference to a constant string (not taking a copy) -/*! - \tparam CharType character type of the string - - This helper class is used to automatically infer constant string - references for string literals, especially from \c const \b (!) - character arrays. - - The main use is for creating JSON string values without copying the - source string via an \ref Allocator. This requires that the referenced - string pointers have a sufficient lifetime, which exceeds the lifetime - of the associated GenericValue. - - \b Example - \code - Value v("foo"); // ok, no need to copy & calculate length - const char foo[] = "foo"; - v.SetString(foo); // ok - - const char* bar = foo; - // Value x(bar); // not ok, can't rely on bar's lifetime - Value x(StringRef(bar)); // lifetime explicitly guaranteed by user - Value y(StringRef(bar, 3)); // ok, explicitly pass length - \endcode - - \see StringRef, GenericValue::SetString -*/ -template -struct GenericStringRef { - typedef CharType Ch; //!< character type of the string - - //! Create string reference from \c const character array - /*! - This constructor implicitly creates a constant string reference from - a \c const character array. It has better performance than - \ref StringRef(const CharType*) by inferring the string \ref length - from the array length, and also supports strings containing null - characters. - - \tparam N length of the string, automatically inferred - - \param str Constant character array, lifetime assumed to be longer - than the use of the string in e.g. a GenericValue - - \post \ref s == str - - \note Constant complexity. - \note There is a hidden, private overload to disallow references to - non-const character arrays to be created via this constructor. - By this, e.g. function-scope arrays used to be filled via - \c snprintf are excluded from consideration. - In such cases, the referenced string should be \b copied to the - GenericValue instead. - */ - template - GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT - : s(str), length(N-1) {} - - //! Explicitly create string reference from \c const character pointer - /*! - This constructor can be used to \b explicitly create a reference to - a constant string pointer. - - \see StringRef(const CharType*) - - \param str Constant character pointer, lifetime assumed to be longer - than the use of the string in e.g. a GenericValue - - \post \ref s == str - - \note There is a hidden, private overload to disallow references to - non-const character arrays to be created via this constructor. - By this, e.g. function-scope arrays used to be filled via - \c snprintf are excluded from consideration. - In such cases, the referenced string should be \b copied to the - GenericValue instead. - */ - explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } - - //! Create constant string reference from pointer and length - /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue - \param len length of the string, excluding the trailing NULL terminator - - \post \ref s == str && \ref length == len - \note Constant complexity. - */ - GenericStringRef(const CharType* str, SizeType len) - : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } - - //! implicit conversion to plain CharType pointer - operator const Ch *() const { return s; } - - const Ch* const s; //!< plain CharType pointer - const SizeType length; //!< length of the string (excluding the trailing NULL terminator) - -private: - //! Disallow copy-assignment - GenericStringRef operator=(const GenericStringRef&); - //! Disallow construction from non-const array - template - GenericStringRef(CharType (&str)[N]) /* = delete */; -}; - -//! Mark a character pointer as constant string -/*! Mark a plain character pointer as a "string literal". This function - can be used to avoid copying a character string to be referenced as a - value in a JSON GenericValue object, if the string's lifetime is known - to be valid long enough. - \tparam CharType Character type of the string - \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue - \return GenericStringRef string reference object - \relatesalso GenericStringRef - - \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember -*/ -template -inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str, internal::StrLen(str)); -} - -//! Mark a character pointer as constant string -/*! Mark a plain character pointer as a "string literal". This function - can be used to avoid copying a character string to be referenced as a - value in a JSON GenericValue object, if the string's lifetime is known - to be valid long enough. - - This version has better performance with supplied length, and also - supports string containing null characters. - - \tparam CharType character type of the string - \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue - \param length The length of source string. - \return GenericStringRef string reference object - \relatesalso GenericStringRef -*/ -template -inline GenericStringRef StringRef(const CharType* str, size_t length) { - return GenericStringRef(str, SizeType(length)); -} - -#if RAPIDJSON_HAS_STDSTRING -//! Mark a string object as constant string -/*! Mark a string object (e.g. \c std::string) as a "string literal". - This function can be used to avoid copying a string to be referenced as a - value in a JSON GenericValue object, if the string's lifetime is known - to be valid long enough. - - \tparam CharType character type of the string - \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue - \return GenericStringRef string reference object - \relatesalso GenericStringRef - \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. -*/ -template -inline GenericStringRef StringRef(const std::basic_string& str) { - return GenericStringRef(str.data(), SizeType(str.size())); -} -#endif - -/////////////////////////////////////////////////////////////////////////////// -// GenericValue type traits -namespace internal { - -template -struct IsGenericValueImpl : FalseType {}; - -// select candidates according to nested encoding and allocator types -template struct IsGenericValueImpl::Type, typename Void::Type> - : IsBaseOf, T>::Type {}; - -// helper to match arbitrary GenericValue instantiations, including derived classes -template struct IsGenericValue : IsGenericValueImpl::Type {}; - -} // namespace internal - -/////////////////////////////////////////////////////////////////////////////// -// GenericValue - -//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. -/*! - A JSON value can be one of 7 types. This class is a variant type supporting - these types. - - Use the Value if UTF8 and default allocator - - \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) - \tparam Allocator Allocator type for allocating memory of object, array and string. -*/ -template > -class GenericValue { -public: - //! Name-value pair in an object. - typedef GenericMember Member; - typedef Encoding EncodingType; //!< Encoding type from template parameter. - typedef Allocator AllocatorType; //!< Allocator type from template parameter. - typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. - typedef GenericStringRef StringRefType; //!< Reference to a constant string - typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. - typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. - typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. - typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. - - //!@name Constructors and destructor. - //@{ - - //! Default constructor creates a null value. - GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move constructor in C++11 - GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { - rhs.flags_ = kNullFlag; // give up contents - } -#endif - -private: - //! Copy constructor is not permitted. - GenericValue(const GenericValue& rhs); - -public: - - //! Constructor with JSON value type. - /*! This creates a Value of specified type with default content. - \param type Type of the value. - \note Default content for number is zero. - */ - explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { - static const unsigned defaultFlags[7] = { - kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kConstStringFlag, - kNumberAnyFlag - }; - RAPIDJSON_ASSERT(type <= kNumberType); - flags_ = defaultFlags[type]; - } - - //! Explicit copy constructor (with allocator) - /*! Creates a copy of a Value by using the given Allocator - \tparam SourceAllocator allocator of \c rhs - \param rhs Value to copy from (read-only) - \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). - \see CopyFrom() - */ - template< typename SourceAllocator > - GenericValue(const GenericValue& rhs, Allocator & allocator); - - //! Constructor for boolean value. - /*! \param b Boolean value - \note This constructor is limited to \em real boolean values and rejects - implicitly converted types like arbitrary pointers. Use an explicit cast - to \c bool, if you want to construct a boolean JSON value in such cases. - */ -#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen - template - explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT -#else - explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT -#endif - : data_(), flags_(b ? kTrueFlag : kFalseFlag) { - // safe-guard against failing SFINAE - RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); - } - - //! Constructor for int value. - explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { - data_.n.i64 = i; - if (i >= 0) - flags_ |= kUintFlag | kUint64Flag; - } - - //! Constructor for unsigned value. - explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { - data_.n.u64 = u; - if (!(u & 0x80000000)) - flags_ |= kIntFlag | kInt64Flag; - } - - //! Constructor for int64_t value. - explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { - data_.n.i64 = i64; - if (i64 >= 0) { - flags_ |= kNumberUint64Flag; - if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - flags_ |= kUintFlag; - if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; - } - else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; - } - - //! Constructor for uint64_t value. - explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { - data_.n.u64 = u64; - if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) - flags_ |= kInt64Flag; - if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - flags_ |= kUintFlag; - if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; - } - - //! Constructor for double value. - explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } - - //! Constructor for constant string (i.e. do not make a copy of string) - GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } - - //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } - - //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } - - //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } - -#if RAPIDJSON_HAS_STDSTRING - //! Constructor for copy-string from a string object (i.e. do make a copy of string) - /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. - */ - GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } -#endif - - //! Destructor. - /*! Need to destruct elements of array, members of object, or copy-string. - */ - ~GenericValue() { - if (Allocator::kNeedFree) { // Shortcut by Allocator's trait - switch(flags_) { - case kArrayFlag: - for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) - v->~GenericValue(); - Allocator::Free(data_.a.elements); - break; - - case kObjectFlag: - for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) - m->~Member(); - Allocator::Free(data_.o.members); - break; - - case kCopyStringFlag: - Allocator::Free(const_cast(data_.s.str)); - break; - - default: - break; // Do nothing for other types. - } - } - } - - //@} - - //!@name Assignment operators - //@{ - - //! Assignment with move semantics. - /*! \param rhs Source of the assignment. It will become a null value after assignment. - */ - GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { - RAPIDJSON_ASSERT(this != &rhs); - this->~GenericValue(); - RawAssign(rhs); - return *this; - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move assignment in C++11 - GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { - return *this = rhs.Move(); - } -#endif - - //! Assignment of constant string reference (no copy) - /*! \param str Constant string reference to be assigned - \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. - \see GenericStringRef, operator=(T) - */ - GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { - GenericValue s(str); - return *this = s; - } - - //! Assignment with primitive types. - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t - \param value The value to be assigned. - - \note The source type \c T explicitly disallows all pointer types, - especially (\c const) \ref Ch*. This helps avoiding implicitly - referencing character strings with insufficient lifetime, use - \ref SetString(const Ch*, Allocator&) (for copying) or - \ref StringRef() (to explicitly mark the pointer as constant) instead. - All other pointer types would implicitly convert to \c bool, - use \ref SetBool() instead. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) - operator=(T value) { - GenericValue v(value); - return *this = v; - } - - //! Deep-copy assignment from Value - /*! Assigns a \b copy of the Value to the current Value object - \tparam SourceAllocator Allocator type of \c rhs - \param rhs Value to copy from (read-only) - \param allocator Allocator to use for copying - */ - template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { - RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); - this->~GenericValue(); - new (this) GenericValue(rhs, allocator); - return *this; - } - - //! Exchange the contents of this value with those of other. - /*! - \param other Another value. - \note Constant complexity. - */ - GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { - GenericValue temp; - temp.RawAssign(*this); - RawAssign(other); - other.RawAssign(temp); - return *this; - } - - //! Prepare Value for move semantics - /*! \return *this */ - GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } - //@} - - //!@name Equal-to and not-equal-to operators - //@{ - //! Equal-to operator - /*! - \note If an object contains duplicated named member, comparing equality with any object is always \c false. - \note Linear time complexity (number of all values in the subtree and total lengths of all strings). - */ - template - bool operator==(const GenericValue& rhs) const { - typedef GenericValue RhsType; - if (GetType() != rhs.GetType()) - return false; - - switch (GetType()) { - case kObjectType: // Warning: O(n^2) inner-loop - if (data_.o.size != rhs.data_.o.size) - return false; - for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { - typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); - if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) - return false; - } - return true; - - case kArrayType: - if (data_.a.size != rhs.data_.a.size) - return false; - for (SizeType i = 0; i < data_.a.size; i++) - if ((*this)[i] != rhs[i]) - return false; - return true; - - case kStringType: - return StringEqual(rhs); - - case kNumberType: - if (IsDouble() || rhs.IsDouble()) - return GetDouble() == rhs.GetDouble(); // May convert one operand from integer to double. - else - return data_.n.u64 == rhs.data_.n.u64; - - default: // kTrueType, kFalseType, kNullType - return true; - } - } - - //! Equal-to operator with const C-string pointer - bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } - -#if RAPIDJSON_HAS_STDSTRING - //! Equal-to operator with string object - /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. - */ - bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } -#endif - - //! Equal-to operator with primitive types - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false - */ - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } - - //! Not-equal-to operator - /*! \return !(*this == rhs) - */ - template - bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } - - //! Not-equal-to operator with const C-string pointer - bool operator!=(const Ch* rhs) const { return !(*this == rhs); } - - //! Not-equal-to operator with arbitrary types - /*! \return !(*this == rhs) - */ - template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } - - //! Equal-to operator with arbitrary types (symmetric version) - /*! \return (rhs == lhs) - */ - template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } - - //! Not-Equal-to operator with arbitrary types (symmetric version) - /*! \return !(rhs == lhs) - */ - template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } - //@} - - //!@name Type - //@{ - - Type GetType() const { return static_cast(flags_ & kTypeMask); } - bool IsNull() const { return flags_ == kNullFlag; } - bool IsFalse() const { return flags_ == kFalseFlag; } - bool IsTrue() const { return flags_ == kTrueFlag; } - bool IsBool() const { return (flags_ & kBoolFlag) != 0; } - bool IsObject() const { return flags_ == kObjectFlag; } - bool IsArray() const { return flags_ == kArrayFlag; } - bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } - bool IsInt() const { return (flags_ & kIntFlag) != 0; } - bool IsUint() const { return (flags_ & kUintFlag) != 0; } - bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } - bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } - bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } - bool IsString() const { return (flags_ & kStringFlag) != 0; } - - //@} - - //!@name Null - //@{ - - GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } - - //@} - - //!@name Bool - //@{ - - bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } - //!< Set boolean value - /*! \post IsBool() == true */ - GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } - - //@} - - //!@name Object - //@{ - - //! Set this value as an empty object. - /*! \post IsObject() == true */ - GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } - - //! Get the number of members in the object. - SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } - - //! Check whether the object is empty. - bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } - - //! Get a value from an object associated with the name. - /*! \pre IsObject() == true - \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) - \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. - Since 0.2, if the name is not correct, it will assert. - If user is unsure whether a member exists, user should use HasMember() first. - A better approach is to use FindMember(). - \note Linear time complexity. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { - GenericValue n(StringRef(name)); - return (*this)[n]; - } - template - RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } - - //! Get a value from an object associated with the name. - /*! \pre IsObject() == true - \tparam SourceAllocator Allocator of the \c name value - - \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). - And it can also handle strings with embedded null characters. - - \note Linear time complexity. - */ - template - GenericValue& operator[](const GenericValue& name) { - MemberIterator member = FindMember(name); - if (member != MemberEnd()) - return member->value; - else { - RAPIDJSON_ASSERT(false); // see above note - static GenericValue NullValue; - return NullValue; - } - } - template - const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } - - //! Const member iterator - /*! \pre IsObject() == true */ - ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } - //! Const \em past-the-end member iterator - /*! \pre IsObject() == true */ - ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } - //! Member iterator - /*! \pre IsObject() == true */ - MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } - //! \em Past-the-end member iterator - /*! \pre IsObject() == true */ - MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } - - //! Check whether a member exists in the object. - /*! - \param name Member name to be searched. - \pre IsObject() == true - \return Whether a member with that name exists. - \note It is better to use FindMember() directly if you need the obtain the value as well. - \note Linear time complexity. - */ - bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } - - //! Check whether a member exists in the object with GenericValue name. - /*! - This version is faster because it does not need a StrLen(). It can also handle string with null character. - \param name Member name to be searched. - \pre IsObject() == true - \return Whether a member with that name exists. - \note It is better to use FindMember() directly if you need the obtain the value as well. - \note Linear time complexity. - */ - template - bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } - - //! Find member by name. - /*! - \param name Member name to be searched. - \pre IsObject() == true - \return Iterator to member, if it exists. - Otherwise returns \ref MemberEnd(). - - \note Earlier versions of Rapidjson returned a \c NULL pointer, in case - the requested member doesn't exist. For consistency with e.g. - \c std::map, this has been changed to MemberEnd() now. - \note Linear time complexity. - */ - MemberIterator FindMember(const Ch* name) { - GenericValue n(StringRef(name)); - return FindMember(n); - } - - ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } - - //! Find member by name. - /*! - This version is faster because it does not need a StrLen(). It can also handle string with null character. - \param name Member name to be searched. - \pre IsObject() == true - \return Iterator to member, if it exists. - Otherwise returns \ref MemberEnd(). - - \note Earlier versions of Rapidjson returned a \c NULL pointer, in case - the requested member doesn't exist. For consistency with e.g. - \c std::map, this has been changed to MemberEnd() now. - \note Linear time complexity. - */ - template - MemberIterator FindMember(const GenericValue& name) { - RAPIDJSON_ASSERT(IsObject()); - RAPIDJSON_ASSERT(name.IsString()); - MemberIterator member = MemberBegin(); - for ( ; member != MemberEnd(); ++member) - if (name.StringEqual(member->name)) - break; - return member; - } - template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } - - //! Add a member (name-value pair) to the object. - /*! \param name A string value as name of member. - \param value Value of any type. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \note The ownership of \c name and \c value will be transferred to this object on success. - \pre IsObject() && name.IsString() - \post name.IsNull() && value.IsNull() - \note Amortized Constant time complexity. - */ - GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { - RAPIDJSON_ASSERT(IsObject()); - RAPIDJSON_ASSERT(name.IsString()); - - Object& o = data_.o; - if (o.size >= o.capacity) { - if (o.capacity == 0) { - o.capacity = kDefaultObjectCapacity; - o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); - } - else { - SizeType oldCapacity = o.capacity; - o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 - o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); - } - } - o.members[o.size].name.RawAssign(name); - o.members[o.size].value.RawAssign(value); - o.size++; - return *this; - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { - return AddMember(name, value, allocator); - } - GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { - return AddMember(name, value, allocator); - } - GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { - return AddMember(name, value, allocator); - } - GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { - GenericValue n(name); - return AddMember(n, value, allocator); - } -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - - - //! Add a member (name-value pair) to the object. - /*! \param name A constant string reference as name of member. - \param value Value of any type. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \note The ownership of \c value will be transferred to this object on success. - \pre IsObject() - \post value.IsNull() - \note Amortized Constant time complexity. - */ - GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { - GenericValue n(name); - return AddMember(n, value, allocator); - } - - //! Add a constant string value as member (name-value pair) to the object. - /*! \param name A constant string reference as name of member. - \param value constant string reference as value of member. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \pre IsObject() - \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. - \note Amortized Constant time complexity. - */ - GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { - GenericValue v(value); - return AddMember(name, v, allocator); - } - - //! Add any primitive value as member (name-value pair) to the object. - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t - \param name A constant string reference as name of member. - \param value Value of primitive type \c T as value of member - \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \pre IsObject() - - \note The source type \c T explicitly disallows all pointer types, - especially (\c const) \ref Ch*. This helps avoiding implicitly - referencing character strings with insufficient lifetime, use - \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref - AddMember(StringRefType, StringRefType, Allocator&). - All other pointer types would implicitly convert to \c bool, - use an explicit cast instead, if needed. - \note Amortized Constant time complexity. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) - AddMember(StringRefType name, T value, Allocator& allocator) { - GenericValue n(name); - GenericValue v(value); - return AddMember(n, v, allocator); - } - - //! Remove all members in the object. - /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. - \note Linear time complexity. - */ - void RemoveAllMembers() { - RAPIDJSON_ASSERT(IsObject()); - for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) - m->~Member(); - data_.o.size = 0; - } - - //! Remove a member in object by its name. - /*! \param name Name of member to be removed. - \return Whether the member existed. - \note This function may reorder the object members. Use \ref - EraseMember(ConstMemberIterator) if you need to preserve the - relative order of the remaining members. - \note Linear time complexity. - */ - bool RemoveMember(const Ch* name) { - GenericValue n(StringRef(name)); - return RemoveMember(n); - } - - template - bool RemoveMember(const GenericValue& name) { - MemberIterator m = FindMember(name); - if (m != MemberEnd()) { - RemoveMember(m); - return true; - } - else - return false; - } - - //! Remove a member in object by iterator. - /*! \param m member iterator (obtained by FindMember() or MemberBegin()). - \return the new iterator after removal. - \note This function may reorder the object members. Use \ref - EraseMember(ConstMemberIterator) if you need to preserve the - relative order of the remaining members. - \note Constant time complexity. - */ - MemberIterator RemoveMember(MemberIterator m) { - RAPIDJSON_ASSERT(IsObject()); - RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(data_.o.members != 0); - RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); - - MemberIterator last(data_.o.members + (data_.o.size - 1)); - if (data_.o.size > 1 && m != last) { - // Move the last one to this place - *m = *last; - } - else { - // Only one left, just destroy - m->~Member(); - } - --data_.o.size; - return m; - } - - //! Remove a member from an object by iterator. - /*! \param pos iterator to the member to remove - \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() - \return Iterator following the removed element. - If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. - \note This function preserves the relative order of the remaining object - members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). - \note Linear time complexity. - */ - MemberIterator EraseMember(ConstMemberIterator pos) { - return EraseMember(pos, pos +1); - } - - //! Remove members in the range [first, last) from an object. - /*! \param first iterator to the first member to remove - \param last iterator following the last member to remove - \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() - \return Iterator following the last removed element. - \note This function preserves the relative order of the remaining object - members. - \note Linear time complexity. - */ - MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { - RAPIDJSON_ASSERT(IsObject()); - RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(data_.o.members != 0); - RAPIDJSON_ASSERT(first >= MemberBegin()); - RAPIDJSON_ASSERT(first <= last); - RAPIDJSON_ASSERT(last <= MemberEnd()); - - MemberIterator pos = MemberBegin() + (first - MemberBegin()); - for (MemberIterator itr = pos; itr != last; ++itr) - itr->~Member(); - std::memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member)); - data_.o.size -= (last - first); - return pos; - } - - //@} - - //!@name Array - //@{ - - //! Set this value as an empty array. - /*! \post IsArray == true */ - GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } - - //! Get the number of elements in array. - SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } - - //! Get the capacity of array. - SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } - - //! Check whether the array is empty. - bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } - - //! Remove all elements in the array. - /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. - \note Linear time complexity. - */ - void Clear() { - RAPIDJSON_ASSERT(IsArray()); - for (SizeType i = 0; i < data_.a.size; ++i) - data_.a.elements[i].~GenericValue(); - data_.a.size = 0; - } - - //! Get an element from array by index. - /*! \pre IsArray() == true - \param index Zero-based index of element. - \see operator[](T*) - */ - GenericValue& operator[](SizeType index) { - RAPIDJSON_ASSERT(IsArray()); - RAPIDJSON_ASSERT(index < data_.a.size); - return data_.a.elements[index]; - } - const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } - - //! Element iterator - /*! \pre IsArray() == true */ - ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } - //! \em Past-the-end element iterator - /*! \pre IsArray() == true */ - ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } - //! Constant element iterator - /*! \pre IsArray() == true */ - ConstValueIterator Begin() const { return const_cast(*this).Begin(); } - //! Constant \em past-the-end element iterator - /*! \pre IsArray() == true */ - ConstValueIterator End() const { return const_cast(*this).End(); } - - //! Request the array to have enough capacity to store elements. - /*! \param newCapacity The capacity that the array at least need to have. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \note Linear time complexity. - */ - GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { - RAPIDJSON_ASSERT(IsArray()); - if (newCapacity > data_.a.capacity) { - data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)); - data_.a.capacity = newCapacity; - } - return *this; - } - - //! Append a GenericValue at the end of the array. - /*! \param value Value to be appended. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \pre IsArray() == true - \post value.IsNull() == true - \return The value itself for fluent API. - \note The ownership of \c value will be transferred to this array on success. - \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. - \note Amortized constant time complexity. - */ - GenericValue& PushBack(GenericValue& value, Allocator& allocator) { - RAPIDJSON_ASSERT(IsArray()); - if (data_.a.size >= data_.a.capacity) - Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); - data_.a.elements[data_.a.size++].RawAssign(value); - return *this; - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { - return PushBack(value, allocator); - } -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - - //! Append a constant string reference at the end of the array. - /*! \param value Constant string reference to be appended. - \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). - \pre IsArray() == true - \return The value itself for fluent API. - \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. - \note Amortized constant time complexity. - \see GenericStringRef - */ - GenericValue& PushBack(StringRefType value, Allocator& allocator) { - return (*this).template PushBack(value, allocator); - } - - //! Append a primitive value at the end of the array. - /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t - \param value Value of primitive type T to be appended. - \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). - \pre IsArray() == true - \return The value itself for fluent API. - \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. - - \note The source type \c T explicitly disallows all pointer types, - especially (\c const) \ref Ch*. This helps avoiding implicitly - referencing character strings with insufficient lifetime, use - \ref PushBack(GenericValue&, Allocator&) or \ref - PushBack(StringRefType, Allocator&). - All other pointer types would implicitly convert to \c bool, - use an explicit cast instead, if needed. - \note Amortized constant time complexity. - */ - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) - PushBack(T value, Allocator& allocator) { - GenericValue v(value); - return PushBack(v, allocator); - } - - //! Remove the last element in the array. - /*! - \note Constant time complexity. - */ - GenericValue& PopBack() { - RAPIDJSON_ASSERT(IsArray()); - RAPIDJSON_ASSERT(!Empty()); - data_.a.elements[--data_.a.size].~GenericValue(); - return *this; - } - - //! Remove an element of array by iterator. - /*! - \param pos iterator to the element to remove - \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() - \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. - \note Linear time complexity. - */ - ValueIterator Erase(ConstValueIterator pos) { - return Erase(pos, pos + 1); - } - - //! Remove elements in the range [first, last) of the array. - /*! - \param first iterator to the first element to remove - \param last iterator following the last element to remove - \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() - \return Iterator following the last removed element. - \note Linear time complexity. - */ - ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { - RAPIDJSON_ASSERT(IsArray()); - RAPIDJSON_ASSERT(data_.a.size > 0); - RAPIDJSON_ASSERT(data_.a.elements != 0); - RAPIDJSON_ASSERT(first >= Begin()); - RAPIDJSON_ASSERT(first <= last); - RAPIDJSON_ASSERT(last <= End()); - ValueIterator pos = Begin() + (first - Begin()); - for (ValueIterator itr = pos; itr != last; ++itr) - itr->~GenericValue(); - std::memmove(pos, last, (End() - last) * sizeof(GenericValue)); - data_.a.size -= (last - first); - return pos; - } - - //@} - - //!@name Number - //@{ - - int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } - unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } - int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } - uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } - - double GetDouble() const { - RAPIDJSON_ASSERT(IsNumber()); - if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. - if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double - if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double - if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision) - RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision) - } - - GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } - GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } - GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } - GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } - GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } - - //@} - - //!@name String - //@{ - - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } - - //! Get the length of string. - /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). - */ - SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } - - //! Set this value as a string without copying source string. - /*! This version has better performance with supplied length, and also support string containing null character. - \param s source string pointer. - \param length The length of source string, excluding the trailing null terminator. - \return The value itself for fluent API. - \post IsString() == true && GetString() == s && GetStringLength() == length - \see SetString(StringRefType) - */ - GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } - - //! Set this value as a string without copying source string. - /*! \param s source string reference - \return The value itself for fluent API. - \post IsString() == true && GetString() == s && GetStringLength() == s.length - */ - GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } - - //! Set this value as a string by copying from source string. - /*! This version has better performance with supplied length, and also support string containing null character. - \param s source string. - \param length The length of source string, excluding the trailing null terminator. - \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length - */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } - - //! Set this value as a string by copying from source string. - /*! \param s source string. - \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length - */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } - -#if RAPIDJSON_HAS_STDSTRING - //! Set this value as a string by copying from source string. - /*! \param s source string. - \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). - \return The value itself for fluent API. - \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() - \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. - */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), s.size(), allocator); } -#endif - - //@} - - //! Generate events of this value to a Handler. - /*! This function adopts the GoF visitor pattern. - Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. - It can also be used to deep clone this value via GenericDocument, which is also a Handler. - \tparam Handler type of handler. - \param handler An object implementing concept Handler. - */ - template - bool Accept(Handler& handler) const { - switch(GetType()) { - case kNullType: return handler.Null(); - case kFalseType: return handler.Bool(false); - case kTrueType: return handler.Bool(true); - - case kObjectType: - if (!handler.StartObject()) - return false; - for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { - if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) - return false; - if (!m->value.Accept(handler)) - return false; - } - return handler.EndObject(data_.o.size); - - case kArrayType: - if (!handler.StartArray()) - return false; - for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) - if (!v->Accept(handler)) - return false; - return handler.EndArray(data_.a.size); - - case kStringType: - return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); - - case kNumberType: - if (IsInt()) return handler.Int(data_.n.i.i); - else if (IsUint()) return handler.Uint(data_.n.u.u); - else if (IsInt64()) return handler.Int64(data_.n.i64); - else if (IsUint64()) return handler.Uint64(data_.n.u64); - else return handler.Double(data_.n.d); - - default: - RAPIDJSON_ASSERT(false); - } - return false; - } - -private: - template friend class GenericValue; - template friend class GenericDocument; - - enum { - kBoolFlag = 0x100, - kNumberFlag = 0x200, - kIntFlag = 0x400, - kUintFlag = 0x800, - kInt64Flag = 0x1000, - kUint64Flag = 0x2000, - kDoubleFlag = 0x4000, - kStringFlag = 0x100000, - kCopyFlag = 0x200000, - kInlineStrFlag = 0x400000, - - // Initial flags of different types. - kNullFlag = kNullType, - kTrueFlag = kTrueType | kBoolFlag, - kFalseFlag = kFalseType | kBoolFlag, - kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, - kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, - kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, - kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, - kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, - kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, - kConstStringFlag = kStringType | kStringFlag, - kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, - kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, - kObjectFlag = kObjectType, - kArrayFlag = kArrayType, - - kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler - }; - - static const SizeType kDefaultArrayCapacity = 16; - static const SizeType kDefaultObjectCapacity = 16; - - struct String { - const Ch* str; - SizeType length; - unsigned hashcode; //!< reserved - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars - // (excluding the terminating zero) and store a value to determine the length of the contained - // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string - // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as - // the string terminator as well. For getting the string length back from that value just use - // "MaxSize - str[LenPos]". - // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode - // inline (for `UTF8`-encoded strings). - struct ShortString { - enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; - Ch str[MaxChars]; - - inline static bool Usable(SizeType len) { return (MaxSize >= len); } - inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } - inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } - }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - // By using proper binary layout, retrieval of different integer types do not need conversions. - union Number { -#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN - struct I { - int i; - char padding[4]; - }i; - struct U { - unsigned u; - char padding2[4]; - }u; -#else - struct I { - char padding[4]; - int i; - }i; - struct U { - char padding2[4]; - unsigned u; - }u; -#endif - int64_t i64; - uint64_t u64; - double d; - }; // 8 bytes - - struct Object { - Member* members; - SizeType size; - SizeType capacity; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - struct Array { - GenericValue* elements; - SizeType size; - SizeType capacity; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - union Data { - String s; - ShortString ss; - Number n; - Object o; - Array a; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - - // Initialize this value as array with initial data, without calling destructor. - void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { - flags_ = kArrayFlag; - data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); - std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); - data_.a.size = data_.a.capacity = count; - } - - //! Initialize this value as object with initial data, without calling destructor. - void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { - flags_ = kObjectFlag; - data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); - std::memcpy(data_.o.members, members, count * sizeof(Member)); - data_.o.size = data_.o.capacity = count; - } - - //! Initialize this value as constant string, without calling destructor. - void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { - flags_ = kConstStringFlag; - data_.s.str = s; - data_.s.length = s.length; - } - - //! Initialize this value as copy string with initial data, without calling destructor. - void SetStringRaw(StringRefType s, Allocator& allocator) { - Ch* str = NULL; - if(ShortString::Usable(s.length)) { - flags_ = kShortStringFlag; - data_.ss.SetLength(s.length); - str = data_.ss.str; - } else { - flags_ = kCopyStringFlag; - data_.s.length = s.length; - str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); - data_.s.str = str; - } - std::memcpy(str, s, s.length * sizeof(Ch)); - str[s.length] = '\0'; - } - - //! Assignment without calling destructor - void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { - data_ = rhs.data_; - flags_ = rhs.flags_; - rhs.flags_ = kNullFlag; - } - - template - bool StringEqual(const GenericValue& rhs) const { - RAPIDJSON_ASSERT(IsString()); - RAPIDJSON_ASSERT(rhs.IsString()); - - const SizeType len1 = GetStringLength(); - const SizeType len2 = rhs.GetStringLength(); - if(len1 != len2) { return false; } - - const Ch* const str1 = GetString(); - const Ch* const str2 = rhs.GetString(); - if(str1 == str2) { return true; } // fast path for constant string - - return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); - } - - Data data_; - unsigned flags_; -}; - -//! GenericValue with UTF8 encoding -typedef GenericValue > Value; - -/////////////////////////////////////////////////////////////////////////////// -// GenericDocument - -//! A document for parsing JSON text as DOM. -/*! - \note implements Handler concept - \tparam Encoding Encoding for both parsing and string storage. - \tparam Allocator Allocator for allocating memory for the DOM - \tparam StackAllocator Allocator for allocating memory for stack during parsing. - \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. -*/ -template , typename StackAllocator = CrtAllocator> -class GenericDocument : public GenericValue { -public: - typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. - typedef GenericValue ValueType; //!< Value type of the document. - typedef Allocator AllocatorType; //!< Allocator type from template parameter. - - //! Constructor - /*! \param allocator Optional allocator for allocating memory. - \param stackCapacity Optional initial capacity of stack in bytes. - \param stackAllocator Optional allocator for allocating memory for stack. - */ - GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : - allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() - { - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move constructor in C++11 - GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT - : ValueType(std::move(rhs)), - allocator_(rhs.allocator_), - ownAllocator_(rhs.ownAllocator_), - stack_(std::move(rhs.stack_)), - parseResult_(rhs.parseResult_) - { - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.parseResult_ = ParseResult(); - } -#endif - - ~GenericDocument() { - Destroy(); - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - //! Move assignment in C++11 - GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT - { - // The cast to ValueType is necessary here, because otherwise it would - // attempt to call GenericValue's templated assignment operator. - ValueType::operator=(std::forward(rhs)); - - // Calling the destructor here would prematurely call stack_'s destructor - Destroy(); - - allocator_ = rhs.allocator_; - ownAllocator_ = rhs.ownAllocator_; - stack_ = std::move(rhs.stack_); - parseResult_ = rhs.parseResult_; - - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.parseResult_ = ParseResult(); - - return *this; - } -#endif - - //!@name Parse from stream - //!@{ - - //! Parse JSON text from an input stream (with Encoding conversion) - /*! \tparam parseFlags Combination of \ref ParseFlag. - \tparam SourceEncoding Encoding of input stream - \tparam InputStream Type of input stream, implementing Stream concept - \param is Input stream to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseStream(InputStream& is) { - ValueType::SetNull(); // Remove existing root if exist - GenericReader reader(&GetAllocator()); - ClearStackOnExit scope(*this); - parseResult_ = reader.template Parse(is, *this); - if (parseResult_) { - RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object - this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. - } - return *this; - } - - //! Parse JSON text from an input stream - /*! \tparam parseFlags Combination of \ref ParseFlag. - \tparam InputStream Type of input stream, implementing Stream concept - \param is Input stream to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseStream(InputStream& is) { - return ParseStream(is); - } - - //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) - /*! \tparam InputStream Type of input stream, implementing Stream concept - \param is Input stream to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseStream(InputStream& is) { - return ParseStream(is); - } - //!@} - - //!@name Parse in-place from mutable string - //!@{ - - //! Parse JSON text from a mutable string (with Encoding conversion) - /*! \tparam parseFlags Combination of \ref ParseFlag. - \tparam SourceEncoding Transcoding from input Encoding - \param str Mutable zero-terminated string to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseInsitu(Ch* str) { - GenericInsituStringStream s(str); - return ParseStream(s); - } - - //! Parse JSON text from a mutable string - /*! \tparam parseFlags Combination of \ref ParseFlag. - \param str Mutable zero-terminated string to be parsed. - \return The document itself for fluent API. - */ - template - GenericDocument& ParseInsitu(Ch* str) { - return ParseInsitu(str); - } - - //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) - /*! \param str Mutable zero-terminated string to be parsed. - \return The document itself for fluent API. - */ - GenericDocument& ParseInsitu(Ch* str) { - return ParseInsitu(str); - } - //!@} - - //!@name Parse from read-only string - //!@{ - - //! Parse JSON text from a read-only string (with Encoding conversion) - /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). - \tparam SourceEncoding Transcoding from input Encoding - \param str Read-only zero-terminated string to be parsed. - */ - template - GenericDocument& Parse(const Ch* str) { - RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); - GenericStringStream s(str); - return ParseStream(s); - } - - //! Parse JSON text from a read-only string - /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). - \param str Read-only zero-terminated string to be parsed. - */ - template - GenericDocument& Parse(const Ch* str) { - return Parse(str); - } - - //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) - /*! \param str Read-only zero-terminated string to be parsed. - */ - GenericDocument& Parse(const Ch* str) { - return Parse(str); - } - //!@} - - //!@name Handling parse errors - //!@{ - - //! Whether a parse error has occured in the last parsing. - bool HasParseError() const { return parseResult_.IsError(); } - - //! Get the \ref ParseErrorCode of last parsing. - ParseErrorCode GetParseError() const { return parseResult_.Code(); } - - //! Get the position of last parsing error in input, 0 otherwise. - size_t GetErrorOffset() const { return parseResult_.Offset(); } - - //!@} - - //! Get the allocator of this document. - Allocator& GetAllocator() { return *allocator_; } - - //! Get the capacity of stack in bytes. - size_t GetStackCapacity() const { return stack_.GetCapacity(); } - -private: - // clear stack on any exit from ParseStream, e.g. due to exception - struct ClearStackOnExit { - explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} - ~ClearStackOnExit() { d_.ClearStack(); } - private: - ClearStackOnExit(const ClearStackOnExit&); - ClearStackOnExit& operator=(const ClearStackOnExit&); - GenericDocument& d_; - }; - - // callers of the following private Handler functions - template friend class GenericReader; // for parsing - template friend class GenericValue; // for deep copying - - // Implementation of Handler - bool Null() { new (stack_.template Push()) ValueType(); return true; } - bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } - bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } - bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } - bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } - bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } - bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } - - bool String(const Ch* str, SizeType length, bool copy) { - if (copy) - new (stack_.template Push()) ValueType(str, length, GetAllocator()); - else - new (stack_.template Push()) ValueType(str, length); - return true; - } - - bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } - - bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } - - bool EndObject(SizeType memberCount) { - typename ValueType::Member* members = stack_.template Pop(memberCount); - stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); - return true; - } - - bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } - - bool EndArray(SizeType elementCount) { - ValueType* elements = stack_.template Pop(elementCount); - stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); - return true; - } - -private: - //! Prohibit copying - GenericDocument(const GenericDocument&); - //! Prohibit assignment - GenericDocument& operator=(const GenericDocument&); - - void ClearStack() { - if (Allocator::kNeedFree) - while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) - (stack_.template Pop(1))->~ValueType(); - else - stack_.Clear(); - stack_.ShrinkToFit(); - } - - void Destroy() { - RAPIDJSON_DELETE(ownAllocator_); - } - - static const size_t kDefaultStackCapacity = 1024; - Allocator* allocator_; - Allocator* ownAllocator_; - internal::Stack stack_; - ParseResult parseResult_; -}; - -//! GenericDocument with UTF8 encoding -typedef GenericDocument > Document; - -// defined here due to the dependency on GenericDocument -template -template -inline -GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) -{ - switch (rhs.GetType()) { - case kObjectType: - case kArrayType: { // perform deep copy via SAX Handler - GenericDocument d(&allocator); - rhs.Accept(d); - RawAssign(*d.stack_.template Pop(1)); - } - break; - case kStringType: - if (rhs.flags_ == kConstStringFlag) { - flags_ = rhs.flags_; - data_ = *reinterpret_cast(&rhs.data_); - } else { - SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); - } - break; - default: // kNumberType, kTrueType, kFalseType, kNullType - flags_ = rhs.flags_; - data_ = *reinterpret_cast(&rhs.data_); - } -} - -RAPIDJSON_NAMESPACE_END - -#if defined(_MSC_VER) || defined(__GNUC__) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h deleted file mode 100644 index ee8caa0..0000000 --- a/include/rapidjson/encodedstream.h +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_ENCODEDSTREAM_H_ -#define RAPIDJSON_ENCODEDSTREAM_H_ - -#include "rapidjson.h" - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Input byte stream wrapper with a statically bound encoding. -/*! - \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. - \tparam InputByteStream Type of input byte stream. For example, FileReadStream. -*/ -template -class EncodedInputStream { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); -public: - typedef typename Encoding::Ch Ch; - - EncodedInputStream(InputByteStream& is) : is_(is) { - current_ = Encoding::TakeBOM(is_); - } - - Ch Peek() const { return current_; } - Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } - size_t Tell() const { return is_.Tell(); } - - // Not implemented - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - EncodedInputStream(const EncodedInputStream&); - EncodedInputStream& operator=(const EncodedInputStream&); - - InputByteStream& is_; - Ch current_; -}; - -//! Output byte stream wrapper with statically bound encoding. -/*! - \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. - \tparam InputByteStream Type of input byte stream. For example, FileWriteStream. -*/ -template -class EncodedOutputStream { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); -public: - typedef typename Encoding::Ch Ch; - - EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { - if (putBOM) - Encoding::PutBOM(os_); - } - - void Put(Ch c) { Encoding::Put(os_, c); } - void Flush() { os_.Flush(); } - - // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); } - Ch Take() { RAPIDJSON_ASSERT(false); } - size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - EncodedOutputStream(const EncodedOutputStream&); - EncodedOutputStream& operator=(const EncodedOutputStream&); - - OutputByteStream& os_; -}; - -#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x - -//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. -/*! - \tparam CharType Type of character for reading. - \tparam InputByteStream type of input byte stream to be wrapped. -*/ -template -class AutoUTFInputStream { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); -public: - typedef CharType Ch; - - //! Constructor. - /*! - \param is input stream to be wrapped. - \param type UTF encoding type if it is not detected from the stream. - */ - AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { - DetectType(); - static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; - takeFunc_ = f[type_]; - current_ = takeFunc_(*is_); - } - - UTFType GetType() const { return type_; } - bool HasBOM() const { return hasBOM_; } - - Ch Peek() const { return current_; } - Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } - size_t Tell() const { return is_->Tell(); } - - // Not implemented - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - AutoUTFInputStream(const AutoUTFInputStream&); - AutoUTFInputStream& operator=(const AutoUTFInputStream&); - - // Detect encoding type with BOM or RFC 4627 - void DetectType() { - // BOM (Byte Order Mark): - // 00 00 FE FF UTF-32BE - // FF FE 00 00 UTF-32LE - // FE FF UTF-16BE - // FF FE UTF-16LE - // EF BB BF UTF-8 - - const unsigned char* c = (const unsigned char *)is_->Peek4(); - if (!c) - return; - - unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); - hasBOM_ = false; - if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } - else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } - else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } - else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } - else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } - - // RFC 4627: Section 3 - // "Since the first two characters of a JSON text will always be ASCII - // characters [RFC0020], it is possible to determine whether an octet - // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking - // at the pattern of nulls in the first four octets." - // 00 00 00 xx UTF-32BE - // 00 xx 00 xx UTF-16BE - // xx 00 00 00 UTF-32LE - // xx 00 xx 00 UTF-16LE - // xx xx xx xx UTF-8 - - if (!hasBOM_) { - unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); - switch (pattern) { - case 0x08: type_ = kUTF32BE; break; - case 0x0A: type_ = kUTF16BE; break; - case 0x01: type_ = kUTF32LE; break; - case 0x05: type_ = kUTF16LE; break; - case 0x0F: type_ = kUTF8; break; - default: break; // Use type defined by user. - } - } - - // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. - switch (type_) { - case kUTF8: - // Do nothing - break; - case kUTF16LE: - case kUTF16BE: - RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - break; - case kUTF32LE: - case kUTF32BE: - RAPIDJSON_ASSERT(sizeof(Ch) >= 4); - break; - default: - RAPIDJSON_ASSERT(false); // Invalid type - } - } - - typedef Ch (*TakeFunc)(InputByteStream& is); - InputByteStream* is_; - UTFType type_; - Ch current_; - TakeFunc takeFunc_; - bool hasBOM_; -}; - -//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. -/*! - \tparam CharType Type of character for writing. - \tparam InputByteStream type of output byte stream to be wrapped. -*/ -template -class AutoUTFOutputStream { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); -public: - typedef CharType Ch; - - //! Constructor. - /*! - \param os output stream to be wrapped. - \param type UTF encoding type. - \param putBOM Whether to write BOM at the beginning of the stream. - */ - AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { - // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. - switch (type_) { - case kUTF16LE: - case kUTF16BE: - RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - break; - case kUTF32LE: - case kUTF32BE: - RAPIDJSON_ASSERT(sizeof(Ch) >= 4); - break; - case kUTF8: - // Do nothing - break; - default: - RAPIDJSON_ASSERT(false); // Invalid UTFType - } - - static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; - putFunc_ = f[type_]; - - if (putBOM) - PutBOM(); - } - - UTFType GetType() const { return type_; } - - void Put(Ch c) { putFunc_(*os_, c); } - void Flush() { os_->Flush(); } - - // Not implemented - Ch Peek() const { RAPIDJSON_ASSERT(false); } - Ch Take() { RAPIDJSON_ASSERT(false); } - size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - AutoUTFOutputStream(const AutoUTFOutputStream&); - AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); - - void PutBOM() { - typedef void (*PutBOMFunc)(OutputByteStream&); - static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; - f[type_](*os_); - } - - typedef void (*PutFunc)(OutputByteStream&, Ch); - - OutputByteStream* os_; - UTFType type_; - PutFunc putFunc_; -}; - -#undef RAPIDJSON_ENCODINGS_FUNC - -RAPIDJSON_NAMESPACE_END - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h deleted file mode 100644 index 71595f7..0000000 --- a/include/rapidjson/encodings.h +++ /dev/null @@ -1,630 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_ENCODINGS_H_ -#define RAPIDJSON_ENCODINGS_H_ - -#include "rapidjson.h" - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data -RAPIDJSON_DIAG_OFF(4702) // unreachable code -#elif defined(__GNUC__) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Encoding - -/*! \class rapidjson::Encoding - \brief Concept for encoding of Unicode characters. - -\code -concept Encoding { - typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. - - enum { supportUnicode = 1 }; // or 0 if not supporting unicode - - //! \brief Encode a Unicode codepoint to an output stream. - //! \param os Output stream. - //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. - template - static void Encode(OutputStream& os, unsigned codepoint); - - //! \brief Decode a Unicode codepoint from an input stream. - //! \param is Input stream. - //! \param codepoint Output of the unicode codepoint. - //! \return true if a valid codepoint can be decoded from the stream. - template - static bool Decode(InputStream& is, unsigned* codepoint); - - //! \brief Validate one Unicode codepoint from an encoded stream. - //! \param is Input stream to obtain codepoint. - //! \param os Output for copying one codepoint. - //! \return true if it is valid. - //! \note This function just validating and copying the codepoint without actually decode it. - template - static bool Validate(InputStream& is, OutputStream& os); - - // The following functions are deal with byte streams. - - //! Take a character from input byte stream, skip BOM if exist. - template - static CharType TakeBOM(InputByteStream& is); - - //! Take a character from input byte stream. - template - static Ch Take(InputByteStream& is); - - //! Put BOM to output byte stream. - template - static void PutBOM(OutputByteStream& os); - - //! Put a character to output byte stream. - template - static void Put(OutputByteStream& os, Ch c); -}; -\endcode -*/ - -/////////////////////////////////////////////////////////////////////////////// -// UTF8 - -//! UTF-8 encoding. -/*! http://en.wikipedia.org/wiki/UTF-8 - http://tools.ietf.org/html/rfc3629 - \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. - \note implements Encoding concept -*/ -template -struct UTF8 { - typedef CharType Ch; - - enum { supportUnicode = 1 }; - - template - static void Encode(OutputStream& os, unsigned codepoint) { - if (codepoint <= 0x7F) - os.Put(static_cast(codepoint & 0xFF)); - else if (codepoint <= 0x7FF) { - os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); - os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); - } - else if (codepoint <= 0xFFFF) { - os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); - os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - os.Put(static_cast(0x80 | (codepoint & 0x3F))); - } - else { - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); - os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); - os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - os.Put(static_cast(0x80 | (codepoint & 0x3F))); - } - } - - template - static bool Decode(InputStream& is, unsigned* codepoint) { -#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) -#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) -#define TAIL() COPY(); TRANS(0x70) - Ch c = is.Take(); - if (!(c & 0x80)) { - *codepoint = (unsigned char)c; - return true; - } - - unsigned char type = GetRange((unsigned char)c); - *codepoint = (0xFF >> type) & (unsigned char)c; - bool result = true; - switch (type) { - case 2: TAIL(); return result; - case 3: TAIL(); TAIL(); return result; - case 4: COPY(); TRANS(0x50); TAIL(); return result; - case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; - case 6: TAIL(); TAIL(); TAIL(); return result; - case 10: COPY(); TRANS(0x20); TAIL(); return result; - case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; - default: return false; - } -#undef COPY -#undef TRANS -#undef TAIL - } - - template - static bool Validate(InputStream& is, OutputStream& os) { -#define COPY() os.Put(c = is.Take()) -#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) -#define TAIL() COPY(); TRANS(0x70) - Ch c; - COPY(); - if (!(c & 0x80)) - return true; - - bool result = true; - switch (GetRange((unsigned char)c)) { - case 2: TAIL(); return result; - case 3: TAIL(); TAIL(); return result; - case 4: COPY(); TRANS(0x50); TAIL(); return result; - case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; - case 6: TAIL(); TAIL(); TAIL(); return result; - case 10: COPY(); TRANS(0x20); TAIL(); return result; - case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; - default: return false; - } -#undef COPY -#undef TRANS -#undef TAIL - } - - static unsigned char GetRange(unsigned char c) { - // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ - // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. - static const unsigned char type[] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, - }; - return type[c]; - } - - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - Ch c = Take(is); - if ((unsigned char)c != 0xEFu) return c; - c = is.Take(); - if ((unsigned char)c != 0xBBu) return c; - c = is.Take(); - if ((unsigned char)c != 0xBFu) return c; - c = is.Take(); - return c; - } - - template - static Ch Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return is.Take(); - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu); - } - - template - static void Put(OutputByteStream& os, Ch c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(c)); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// UTF16 - -//! UTF-16 encoding. -/*! http://en.wikipedia.org/wiki/UTF-16 - http://tools.ietf.org/html/rfc2781 - \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. - \note implements Encoding concept - - \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. - For streaming, use UTF16LE and UTF16BE, which handle endianness. -*/ -template -struct UTF16 { - typedef CharType Ch; - RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); - - enum { supportUnicode = 1 }; - - template - static void Encode(OutputStream& os, unsigned codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - if (codepoint <= 0xFFFF) { - RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair - os.Put(static_cast(codepoint)); - } - else { - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - unsigned v = codepoint - 0x10000; - os.Put(static_cast((v >> 10) | 0xD800)); - os.Put((v & 0x3FF) | 0xDC00); - } - } - - template - static bool Decode(InputStream& is, unsigned* codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); - Ch c = is.Take(); - if (c < 0xD800 || c > 0xDFFF) { - *codepoint = c; - return true; - } - else if (c <= 0xDBFF) { - *codepoint = (c & 0x3FF) << 10; - c = is.Take(); - *codepoint |= (c & 0x3FF); - *codepoint += 0x10000; - return c >= 0xDC00 && c <= 0xDFFF; - } - return false; - } - - template - static bool Validate(InputStream& is, OutputStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); - Ch c; - os.Put(c = is.Take()); - if (c < 0xD800 || c > 0xDFFF) - return true; - else if (c <= 0xDBFF) { - os.Put(c = is.Take()); - return c >= 0xDC00 && c <= 0xDFFF; - } - return false; - } -}; - -//! UTF-16 little endian encoding. -template -struct UTF16LE : UTF16 { - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = Take(is); - return (unsigned short)c == 0xFEFFu ? Take(is) : c; - } - - template - static CharType Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take(); - c |= (unsigned char)is.Take() << 8; - return c; - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFFu); os.Put(0xFEu); - } - - template - static void Put(OutputByteStream& os, CharType c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(c & 0xFFu); - os.Put((c >> 8) & 0xFFu); - } -}; - -//! UTF-16 big endian encoding. -template -struct UTF16BE : UTF16 { - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = Take(is); - return (unsigned short)c == 0xFEFFu ? Take(is) : c; - } - - template - static CharType Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take(); - return c; - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFEu); os.Put(0xFFu); - } - - template - static void Put(OutputByteStream& os, CharType c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put((c >> 8) & 0xFFu); - os.Put(c & 0xFFu); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// UTF32 - -//! UTF-32 encoding. -/*! http://en.wikipedia.org/wiki/UTF-32 - \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. - \note implements Encoding concept - - \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. - For streaming, use UTF32LE and UTF32BE, which handle endianness. -*/ -template -struct UTF32 { - typedef CharType Ch; - RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); - - enum { supportUnicode = 1 }; - - template - static void Encode(OutputStream& os, unsigned codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); - RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); - os.Put(codepoint); - } - - template - static bool Decode(InputStream& is, unsigned* codepoint) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); - Ch c = is.Take(); - *codepoint = c; - return c <= 0x10FFFF; - } - - template - static bool Validate(InputStream& is, OutputStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); - Ch c; - os.Put(c = is.Take()); - return c <= 0x10FFFF; - } -}; - -//! UTF-32 little endian enocoding. -template -struct UTF32LE : UTF32 { - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = Take(is); - return (unsigned)c == 0x0000FEFFu ? Take(is) : c; - } - - template - static CharType Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take(); - c |= (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take() << 16; - c |= (unsigned char)is.Take() << 24; - return c; - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); - } - - template - static void Put(OutputByteStream& os, CharType c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(c & 0xFFu); - os.Put((c >> 8) & 0xFFu); - os.Put((c >> 16) & 0xFFu); - os.Put((c >> 24) & 0xFFu); - } -}; - -//! UTF-32 big endian encoding. -template -struct UTF32BE : UTF32 { - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = Take(is); - return (unsigned)c == 0x0000FEFFu ? Take(is) : c; - } - - template - static CharType Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - CharType c = (unsigned char)is.Take() << 24; - c |= (unsigned char)is.Take() << 16; - c |= (unsigned char)is.Take() << 8; - c |= (unsigned char)is.Take(); - return c; - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); - } - - template - static void Put(OutputByteStream& os, CharType c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put((c >> 24) & 0xFFu); - os.Put((c >> 16) & 0xFFu); - os.Put((c >> 8) & 0xFFu); - os.Put(c & 0xFFu); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// ASCII - -//! ASCII encoding. -/*! http://en.wikipedia.org/wiki/ASCII - \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. - \note implements Encoding concept -*/ -template -struct ASCII { - typedef CharType Ch; - - enum { supportUnicode = 0 }; - - template - static void Encode(OutputStream& os, unsigned codepoint) { - RAPIDJSON_ASSERT(codepoint <= 0x7F); - os.Put(static_cast(codepoint & 0xFF)); - } - - template - static bool Decode(InputStream& is, unsigned* codepoint) { - unsigned char c = static_cast(is.Take()); - *codepoint = c; - return c <= 0X7F; - } - - template - static bool Validate(InputStream& is, OutputStream& os) { - unsigned char c = is.Take(); - os.Put(c); - return c <= 0x7F; - } - - template - static CharType TakeBOM(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - Ch c = Take(is); - return c; - } - - template - static Ch Take(InputByteStream& is) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); - return is.Take(); - } - - template - static void PutBOM(OutputByteStream& os) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - (void)os; - } - - template - static void Put(OutputByteStream& os, Ch c) { - RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); - os.Put(static_cast(c)); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// AutoUTF - -//! Runtime-specified UTF encoding type of a stream. -enum UTFType { - kUTF8 = 0, //!< UTF-8. - kUTF16LE = 1, //!< UTF-16 little endian. - kUTF16BE = 2, //!< UTF-16 big endian. - kUTF32LE = 3, //!< UTF-32 little endian. - kUTF32BE = 4 //!< UTF-32 big endian. -}; - -//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. -/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). -*/ -template -struct AutoUTF { - typedef CharType Ch; - - enum { supportUnicode = 1 }; - -#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x - - template - RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { - typedef void (*EncodeFunc)(OutputStream&, unsigned); - static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; - (*f[os.GetType()])(os, codepoint); - } - - template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { - typedef bool (*DecodeFunc)(InputStream&, unsigned*); - static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; - return (*f[is.GetType()])(is, codepoint); - } - - template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { - typedef bool (*ValidateFunc)(InputStream&, OutputStream&); - static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; - return (*f[is.GetType()])(is, os); - } - -#undef RAPIDJSON_ENCODINGS_FUNC -}; - -/////////////////////////////////////////////////////////////////////////////// -// Transcoder - -//! Encoding conversion. -template -struct Transcoder { - //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. - template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { - unsigned codepoint; - if (!SourceEncoding::Decode(is, &codepoint)) - return false; - TargetEncoding::Encode(os, codepoint); - return true; - } - - //! Validate one Unicode codepoint from an encoded stream. - template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { - return Transcode(is, os); // Since source/target encoding is different, must transcode. - } -}; - -//! Specialization of Transcoder with same source and target encoding. -template -struct Transcoder { - template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { - os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. - return true; - } - - template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { - return Encoding::Validate(is, os); // source/target encoding are the same - } -}; - -RAPIDJSON_NAMESPACE_END - -#if defined(__GNUC__) || defined(_MSV_VER) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h deleted file mode 100644 index 0171183..0000000 --- a/include/rapidjson/error/en.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_ERROR_EN_H__ -#define RAPIDJSON_ERROR_EN_H__ - -#include "error.h" - -RAPIDJSON_NAMESPACE_BEGIN - -//! Maps error code of parsing into error message. -/*! - \ingroup RAPIDJSON_ERRORS - \param parseErrorCode Error code obtained in parsing. - \return the error message. - \note User can make a copy of this function for localization. - Using switch-case is safer for future modification of error codes. -*/ -inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { - switch (parseErrorCode) { - case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); - - case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); - case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); - - case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); - - case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); - case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); - case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); - - case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); - - case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); - case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); - case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); - case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); - case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); - - case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); - case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); - case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); - - case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); - case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); - - default: - return RAPIDJSON_ERROR_STRING("Unknown error."); - } -} - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_ERROR_EN_H__ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h deleted file mode 100644 index 161ef87..0000000 --- a/include/rapidjson/error/error.h +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_ERROR_ERROR_H__ -#define RAPIDJSON_ERROR_ERROR_H__ - -/*! \file error.h */ - -/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ERROR_CHARTYPE - -//! Character type of error messages. -/*! \ingroup RAPIDJSON_ERRORS - The default character type is \c char. - On Windows, user can define this macro as \c TCHAR for supporting both - unicode/non-unicode settings. -*/ -#ifndef RAPIDJSON_ERROR_CHARTYPE -#define RAPIDJSON_ERROR_CHARTYPE char -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ERROR_STRING - -//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. -/*! \ingroup RAPIDJSON_ERRORS - By default this conversion macro does nothing. - On Windows, user can define this macro as \c _T(x) for supporting both - unicode/non-unicode settings. -*/ -#ifndef RAPIDJSON_ERROR_STRING -#define RAPIDJSON_ERROR_STRING(x) x -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// ParseErrorCode - -//! Error code of parsing. -/*! \ingroup RAPIDJSON_ERRORS - \see GenericReader::Parse, GenericReader::GetParseErrorCode -*/ -enum ParseErrorCode { - kParseErrorNone = 0, //!< No error. - - kParseErrorDocumentEmpty, //!< The document is empty. - kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. - - kParseErrorValueInvalid, //!< Invalid value. - - kParseErrorObjectMissName, //!< Missing a name for object member. - kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. - kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. - - kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. - - kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. - kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. - kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. - kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. - kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. - - kParseErrorNumberTooBig, //!< Number too big to be stored in double. - kParseErrorNumberMissFraction, //!< Miss fraction part in number. - kParseErrorNumberMissExponent, //!< Miss exponent in number. - - kParseErrorTermination, //!< Parsing was terminated. - kParseErrorUnspecificSyntaxError, //!< Unspecific syntax error. -}; - -//! Result of parsing (wraps ParseErrorCode) -/*! - \ingroup RAPIDJSON_ERRORS - \code - Document doc; - ParseResult ok = doc.Parse("[42]"); - if (!ok) { - fprintf(stderr, "JSON parse error: %s (%u)", - GetParseError_En(ok.Code()), ok.Offset()); - exit(EXIT_FAILURE); - } - \endcode - \see GenericReader::Parse, GenericDocument::Parse -*/ -struct ParseResult { - - //! Default constructor, no error. - ParseResult() : code_(kParseErrorNone), offset_(0) {} - //! Constructor to set an error. - ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} - - //! Get the error code. - ParseErrorCode Code() const { return code_; } - //! Get the error offset, if \ref IsError(), 0 otherwise. - size_t Offset() const { return offset_; } - - //! Conversion to \c bool, returns \c true, iff !\ref IsError(). - operator bool() const { return !IsError(); } - //! Whether the result is an error. - bool IsError() const { return code_ != kParseErrorNone; } - - bool operator==(const ParseResult& that) const { return code_ == that.code_; } - bool operator==(ParseErrorCode code) const { return code_ == code; } - friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } - - //! Reset error code. - void Clear() { Set(kParseErrorNone); } - //! Update error code and offset. - void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } - -private: - ParseErrorCode code_; - size_t offset_; -}; - -//! Function pointer type of GetParseError(). -/*! \ingroup RAPIDJSON_ERRORS - - This is the prototype for \c GetParseError_X(), where \c X is a locale. - User can dynamically change locale in runtime, e.g.: -\code - GetParseErrorFunc GetParseError = GetParseError_En; // or whatever - const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); -\endcode -*/ -typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_ERROR_ERROR_H__ diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h deleted file mode 100644 index 5af9be5..0000000 --- a/include/rapidjson/filereadstream.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_FILEREADSTREAM_H_ -#define RAPIDJSON_FILEREADSTREAM_H_ - -#include "rapidjson.h" -#include - -RAPIDJSON_NAMESPACE_BEGIN - -//! File byte stream for input using fread(). -/*! - \note implements Stream concept -*/ -class FileReadStream { -public: - typedef char Ch; //!< Character type (byte). - - //! Constructor. - /*! - \param fp File pointer opened for read. - \param buffer user-supplied buffer. - \param bufferSize size of buffer in bytes. Must >=4 bytes. - */ - FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { - RAPIDJSON_ASSERT(fp_ != 0); - RAPIDJSON_ASSERT(bufferSize >= 4); - Read(); - } - - Ch Peek() const { return *current_; } - Ch Take() { Ch c = *current_; Read(); return c; } - size_t Tell() const { return count_ + static_cast(current_ - buffer_); } - - // Not implemented - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - // For encoding detection only. - const Ch* Peek4() const { - return (current_ + 4 <= bufferLast_) ? current_ : 0; - } - -private: - void Read() { - if (current_ < bufferLast_) - ++current_; - else if (!eof_) { - count_ += readCount_; - readCount_ = fread(buffer_, 1, bufferSize_, fp_); - bufferLast_ = buffer_ + readCount_ - 1; - current_ = buffer_; - - if (readCount_ < bufferSize_) { - buffer_[readCount_] = '\0'; - ++bufferLast_; - eof_ = true; - } - } - } - - std::FILE* fp_; - Ch *buffer_; - size_t bufferSize_; - Ch *bufferLast_; - Ch *current_; - size_t readCount_; - size_t count_; //!< Number of characters read - bool eof_; -}; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filestream.h b/include/rapidjson/filestream.h deleted file mode 100644 index a370c60..0000000 --- a/include/rapidjson/filestream.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_FILESTREAM_H_ -#define RAPIDJSON_FILESTREAM_H_ - -#include "rapidjson.h" -#include - -RAPIDJSON_NAMESPACE_BEGIN - -//! (Deprecated) Wrapper of C file stream for input or output. -/*! - This simple wrapper does not check the validity of the stream. - \note implements Stream concept - \note deprecated: This was only for basic testing in version 0.1, it is found that the performance is very low by using fgetc(). Use FileReadStream instead. -*/ -class FileStream { -public: - typedef char Ch; //!< Character type. Only support char. - - FileStream(std::FILE* fp) : fp_(fp), current_('\0'), count_(0) { Read(); } - char Peek() const { return current_; } - char Take() { char c = current_; Read(); return c; } - size_t Tell() const { return count_; } - void Put(char c) { fputc(c, fp_); } - void Flush() { fflush(fp_); } - - // Not implemented - char* PutBegin() { return 0; } - size_t PutEnd(char*) { return 0; } - -private: - // Prohibit copy constructor & assignment operator. - FileStream(const FileStream&); - FileStream& operator=(const FileStream&); - - void Read() { - RAPIDJSON_ASSERT(fp_ != 0); - int c = fgetc(fp_); - if (c != EOF) { - current_ = (char)c; - count_++; - } - else if (current_ != '\0') - current_ = '\0'; - } - - std::FILE* fp_; - char current_; - size_t count_; -}; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filewritestream.h b/include/rapidjson/filewritestream.h deleted file mode 100644 index 4352c8f..0000000 --- a/include/rapidjson/filewritestream.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_FILEWRITESTREAM_H_ -#define RAPIDJSON_FILEWRITESTREAM_H_ - -#include "rapidjson.h" -#include - -RAPIDJSON_NAMESPACE_BEGIN - -//! Wrapper of C file stream for input using fread(). -/*! - \note implements Stream concept -*/ -class FileWriteStream { -public: - typedef char Ch; //!< Character type. Only support char. - - FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { - RAPIDJSON_ASSERT(fp_ != 0); - } - - void Put(char c) { - if (current_ >= bufferEnd_) - Flush(); - - *current_++ = c; - } - - void PutN(char c, size_t n) { - size_t avail = static_cast(bufferEnd_ - current_); - while (n > avail) { - std::memset(current_, c, avail); - current_ += avail; - Flush(); - n -= avail; - avail = static_cast(bufferEnd_ - current_); - } - - if (n > 0) { - std::memset(current_, c, n); - current_ += n; - } - } - - void Flush() { - if (current_ != buffer_) { - fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); - current_ = buffer_; - } - } - - // Not implemented - char Peek() const { RAPIDJSON_ASSERT(false); return 0; } - char Take() { RAPIDJSON_ASSERT(false); return 0; } - size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } - char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } - -private: - // Prohibit copy constructor & assignment operator. - FileWriteStream(const FileWriteStream&); - FileWriteStream& operator=(const FileWriteStream&); - - std::FILE* fp_; - char *buffer_; - char *bufferEnd_; - char *current_; -}; - -//! Implement specialized version of PutN() with memset() for better performance. -template<> -inline void PutN(FileWriteStream& stream, char c, size_t n) { - stream.PutN(c, n); -} - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h deleted file mode 100644 index 06a442b..0000000 --- a/include/rapidjson/internal/biginteger.h +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_BIGINTEGER_H_ -#define RAPIDJSON_BIGINTEGER_H_ - -#include "../rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -class BigInteger { -public: - typedef uint64_t Type; - - BigInteger(const BigInteger& rhs) : count_(rhs.count_) { - std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); - } - - explicit BigInteger(uint64_t u) : count_(1) { - digits_[0] = u; - } - - BigInteger(const char* decimals, size_t length) : count_(1) { - RAPIDJSON_ASSERT(length > 0); - digits_[0] = 0; - size_t i = 0; - const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 - while (length >= kMaxDigitPerIteration) { - AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); - length -= kMaxDigitPerIteration; - i += kMaxDigitPerIteration; - } - - if (length > 0) - AppendDecimal64(decimals + i, decimals + i + length); - } - - BigInteger& operator=(uint64_t u) { - digits_[0] = u; - count_ = 1; - return *this; - } - - BigInteger& operator+=(uint64_t u) { - Type backup = digits_[0]; - digits_[0] += u; - for (size_t i = 0; i < count_ - 1; i++) { - if (digits_[i] >= backup) - return *this; // no carry - backup = digits_[i + 1]; - digits_[i + 1] += 1; - } - - // Last carry - if (digits_[count_ - 1] < backup) - PushBack(1); - - return *this; - } - - BigInteger& operator*=(uint64_t u) { - if (u == 0) return *this = 0; - if (u == 1) return *this; - if (*this == 1) return *this = u; - - uint64_t k = 0; - for (size_t i = 0; i < count_; i++) { - uint64_t hi; - digits_[i] = MulAdd64(digits_[i], u, k, &hi); - k = hi; - } - - if (k > 0) - PushBack(k); - - return *this; - } - - BigInteger& operator*=(uint32_t u) { - if (u == 0) return *this = 0; - if (u == 1) return *this; - if (*this == 1) return *this = u; - - uint32_t k = 0; - for (size_t i = 0; i < count_; i++) { - const uint64_t c = digits_[i] >> 32; - const uint64_t d = digits_[i] & 0xFFFFFFFF; - const uint64_t uc = u * c; - const uint64_t ud = u * d; - const uint64_t p0 = ud + k; - const uint64_t p1 = uc + (p0 >> 32); - digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); - k = p1 >> 32; - } - - if (k > 0) - PushBack(k); - - return *this; - } - - BigInteger& operator<<=(size_t shift) { - if (IsZero() || shift == 0) return *this; - - size_t offset = shift / kTypeBit; - size_t interShift = shift % kTypeBit; - RAPIDJSON_ASSERT(count_ + offset <= kCapacity); - - if (interShift == 0) { - std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); - count_ += offset; - } - else { - digits_[count_] = 0; - for (size_t i = count_; i > 0; i--) - digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); - digits_[offset] = digits_[0] << interShift; - count_ += offset; - if (digits_[count_]) - count_++; - } - - std::memset(digits_, 0, offset * sizeof(Type)); - - return *this; - } - - bool operator==(const BigInteger& rhs) const { - return count_ == rhs.count_ && memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; - } - - bool operator==(const Type rhs) const { - return count_ == 1 && digits_[0] == rhs; - } - - BigInteger& MultiplyPow5(unsigned exp) { - static const uint32_t kPow5[12] = { - 5, - 5 * 5, - 5 * 5 * 5, - 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 - }; - if (exp == 0) return *this; - for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 - for (; exp >= 13; exp -= 13) *this *= 1220703125u; // 5^13 - if (exp > 0) *this *= kPow5[exp - 1]; - return *this; - } - - // Compute absolute difference of this and rhs. - // Return false if this < rhs - bool Difference(const BigInteger& rhs, BigInteger* out) const { - int cmp = Compare(rhs); - if (cmp == 0) { - *out = BigInteger(0); - return false; - } - const BigInteger *a, *b; // Makes a > b - bool ret; - if (cmp < 0) { a = &rhs; b = this; ret = true; } - else { a = this; b = &rhs; ret = false; } - - Type borrow = 0; - for (size_t i = 0; i < a->count_; i++) { - Type d = a->digits_[i] - borrow; - if (i < b->count_) - d -= b->digits_[i]; - borrow = (d > a->digits_[i]) ? 1 : 0; - out->digits_[i] = d; - if (d != 0) - out->count_ = i + 1; - } - - return ret; - } - - int Compare(const BigInteger& rhs) const { - if (count_ != rhs.count_) - return count_ < rhs.count_ ? -1 : 1; - - for (size_t i = count_; i-- > 0;) - if (digits_[i] != rhs.digits_[i]) - return digits_[i] < rhs.digits_[i] ? -1 : 1; - - return 0; - } - - size_t GetCount() const { return count_; } - Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } - bool IsZero() const { return count_ == 1 && digits_[0] == 0; } - -private: - void AppendDecimal64(const char* begin, const char* end) { - uint64_t u = ParseUint64(begin, end); - if (IsZero()) - *this = u; - else { - unsigned exp = static_cast(end - begin); - (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u - } - } - - void PushBack(Type digit) { - RAPIDJSON_ASSERT(count_ < kCapacity); - digits_[count_++] = digit; - } - - static uint64_t ParseUint64(const char* begin, const char* end) { - uint64_t r = 0; - for (const char* p = begin; p != end; ++p) { - RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); - r = r * 10 + (*p - '0'); - } - return r; - } - - // Assume a * b + k < 2^128 - static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { -#if defined(_MSC_VER) && defined(_M_AMD64) - uint64_t low = _umul128(a, b, outHigh) + k; - if (low < k) - (*outHigh)++; - return low; -#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) - unsigned __int128 p = static_cast(a) * static_cast(b); - p += k; - *outHigh = p >> 64; - return static_cast(p); -#else - const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; - uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; - x1 += (x0 >> 32); // can't give carry - x1 += x2; - if (x1 < x2) - x3 += (static_cast(1) << 32); - uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); - uint64_t hi = x3 + (x1 >> 32); - - lo += k; - if (lo < k) - hi++; - *outHigh = hi; - return lo; -#endif - } - - static Type FullAdd(Type a, Type b, bool inCarry, bool* outCarry) { - Type c = a + b + (inCarry ? 1 : 0); - *outCarry = c < a; - return c; - } - - static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 - static const size_t kCapacity = kBitCount / sizeof(Type); - static const size_t kTypeBit = sizeof(Type) * 8; - - Type digits_[kCapacity]; - size_t count_; -}; - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h deleted file mode 100644 index 174b9fa..0000000 --- a/include/rapidjson/internal/diyfp.h +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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. - -// This is a C++ header-only implementation of Grisu2 algorithm from the publication: -// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with -// integers." ACM Sigplan Notices 45.6 (2010): 233-243. - -#ifndef RAPIDJSON_DIYFP_H_ -#define RAPIDJSON_DIYFP_H_ - -#if defined(_MSC_VER) -#include -#if defined(_M_AMD64) -#pragma intrinsic(_BitScanReverse64) -#endif -#endif - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -struct DiyFp { - DiyFp() {} - - DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} - - explicit DiyFp(double d) { - union { - double d; - uint64_t u64; - } u = { d }; - - int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; - uint64_t significand = (u.u64 & kDpSignificandMask); - if (biased_e != 0) { - f = significand + kDpHiddenBit; - e = biased_e - kDpExponentBias; - } - else { - f = significand; - e = kDpMinExponent + 1; - } - } - - DiyFp operator-(const DiyFp& rhs) const { - return DiyFp(f - rhs.f, e); - } - - DiyFp operator*(const DiyFp& rhs) const { -#if defined(_MSC_VER) && defined(_M_AMD64) - uint64_t h; - uint64_t l = _umul128(f, rhs.f, &h); - if (l & (uint64_t(1) << 63)) // rounding - h++; - return DiyFp(h, e + rhs.e + 64); -#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) - unsigned __int128 p = static_cast(f) * static_cast(rhs.f); - uint64_t h = p >> 64; - uint64_t l = static_cast(p); - if (l & (uint64_t(1) << 63)) // rounding - h++; - return DiyFp(h, e + rhs.e + 64); -#else - const uint64_t M32 = 0xFFFFFFFF; - const uint64_t a = f >> 32; - const uint64_t b = f & M32; - const uint64_t c = rhs.f >> 32; - const uint64_t d = rhs.f & M32; - const uint64_t ac = a * c; - const uint64_t bc = b * c; - const uint64_t ad = a * d; - const uint64_t bd = b * d; - uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); - tmp += 1U << 31; /// mult_round - return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); -#endif - } - - DiyFp Normalize() const { -#if defined(_MSC_VER) && defined(_M_AMD64) - unsigned long index; - _BitScanReverse64(&index, f); - return DiyFp(f << (63 - index), e - (63 - index)); -#elif defined(__GNUC__) && __GNUC__ >= 4 - int s = __builtin_clzll(f); - return DiyFp(f << s, e - s); -#else - DiyFp res = *this; - while (!(res.f & (static_cast(1) << 63))) { - res.f <<= 1; - res.e--; - } - return res; -#endif - } - - DiyFp NormalizeBoundary() const { - DiyFp res = *this; - while (!(res.f & (kDpHiddenBit << 1))) { - res.f <<= 1; - res.e--; - } - res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); - res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); - return res; - } - - void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { - DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); - DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); - mi.f <<= mi.e - pl.e; - mi.e = pl.e; - *plus = pl; - *minus = mi; - } - - double ToDouble() const { - union { - double d; - uint64_t u64; - }u; - uint64_t significand = f; - int exponent = e; - while (significand > kDpHiddenBit + kDpSignificandMask) { - significand >>= 1; - exponent++; - } - while (exponent > kDpDenormalExponent && (significand & kDpHiddenBit) == 0) { - significand <<= 1; - exponent--; - } - if (exponent >= kDpMaxExponent) { - u.u64 = kDpExponentMask; // Infinity - return u.d; - } - else if (exponent < kDpDenormalExponent) - return 0.0; - const uint64_t be = (exponent == kDpDenormalExponent && (significand & kDpHiddenBit) == 0) ? 0 : - static_cast(exponent + kDpExponentBias); - u.u64 = (significand & kDpSignificandMask) | (be << kDpSignificandSize); - return u.d; - } - - static const int kDiySignificandSize = 64; - static const int kDpSignificandSize = 52; - static const int kDpExponentBias = 0x3FF + kDpSignificandSize; - static const int kDpMaxExponent = 0x7FF - kDpExponentBias; - static const int kDpMinExponent = -kDpExponentBias; - static const int kDpDenormalExponent = -kDpExponentBias + 1; - static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); - static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); - static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); - - uint64_t f; - int e; -}; - -inline DiyFp GetCachedPowerByIndex(size_t index) { - // 10^-348, 10^-340, ..., 10^340 - static const uint64_t kCachedPowers_F[] = { - RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), - RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), - RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), - RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), - RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), - RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), - RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), - RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), - RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), - RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), - RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), - RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), - RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), - RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), - RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), - RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), - RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), - RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), - RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), - RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), - RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), - RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), - RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), - RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), - RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), - RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), - RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), - RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), - RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), - RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), - RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), - RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), - RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), - RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), - RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), - RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), - RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), - RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), - RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), - RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), - RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), - RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), - RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), - RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) - }; - static const int16_t kCachedPowers_E[] = { - -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, - -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, - -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, - -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, - -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, - 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, - 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, - 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, - 907, 933, 960, 986, 1013, 1039, 1066 - }; - return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); -} - -inline DiyFp GetCachedPower(int e, int* K) { - - //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; - double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive - int k = static_cast(dk); - if (k != dk) - k++; - - unsigned index = static_cast((k >> 3) + 1); - *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table - - return GetCachedPowerByIndex(index); -} - -inline DiyFp GetCachedPower10(int exp, int *outExp) { - unsigned index = (exp + 348) / 8; - *outExp = -348 + index * 8; - return GetCachedPowerByIndex(index); - } - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_DIYFP_H_ diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h deleted file mode 100644 index c0fa2b8..0000000 --- a/include/rapidjson/internal/dtoa.h +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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. - -// This is a C++ header-only implementation of Grisu2 algorithm from the publication: -// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with -// integers." ACM Sigplan Notices 45.6 (2010): 233-243. - -#ifndef RAPIDJSON_DTOA_ -#define RAPIDJSON_DTOA_ - -#include "itoa.h" // GetDigitsLut() -#include "diyfp.h" - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { - while (rest < wp_w && delta - rest >= ten_kappa && - (rest + ten_kappa < wp_w || /// closer - wp_w - rest > rest + ten_kappa - wp_w)) { - buffer[len - 1]--; - rest += ten_kappa; - } -} - -inline unsigned CountDecimalDigit32(uint32_t n) { - // Simple pure C++ implementation was faster than __builtin_clz version in this situation. - if (n < 10) return 1; - if (n < 100) return 2; - if (n < 1000) return 3; - if (n < 10000) return 4; - if (n < 100000) return 5; - if (n < 1000000) return 6; - if (n < 10000000) return 7; - if (n < 100000000) return 8; - if (n < 1000000000) return 9; - return 10; -} - -inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { - static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); - const DiyFp wp_w = Mp - W; - uint32_t p1 = static_cast(Mp.f >> -one.e); - uint64_t p2 = Mp.f & (one.f - 1); - int kappa = CountDecimalDigit32(p1); - *len = 0; - - while (kappa > 0) { - uint32_t d; - switch (kappa) { - case 10: d = p1 / 1000000000; p1 %= 1000000000; break; - case 9: d = p1 / 100000000; p1 %= 100000000; break; - case 8: d = p1 / 10000000; p1 %= 10000000; break; - case 7: d = p1 / 1000000; p1 %= 1000000; break; - case 6: d = p1 / 100000; p1 %= 100000; break; - case 5: d = p1 / 10000; p1 %= 10000; break; - case 4: d = p1 / 1000; p1 %= 1000; break; - case 3: d = p1 / 100; p1 %= 100; break; - case 2: d = p1 / 10; p1 %= 10; break; - case 1: d = p1; p1 = 0; break; - default: -#if defined(_MSC_VER) - __assume(0); -#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) - __builtin_unreachable(); -#else - d = 0; -#endif - } - if (d || *len) - buffer[(*len)++] = static_cast('0' + static_cast(d)); - kappa--; - uint64_t tmp = (static_cast(p1) << -one.e) + p2; - if (tmp <= delta) { - *K += kappa; - GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); - return; - } - } - - // kappa = 0 - for (;;) { - p2 *= 10; - delta *= 10; - char d = static_cast(p2 >> -one.e); - if (d || *len) - buffer[(*len)++] = static_cast('0' + d); - p2 &= one.f - 1; - kappa--; - if (p2 < delta) { - *K += kappa; - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); - return; - } - } -} - -inline void Grisu2(double value, char* buffer, int* length, int* K) { - const DiyFp v(value); - DiyFp w_m, w_p; - v.NormalizedBoundaries(&w_m, &w_p); - - const DiyFp c_mk = GetCachedPower(w_p.e, K); - const DiyFp W = v.Normalize() * c_mk; - DiyFp Wp = w_p * c_mk; - DiyFp Wm = w_m * c_mk; - Wm.f++; - Wp.f--; - DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); -} - -inline char* WriteExponent(int K, char* buffer) { - if (K < 0) { - *buffer++ = '-'; - K = -K; - } - - if (K >= 100) { - *buffer++ = static_cast('0' + static_cast(K / 100)); - K %= 100; - const char* d = GetDigitsLut() + K * 2; - *buffer++ = d[0]; - *buffer++ = d[1]; - } - else if (K >= 10) { - const char* d = GetDigitsLut() + K * 2; - *buffer++ = d[0]; - *buffer++ = d[1]; - } - else - *buffer++ = static_cast('0' + static_cast(K)); - - return buffer; -} - -inline char* Prettify(char* buffer, int length, int k) { - const int kk = length + k; // 10^(kk-1) <= v < 10^kk - - if (length <= kk && kk <= 21) { - // 1234e7 -> 12340000000 - for (int i = length; i < kk; i++) - buffer[i] = '0'; - buffer[kk] = '.'; - buffer[kk + 1] = '0'; - return &buffer[kk + 2]; - } - else if (0 < kk && kk <= 21) { - // 1234e-2 -> 12.34 - std::memmove(&buffer[kk + 1], &buffer[kk], length - kk); - buffer[kk] = '.'; - return &buffer[length + 1]; - } - else if (-6 < kk && kk <= 0) { - // 1234e-6 -> 0.001234 - const int offset = 2 - kk; - std::memmove(&buffer[offset], &buffer[0], length); - buffer[0] = '0'; - buffer[1] = '.'; - for (int i = 2; i < offset; i++) - buffer[i] = '0'; - return &buffer[length + offset]; - } - else if (length == 1) { - // 1e30 - buffer[1] = 'e'; - return WriteExponent(kk - 1, &buffer[2]); - } - else { - // 1234e30 -> 1.234e33 - std::memmove(&buffer[2], &buffer[1], length - 1); - buffer[1] = '.'; - buffer[length + 1] = 'e'; - return WriteExponent(kk - 1, &buffer[0 + length + 2]); - } -} - -inline char* dtoa(double value, char* buffer) { - if (value == 0) { - buffer[0] = '0'; - buffer[1] = '.'; - buffer[2] = '0'; - return &buffer[3]; - } - else { - if (value < 0) { - *buffer++ = '-'; - value = -value; - } - int length, K; - Grisu2(value, buffer, &length, &K); - return Prettify(buffer, length, K); - } -} - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_DTOA_ diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h deleted file mode 100644 index ab65cc9..0000000 --- a/include/rapidjson/internal/ieee754.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_IEEE754_ -#define RAPIDJSON_IEEE754_ - -#include "../rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -class Double { -public: - Double() {} - Double(double d) : d(d) {} - Double(uint64_t u) : u(u) {} - - double Value() const { return d; } - uint64_t Uint64Value() const { return u; } - - double NextPositiveDouble() const { - RAPIDJSON_ASSERT(!Sign()); - return Double(u + 1).Value(); - } - - double PreviousPositiveDouble() const { - RAPIDJSON_ASSERT(!Sign()); - if (d == 0.0) - return 0.0; - else - return Double(u - 1).Value(); - } - - bool Sign() const { return (u & kSignMask) != 0; } - uint64_t Significand() const { return u & kSignificandMask; } - int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; } - - bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; } - bool IsInf() const { return (u & kExponentMask) == kExponentMask && Significand() == 0; } - bool IsNormal() const { return (u & kExponentMask) != 0 || Significand() == 0; } - - uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } - int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } - uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; } - - static unsigned EffectiveSignificandSize(int order) { - if (order >= -1021) - return 53; - else if (order <= -1074) - return 0; - else - return order + 1074; - } - -private: - static const int kSignificandSize = 52; - static const int kExponentBias = 0x3FF; - static const int kDenormalExponent = 1 - kExponentBias; - static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); - static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); - static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); - static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); - - union { - double d; - uint64_t u; - }; -}; - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_IEEE754_ diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h deleted file mode 100644 index 3684f07..0000000 --- a/include/rapidjson/internal/itoa.h +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_ITOA_ -#define RAPIDJSON_ITOA_ - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -inline const char* GetDigitsLut() { - static const char cDigitsLut[200] = { - '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', - '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', - '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', - '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', - '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', - '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', - '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', - '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', - '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', - '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' - }; - return cDigitsLut; -} - -inline char* u32toa(uint32_t value, char* buffer) { - const char* cDigitsLut = GetDigitsLut(); - - if (value < 10000) { - const uint32_t d1 = (value / 100) << 1; - const uint32_t d2 = (value % 100) << 1; - - if (value >= 1000) - *buffer++ = cDigitsLut[d1]; - if (value >= 100) - *buffer++ = cDigitsLut[d1 + 1]; - if (value >= 10) - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - } - else if (value < 100000000) { - // value = bbbbcccc - const uint32_t b = value / 10000; - const uint32_t c = value % 10000; - - const uint32_t d1 = (b / 100) << 1; - const uint32_t d2 = (b % 100) << 1; - - const uint32_t d3 = (c / 100) << 1; - const uint32_t d4 = (c % 100) << 1; - - if (value >= 10000000) - *buffer++ = cDigitsLut[d1]; - if (value >= 1000000) - *buffer++ = cDigitsLut[d1 + 1]; - if (value >= 100000) - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - - *buffer++ = cDigitsLut[d3]; - *buffer++ = cDigitsLut[d3 + 1]; - *buffer++ = cDigitsLut[d4]; - *buffer++ = cDigitsLut[d4 + 1]; - } - else { - // value = aabbbbcccc in decimal - - const uint32_t a = value / 100000000; // 1 to 42 - value %= 100000000; - - if (a >= 10) { - const unsigned i = a << 1; - *buffer++ = cDigitsLut[i]; - *buffer++ = cDigitsLut[i + 1]; - } - else - *buffer++ = static_cast('0' + static_cast(a)); - - const uint32_t b = value / 10000; // 0 to 9999 - const uint32_t c = value % 10000; // 0 to 9999 - - const uint32_t d1 = (b / 100) << 1; - const uint32_t d2 = (b % 100) << 1; - - const uint32_t d3 = (c / 100) << 1; - const uint32_t d4 = (c % 100) << 1; - - *buffer++ = cDigitsLut[d1]; - *buffer++ = cDigitsLut[d1 + 1]; - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - *buffer++ = cDigitsLut[d3]; - *buffer++ = cDigitsLut[d3 + 1]; - *buffer++ = cDigitsLut[d4]; - *buffer++ = cDigitsLut[d4 + 1]; - } - return buffer; -} - -inline char* i32toa(int32_t value, char* buffer) { - if (value < 0) { - *buffer++ = '-'; - value = -value; - } - - return u32toa(static_cast(value), buffer); -} - -inline char* u64toa(uint64_t value, char* buffer) { - const char* cDigitsLut = GetDigitsLut(); - const uint64_t kTen8 = 100000000; - const uint64_t kTen9 = kTen8 * 10; - const uint64_t kTen10 = kTen8 * 100; - const uint64_t kTen11 = kTen8 * 1000; - const uint64_t kTen12 = kTen8 * 10000; - const uint64_t kTen13 = kTen8 * 100000; - const uint64_t kTen14 = kTen8 * 1000000; - const uint64_t kTen15 = kTen8 * 10000000; - const uint64_t kTen16 = kTen8 * kTen8; - - if (value < kTen8) { - uint32_t v = static_cast(value); - if (v < 10000) { - const uint32_t d1 = (v / 100) << 1; - const uint32_t d2 = (v % 100) << 1; - - if (v >= 1000) - *buffer++ = cDigitsLut[d1]; - if (v >= 100) - *buffer++ = cDigitsLut[d1 + 1]; - if (v >= 10) - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - } - else { - // value = bbbbcccc - const uint32_t b = v / 10000; - const uint32_t c = v % 10000; - - const uint32_t d1 = (b / 100) << 1; - const uint32_t d2 = (b % 100) << 1; - - const uint32_t d3 = (c / 100) << 1; - const uint32_t d4 = (c % 100) << 1; - - if (value >= 10000000) - *buffer++ = cDigitsLut[d1]; - if (value >= 1000000) - *buffer++ = cDigitsLut[d1 + 1]; - if (value >= 100000) - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - - *buffer++ = cDigitsLut[d3]; - *buffer++ = cDigitsLut[d3 + 1]; - *buffer++ = cDigitsLut[d4]; - *buffer++ = cDigitsLut[d4 + 1]; - } - } - else if (value < kTen16) { - const uint32_t v0 = static_cast(value / kTen8); - const uint32_t v1 = static_cast(value % kTen8); - - const uint32_t b0 = v0 / 10000; - const uint32_t c0 = v0 % 10000; - - const uint32_t d1 = (b0 / 100) << 1; - const uint32_t d2 = (b0 % 100) << 1; - - const uint32_t d3 = (c0 / 100) << 1; - const uint32_t d4 = (c0 % 100) << 1; - - const uint32_t b1 = v1 / 10000; - const uint32_t c1 = v1 % 10000; - - const uint32_t d5 = (b1 / 100) << 1; - const uint32_t d6 = (b1 % 100) << 1; - - const uint32_t d7 = (c1 / 100) << 1; - const uint32_t d8 = (c1 % 100) << 1; - - if (value >= kTen15) - *buffer++ = cDigitsLut[d1]; - if (value >= kTen14) - *buffer++ = cDigitsLut[d1 + 1]; - if (value >= kTen13) - *buffer++ = cDigitsLut[d2]; - if (value >= kTen12) - *buffer++ = cDigitsLut[d2 + 1]; - if (value >= kTen11) - *buffer++ = cDigitsLut[d3]; - if (value >= kTen10) - *buffer++ = cDigitsLut[d3 + 1]; - if (value >= kTen9) - *buffer++ = cDigitsLut[d4]; - if (value >= kTen8) - *buffer++ = cDigitsLut[d4 + 1]; - - *buffer++ = cDigitsLut[d5]; - *buffer++ = cDigitsLut[d5 + 1]; - *buffer++ = cDigitsLut[d6]; - *buffer++ = cDigitsLut[d6 + 1]; - *buffer++ = cDigitsLut[d7]; - *buffer++ = cDigitsLut[d7 + 1]; - *buffer++ = cDigitsLut[d8]; - *buffer++ = cDigitsLut[d8 + 1]; - } - else { - const uint32_t a = static_cast(value / kTen16); // 1 to 1844 - value %= kTen16; - - if (a < 10) - *buffer++ = static_cast('0' + static_cast(a)); - else if (a < 100) { - const uint32_t i = a << 1; - *buffer++ = cDigitsLut[i]; - *buffer++ = cDigitsLut[i + 1]; - } - else if (a < 1000) { - *buffer++ = static_cast('0' + static_cast(a / 100)); - - const uint32_t i = (a % 100) << 1; - *buffer++ = cDigitsLut[i]; - *buffer++ = cDigitsLut[i + 1]; - } - else { - const uint32_t i = (a / 100) << 1; - const uint32_t j = (a % 100) << 1; - *buffer++ = cDigitsLut[i]; - *buffer++ = cDigitsLut[i + 1]; - *buffer++ = cDigitsLut[j]; - *buffer++ = cDigitsLut[j + 1]; - } - - const uint32_t v0 = static_cast(value / kTen8); - const uint32_t v1 = static_cast(value % kTen8); - - const uint32_t b0 = v0 / 10000; - const uint32_t c0 = v0 % 10000; - - const uint32_t d1 = (b0 / 100) << 1; - const uint32_t d2 = (b0 % 100) << 1; - - const uint32_t d3 = (c0 / 100) << 1; - const uint32_t d4 = (c0 % 100) << 1; - - const uint32_t b1 = v1 / 10000; - const uint32_t c1 = v1 % 10000; - - const uint32_t d5 = (b1 / 100) << 1; - const uint32_t d6 = (b1 % 100) << 1; - - const uint32_t d7 = (c1 / 100) << 1; - const uint32_t d8 = (c1 % 100) << 1; - - *buffer++ = cDigitsLut[d1]; - *buffer++ = cDigitsLut[d1 + 1]; - *buffer++ = cDigitsLut[d2]; - *buffer++ = cDigitsLut[d2 + 1]; - *buffer++ = cDigitsLut[d3]; - *buffer++ = cDigitsLut[d3 + 1]; - *buffer++ = cDigitsLut[d4]; - *buffer++ = cDigitsLut[d4 + 1]; - *buffer++ = cDigitsLut[d5]; - *buffer++ = cDigitsLut[d5 + 1]; - *buffer++ = cDigitsLut[d6]; - *buffer++ = cDigitsLut[d6 + 1]; - *buffer++ = cDigitsLut[d7]; - *buffer++ = cDigitsLut[d7 + 1]; - *buffer++ = cDigitsLut[d8]; - *buffer++ = cDigitsLut[d8 + 1]; - } - - return buffer; -} - -inline char* i64toa(int64_t value, char* buffer) { - if (value < 0) { - *buffer++ = '-'; - value = -value; - } - - return u64toa(static_cast(value), buffer); -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_ITOA_ diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h deleted file mode 100644 index c33f607..0000000 --- a/include/rapidjson/internal/meta.h +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_INTERNAL_META_H_ -#define RAPIDJSON_INTERNAL_META_H_ - -#ifndef RAPIDJSON_RAPIDJSON_H_ -#error not yet included. Do not include this file directly. -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif -#if defined(_MSC_VER) -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(6334) -#endif - -#if RAPIDJSON_HAS_CXX11_TYPETRAITS -#include -#endif - -//@cond RAPIDJSON_INTERNAL -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching -template struct Void { typedef void Type; }; - -/////////////////////////////////////////////////////////////////////////////// -// BoolType, TrueType, FalseType -// -template struct BoolType { - static const bool Value = Cond; - typedef BoolType Type; -}; -typedef BoolType TrueType; -typedef BoolType FalseType; - - -/////////////////////////////////////////////////////////////////////////////// -// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr -// - -template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; -template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; -template struct SelectIfCond : SelectIfImpl::template Apply {}; -template struct SelectIf : SelectIfCond {}; - -template struct AndExprCond : FalseType {}; -template <> struct AndExprCond : TrueType {}; -template struct OrExprCond : TrueType {}; -template <> struct OrExprCond : FalseType {}; - -template struct BoolExpr : SelectIf::Type {}; -template struct NotExpr : SelectIf::Type {}; -template struct AndExpr : AndExprCond::Type {}; -template struct OrExpr : OrExprCond::Type {}; - - -/////////////////////////////////////////////////////////////////////////////// -// AddConst, MaybeAddConst, RemoveConst -template struct AddConst { typedef const T Type; }; -template struct MaybeAddConst : SelectIfCond {}; -template struct RemoveConst { typedef T Type; }; -template struct RemoveConst { typedef T Type; }; - - -/////////////////////////////////////////////////////////////////////////////// -// IsSame, IsConst, IsMoreConst, IsPointer -// -template struct IsSame : FalseType {}; -template struct IsSame : TrueType {}; - -template struct IsConst : FalseType {}; -template struct IsConst : TrueType {}; - -template -struct IsMoreConst - : AndExpr::Type, typename RemoveConst::Type>, - BoolType::Value >= IsConst::Value> >::Type {}; - -template struct IsPointer : FalseType {}; -template struct IsPointer : TrueType {}; - -/////////////////////////////////////////////////////////////////////////////// -// IsBaseOf -// -#if RAPIDJSON_HAS_CXX11_TYPETRAITS - -template struct IsBaseOf - : BoolType< ::std::is_base_of::value> {}; - -#else // simplified version adopted from Boost - -template struct IsBaseOfImpl { - RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); - RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); - - typedef char (&Yes)[1]; - typedef char (&No) [2]; - - template - static Yes Check(const D*, T); - static No Check(const B*, int); - - struct Host { - operator const B*() const; - operator const D*(); - }; - - enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; -}; - -template struct IsBaseOf - : OrExpr, BoolExpr > >::Type {}; - -#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS - - -////////////////////////////////////////////////////////////////////////// -// EnableIf / DisableIf -// -template struct EnableIfCond { typedef T Type; }; -template struct EnableIfCond { /* empty */ }; - -template struct DisableIfCond { typedef T Type; }; -template struct DisableIfCond { /* empty */ }; - -template -struct EnableIf : EnableIfCond {}; - -template -struct DisableIf : DisableIfCond {}; - -// SFINAE helpers -struct SfinaeTag {}; -template struct RemoveSfinaeTag; -template struct RemoveSfinaeTag { typedef T Type; }; - -#define RAPIDJSON_REMOVEFPTR_(type) \ - typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ - < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type - -#define RAPIDJSON_ENABLEIF(cond) \ - typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ - ::Type * = NULL - -#define RAPIDJSON_DISABLEIF(cond) \ - typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ - ::Type * = NULL - -#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ - typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ - ::Type - -#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ - typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ - ::Type - -} // namespace internal -RAPIDJSON_NAMESPACE_END -//@endcond - -#if defined(__GNUC__) || defined(_MSC_VER) -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/include/rapidjson/internal/pow10.h b/include/rapidjson/internal/pow10.h deleted file mode 100644 index 91cf647..0000000 --- a/include/rapidjson/internal/pow10.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_POW10_ -#define RAPIDJSON_POW10_ - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -//! Computes integer powers of 10 in double (10.0^n). -/*! This function uses lookup table for fast and accurate results. - \param n non-negative exponent. Must <= 308. - \return 10.0^n -*/ -inline double Pow10(int n) { - static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes - 1e+0, - 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, - 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, - 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, - 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, - 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, - 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, - 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, - 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, - 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, - 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, - 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, - 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, - 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, - 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, - 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, - 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 - }; - RAPIDJSON_ASSERT(n >= 0 && n <= 308); - return e[n]; -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_POW10_ diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h deleted file mode 100644 index 62ae7aa..0000000 --- a/include/rapidjson/internal/stack.h +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_INTERNAL_STACK_H_ -#define RAPIDJSON_INTERNAL_STACK_H_ - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -/////////////////////////////////////////////////////////////////////////////// -// Stack - -//! A type-unsafe stack for storing different types of data. -/*! \tparam Allocator Allocator for allocating stack memory. -*/ -template -class Stack { -public: - // Optimization note: Do not allocate memory for stack_ in constructor. - // Do it lazily when first Push() -> Expand() -> Resize(). - Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { - RAPIDJSON_ASSERT(stackCapacity > 0); - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - Stack(Stack&& rhs) - : allocator_(rhs.allocator_), - ownAllocator_(rhs.ownAllocator_), - stack_(rhs.stack_), - stackTop_(rhs.stackTop_), - stackEnd_(rhs.stackEnd_), - initialCapacity_(rhs.initialCapacity_) - { - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.stack_ = 0; - rhs.stackTop_ = 0; - rhs.stackEnd_ = 0; - rhs.initialCapacity_ = 0; - } -#endif - - ~Stack() { - Destroy(); - } - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - Stack& operator=(Stack&& rhs) { - if (&rhs != this) - { - Destroy(); - - allocator_ = rhs.allocator_; - ownAllocator_ = rhs.ownAllocator_; - stack_ = rhs.stack_; - stackTop_ = rhs.stackTop_; - stackEnd_ = rhs.stackEnd_; - initialCapacity_ = rhs.initialCapacity_; - - rhs.allocator_ = 0; - rhs.ownAllocator_ = 0; - rhs.stack_ = 0; - rhs.stackTop_ = 0; - rhs.stackEnd_ = 0; - rhs.initialCapacity_ = 0; - } - return *this; - } -#endif - - void Clear() { stackTop_ = stack_; } - - void ShrinkToFit() { - if (Empty()) { - // If the stack is empty, completely deallocate the memory. - Allocator::Free(stack_); - stack_ = 0; - stackTop_ = 0; - stackEnd_ = 0; - } - else - Resize(GetSize()); - } - - // Optimization note: try to minimize the size of this function for force inline. - // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. - template - RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { - // Expand the stack if needed - if (stackTop_ + sizeof(T) * count >= stackEnd_) - Expand(count); - - T* ret = reinterpret_cast(stackTop_); - stackTop_ += sizeof(T) * count; - return ret; - } - - template - T* Pop(size_t count) { - RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); - stackTop_ -= count * sizeof(T); - return reinterpret_cast(stackTop_); - } - - template - T* Top() { - RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); - return reinterpret_cast(stackTop_ - sizeof(T)); - } - - template - T* Bottom() { return (T*)stack_; } - - Allocator& GetAllocator() { return *allocator_; } - bool Empty() const { return stackTop_ == stack_; } - size_t GetSize() const { return static_cast(stackTop_ - stack_); } - size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } - -private: - template - void Expand(size_t count) { - // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. - size_t newCapacity; - if (stack_ == 0) { - if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); - newCapacity = initialCapacity_; - } else { - newCapacity = GetCapacity(); - newCapacity += (newCapacity + 1) / 2; - } - size_t newSize = GetSize() + sizeof(T) * count; - if (newCapacity < newSize) - newCapacity = newSize; - - Resize(newCapacity); - } - - void Resize(size_t newCapacity) { - const size_t size = GetSize(); // Backup the current size - stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity); - stackTop_ = stack_ + size; - stackEnd_ = stack_ + newCapacity; - } - - void Destroy() { - Allocator::Free(stack_); - RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack - } - - // Prohibit copy constructor & assignment operator. - Stack(const Stack&); - Stack& operator=(const Stack&); - - Allocator* allocator_; - Allocator* ownAllocator_; - char *stack_; - char *stackTop_; - char *stackEnd_; - size_t initialCapacity_; -}; - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_STACK_H_ diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h deleted file mode 100644 index 734adc3..0000000 --- a/include/rapidjson/internal/strfunc.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_INTERNAL_STRFUNC_H_ -#define RAPIDJSON_INTERNAL_STRFUNC_H_ - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -//! Custom strlen() which works on different character types. -/*! \tparam Ch Character type (e.g. char, wchar_t, short) - \param s Null-terminated input string. - \return Number of characters in the string. - \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. -*/ -template -inline SizeType StrLen(const Ch* s) { - const Ch* p = s; - while (*p) ++p; - return SizeType(p - s); -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h deleted file mode 100644 index 1fc6050..0000000 --- a/include/rapidjson/internal/strtod.h +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_STRTOD_ -#define RAPIDJSON_STRTOD_ - -#include "../rapidjson.h" -#include "ieee754.h" -#include "biginteger.h" -#include "diyfp.h" -#include "pow10.h" - -RAPIDJSON_NAMESPACE_BEGIN -namespace internal { - -inline double FastPath(double significand, int exp) { - if (exp < -308) - return 0.0; - else if (exp >= 0) - return significand * internal::Pow10(exp); - else - return significand / internal::Pow10(-exp); -} - -inline double StrtodNormalPrecision(double d, int p) { - if (p < -308) { - // Prevent expSum < -308, making Pow10(p) = 0 - d = FastPath(d, -308); - d = FastPath(d, p + 308); - } - else - d = FastPath(d, p); - return d; -} - -template -inline T Min3(T a, T b, T c) { - T m = a; - if (m > b) m = b; - if (m > c) m = c; - return m; -} - -inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adjustToNegative) { - const Double db(b); - const uint64_t bInt = db.IntegerSignificand(); - const int bExp = db.IntegerExponent(); - const int hExp = bExp - 1; - - int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; - - // Adjust for decimal exponent - if (dExp >= 0) { - dS_Exp2 += dExp; - dS_Exp5 += dExp; - } - else { - bS_Exp2 -= dExp; - bS_Exp5 -= dExp; - hS_Exp2 -= dExp; - hS_Exp5 -= dExp; - } - - // Adjust for binary exponent - if (bExp >= 0) - bS_Exp2 += bExp; - else { - dS_Exp2 -= bExp; - hS_Exp2 -= bExp; - } - - // Adjust for half ulp exponent - if (hExp >= 0) - hS_Exp2 += hExp; - else { - dS_Exp2 -= hExp; - bS_Exp2 -= hExp; - } - - // Remove common power of two factor from all three scaled values - int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); - dS_Exp2 -= common_Exp2; - bS_Exp2 -= common_Exp2; - hS_Exp2 -= common_Exp2; - - BigInteger dS = d; - dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2; - - BigInteger bS(bInt); - bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2; - - BigInteger hS(1); - hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; - - BigInteger delta(0); - *adjustToNegative = dS.Difference(bS, &delta); - - int cmp = delta.Compare(hS); - // If delta is within 1/2 ULP, check for special case when significand is power of two. - // In this case, need to compare with 1/2h in the lower bound. - if (cmp < 0 && *adjustToNegative && // within and dS < bS - db.IsNormal() && (bInt & (bInt - 1)) == 0 && // Power of 2 - db.Uint64Value() != RAPIDJSON_UINT64_C2(0x00100000, 0x00000000)) // minimum normal number must not do this - { - delta <<= 1; - return delta.Compare(hS); - } - return cmp; -} - -inline bool StrtodFast(double d, int p, double* result) { - // Use fast path for string-to-double conversion if possible - // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - if (p > 22 && p < 22 + 16) { - // Fast Path Cases In Disguise - d *= internal::Pow10(p - 22); - p = 22; - } - - if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 - *result = FastPath(d, p); - return true; - } - else - return false; -} - -// Compute an approximation and see if it is within 1/2 ULP -inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { - uint64_t significand = 0; - size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 - for (; i < length; i++) { - if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || - (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) - break; - significand = significand * 10 + (decimals[i] - '0'); - } - - if (i < length && decimals[i] >= '5') // Rounding - significand++; - - size_t remaining = length - i; - const unsigned kUlpShift = 3; - const unsigned kUlp = 1 << kUlpShift; - int error = (remaining == 0) ? 0 : kUlp / 2; - - DiyFp v(significand, 0); - v = v.Normalize(); - error <<= -v.e; - - const int dExp = (int)decimalPosition - (int)i + exp; - - int actualExp; - DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); - if (actualExp != dExp) { - static const DiyFp kPow10[] = { - DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 - DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 - DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 - DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 - DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 - DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 - DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 - }; - int adjustment = dExp - actualExp - 1; - RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); - v = v * kPow10[adjustment]; - if (length + adjustment > 19) // has more digits than decimal digits in 64-bit - error += kUlp / 2; - } - - v = v * cachedPower; - - error += kUlp + (error == 0 ? 0 : 1); - - const int oldExp = v.e; - v = v.Normalize(); - error <<= oldExp - v.e; - - const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); - unsigned precisionSize = 64 - effectiveSignificandSize; - if (precisionSize + kUlpShift >= 64) { - unsigned scaleExp = (precisionSize + kUlpShift) - 63; - v.f >>= scaleExp; - v.e += scaleExp; - error = (error >> scaleExp) + 1 + kUlp; - precisionSize -= scaleExp; - } - - DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); - const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; - const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; - if (precisionBits >= halfWay + error) - rounded.f++; - - *result = rounded.ToDouble(); - - return halfWay - error >= precisionBits || precisionBits >= halfWay + error; -} - -inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { - const BigInteger dInt(decimals, length); - const int dExp = (int)decimalPosition - (int)length + exp; - Double a(approx); - for (int i = 0; i < 10; i++) { - bool adjustToNegative; - int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp, &adjustToNegative); - if (cmp < 0) - return a.Value(); // within half ULP - else if (cmp == 0) { - // Round towards even - if (a.Significand() & 1) - return adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); - else - return a.Value(); - } - else // adjustment - a = adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); - } - - // This should not happen, but in case there is really a bug, break the infinite-loop - return a.Value(); -} - -inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { - RAPIDJSON_ASSERT(d >= 0.0); - RAPIDJSON_ASSERT(length >= 1); - - double result; - if (StrtodFast(d, p, &result)) - return result; - - // Trim leading zeros - while (*decimals == '0' && length > 1) { - length--; - decimals++; - decimalPosition--; - } - - // Trim trailing zeros - while (decimals[length - 1] == '0' && length > 1) { - length--; - decimalPosition--; - exp++; - } - - // Trim right-most digits - const int kMaxDecimalDigit = 780; - if ((int)length > kMaxDecimalDigit) { - exp += (int(length) - kMaxDecimalDigit); - length = kMaxDecimalDigit; - } - - // If too small, underflow to zero - if (int(length) + exp < -324) - return 0.0; - - if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) - return result; - - // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison - return StrtodBigInteger(result, decimals, length, decimalPosition, exp); -} - -} // namespace internal -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_STRTOD_ diff --git a/include/rapidjson/memorybuffer.h b/include/rapidjson/memorybuffer.h deleted file mode 100644 index 95c68a3..0000000 --- a/include/rapidjson/memorybuffer.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_MEMORYBUFFER_H_ -#define RAPIDJSON_MEMORYBUFFER_H_ - -#include "rapidjson.h" -#include "internal/stack.h" - -RAPIDJSON_NAMESPACE_BEGIN - -//! Represents an in-memory output byte stream. -/*! - This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. - - It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. - - Differences between MemoryBuffer and StringBuffer: - 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. - 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. - - \tparam Allocator type for allocating memory buffer. - \note implements Stream concept -*/ -template -struct GenericMemoryBuffer { - typedef char Ch; // byte - - GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} - - void Put(Ch c) { *stack_.template Push() = c; } - void Flush() {} - - void Clear() { stack_.Clear(); } - void ShrinkToFit() { stack_.ShrinkToFit(); } - Ch* Push(size_t count) { return stack_.template Push(count); } - void Pop(size_t count) { stack_.template Pop(count); } - - const Ch* GetBuffer() const { - return stack_.template Bottom(); - } - - size_t GetSize() const { return stack_.GetSize(); } - - static const size_t kDefaultCapacity = 256; - mutable internal::Stack stack_; -}; - -typedef GenericMemoryBuffer<> MemoryBuffer; - -//! Implement specialized version of PutN() with memset() for better performance. -template<> -inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { - std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); -} - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/memorystream.h b/include/rapidjson/memorystream.h deleted file mode 100644 index f994a12..0000000 --- a/include/rapidjson/memorystream.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_MEMORYSTREAM_H_ -#define RAPIDJSON_MEMORYSTREAM_H_ - -#include "rapidjson.h" - -RAPIDJSON_NAMESPACE_BEGIN - -//! Represents an in-memory input byte stream. -/*! - This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. - - It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. - - Differences between MemoryStream and StringStream: - 1. StringStream has encoding but MemoryStream is a byte stream. - 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. - 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). - \note implements Stream concept -*/ -struct MemoryStream { - typedef char Ch; // byte - - MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} - - Ch Peek() const { return (src_ == end_) ? '\0' : *src_; } - Ch Take() { return (src_ == end_) ? '\0' : *src_++; } - size_t Tell() const { return static_cast(src_ - begin_); } - - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - // For encoding detection only. - const Ch* Peek4() const { - return Tell() + 4 <= size_ ? src_ : 0; - } - - const Ch* src_; //!< Current read position. - const Ch* begin_; //!< Original head of the string. - const Ch* end_; //!< End of stream. - size_t size_; //!< Size of the stream. -}; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/msinttypes/inttypes.h b/include/rapidjson/msinttypes/inttypes.h deleted file mode 100644 index af713c9..0000000 --- a/include/rapidjson/msinttypes/inttypes.h +++ /dev/null @@ -1,312 +0,0 @@ -// ISO C9x compliant inttypes.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2013 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the product nor the names of its contributors may -// be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_INTTYPES_H_ // [ -#define _MSC_INTTYPES_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include "stdint.h" - -// miloyip: VC supports inttypes.h since VC2013 -#if _MSC_VER >= 1800 -#include -#else - -// 7.8 Format conversion of integer types - -typedef struct { - intmax_t quot; - intmax_t rem; -} imaxdiv_t; - -// 7.8.1 Macros for format specifiers - -#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 - -// The fprintf macros for signed integers are: -#define PRId8 "d" -#define PRIi8 "i" -#define PRIdLEAST8 "d" -#define PRIiLEAST8 "i" -#define PRIdFAST8 "d" -#define PRIiFAST8 "i" - -#define PRId16 "hd" -#define PRIi16 "hi" -#define PRIdLEAST16 "hd" -#define PRIiLEAST16 "hi" -#define PRIdFAST16 "hd" -#define PRIiFAST16 "hi" - -#define PRId32 "I32d" -#define PRIi32 "I32i" -#define PRIdLEAST32 "I32d" -#define PRIiLEAST32 "I32i" -#define PRIdFAST32 "I32d" -#define PRIiFAST32 "I32i" - -#define PRId64 "I64d" -#define PRIi64 "I64i" -#define PRIdLEAST64 "I64d" -#define PRIiLEAST64 "I64i" -#define PRIdFAST64 "I64d" -#define PRIiFAST64 "I64i" - -#define PRIdMAX "I64d" -#define PRIiMAX "I64i" - -#define PRIdPTR "Id" -#define PRIiPTR "Ii" - -// The fprintf macros for unsigned integers are: -#define PRIo8 "o" -#define PRIu8 "u" -#define PRIx8 "x" -#define PRIX8 "X" -#define PRIoLEAST8 "o" -#define PRIuLEAST8 "u" -#define PRIxLEAST8 "x" -#define PRIXLEAST8 "X" -#define PRIoFAST8 "o" -#define PRIuFAST8 "u" -#define PRIxFAST8 "x" -#define PRIXFAST8 "X" - -#define PRIo16 "ho" -#define PRIu16 "hu" -#define PRIx16 "hx" -#define PRIX16 "hX" -#define PRIoLEAST16 "ho" -#define PRIuLEAST16 "hu" -#define PRIxLEAST16 "hx" -#define PRIXLEAST16 "hX" -#define PRIoFAST16 "ho" -#define PRIuFAST16 "hu" -#define PRIxFAST16 "hx" -#define PRIXFAST16 "hX" - -#define PRIo32 "I32o" -#define PRIu32 "I32u" -#define PRIx32 "I32x" -#define PRIX32 "I32X" -#define PRIoLEAST32 "I32o" -#define PRIuLEAST32 "I32u" -#define PRIxLEAST32 "I32x" -#define PRIXLEAST32 "I32X" -#define PRIoFAST32 "I32o" -#define PRIuFAST32 "I32u" -#define PRIxFAST32 "I32x" -#define PRIXFAST32 "I32X" - -#define PRIo64 "I64o" -#define PRIu64 "I64u" -#define PRIx64 "I64x" -#define PRIX64 "I64X" -#define PRIoLEAST64 "I64o" -#define PRIuLEAST64 "I64u" -#define PRIxLEAST64 "I64x" -#define PRIXLEAST64 "I64X" -#define PRIoFAST64 "I64o" -#define PRIuFAST64 "I64u" -#define PRIxFAST64 "I64x" -#define PRIXFAST64 "I64X" - -#define PRIoMAX "I64o" -#define PRIuMAX "I64u" -#define PRIxMAX "I64x" -#define PRIXMAX "I64X" - -#define PRIoPTR "Io" -#define PRIuPTR "Iu" -#define PRIxPTR "Ix" -#define PRIXPTR "IX" - -// The fscanf macros for signed integers are: -#define SCNd8 "d" -#define SCNi8 "i" -#define SCNdLEAST8 "d" -#define SCNiLEAST8 "i" -#define SCNdFAST8 "d" -#define SCNiFAST8 "i" - -#define SCNd16 "hd" -#define SCNi16 "hi" -#define SCNdLEAST16 "hd" -#define SCNiLEAST16 "hi" -#define SCNdFAST16 "hd" -#define SCNiFAST16 "hi" - -#define SCNd32 "ld" -#define SCNi32 "li" -#define SCNdLEAST32 "ld" -#define SCNiLEAST32 "li" -#define SCNdFAST32 "ld" -#define SCNiFAST32 "li" - -#define SCNd64 "I64d" -#define SCNi64 "I64i" -#define SCNdLEAST64 "I64d" -#define SCNiLEAST64 "I64i" -#define SCNdFAST64 "I64d" -#define SCNiFAST64 "I64i" - -#define SCNdMAX "I64d" -#define SCNiMAX "I64i" - -#ifdef _WIN64 // [ -# define SCNdPTR "I64d" -# define SCNiPTR "I64i" -#else // _WIN64 ][ -# define SCNdPTR "ld" -# define SCNiPTR "li" -#endif // _WIN64 ] - -// The fscanf macros for unsigned integers are: -#define SCNo8 "o" -#define SCNu8 "u" -#define SCNx8 "x" -#define SCNX8 "X" -#define SCNoLEAST8 "o" -#define SCNuLEAST8 "u" -#define SCNxLEAST8 "x" -#define SCNXLEAST8 "X" -#define SCNoFAST8 "o" -#define SCNuFAST8 "u" -#define SCNxFAST8 "x" -#define SCNXFAST8 "X" - -#define SCNo16 "ho" -#define SCNu16 "hu" -#define SCNx16 "hx" -#define SCNX16 "hX" -#define SCNoLEAST16 "ho" -#define SCNuLEAST16 "hu" -#define SCNxLEAST16 "hx" -#define SCNXLEAST16 "hX" -#define SCNoFAST16 "ho" -#define SCNuFAST16 "hu" -#define SCNxFAST16 "hx" -#define SCNXFAST16 "hX" - -#define SCNo32 "lo" -#define SCNu32 "lu" -#define SCNx32 "lx" -#define SCNX32 "lX" -#define SCNoLEAST32 "lo" -#define SCNuLEAST32 "lu" -#define SCNxLEAST32 "lx" -#define SCNXLEAST32 "lX" -#define SCNoFAST32 "lo" -#define SCNuFAST32 "lu" -#define SCNxFAST32 "lx" -#define SCNXFAST32 "lX" - -#define SCNo64 "I64o" -#define SCNu64 "I64u" -#define SCNx64 "I64x" -#define SCNX64 "I64X" -#define SCNoLEAST64 "I64o" -#define SCNuLEAST64 "I64u" -#define SCNxLEAST64 "I64x" -#define SCNXLEAST64 "I64X" -#define SCNoFAST64 "I64o" -#define SCNuFAST64 "I64u" -#define SCNxFAST64 "I64x" -#define SCNXFAST64 "I64X" - -#define SCNoMAX "I64o" -#define SCNuMAX "I64u" -#define SCNxMAX "I64x" -#define SCNXMAX "I64X" - -#ifdef _WIN64 // [ -# define SCNoPTR "I64o" -# define SCNuPTR "I64u" -# define SCNxPTR "I64x" -# define SCNXPTR "I64X" -#else // _WIN64 ][ -# define SCNoPTR "lo" -# define SCNuPTR "lu" -# define SCNxPTR "lx" -# define SCNXPTR "lX" -#endif // _WIN64 ] - -#endif // __STDC_FORMAT_MACROS ] - -// 7.8.2 Functions for greatest-width integer types - -// 7.8.2.1 The imaxabs function -#define imaxabs _abs64 - -// 7.8.2.2 The imaxdiv function - -// This is modified version of div() function from Microsoft's div.c found -// in %MSVC.NET%\crt\src\div.c -#ifdef STATIC_IMAXDIV // [ -static -#else // STATIC_IMAXDIV ][ -_inline -#endif // STATIC_IMAXDIV ] -imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) -{ - imaxdiv_t result; - - result.quot = numer / denom; - result.rem = numer % denom; - - if (numer < 0 && result.rem > 0) { - // did division wrong; must fix up - ++result.quot; - result.rem -= denom; - } - - return result; -} - -// 7.8.2.3 The strtoimax and strtoumax functions -#define strtoimax _strtoi64 -#define strtoumax _strtoui64 - -// 7.8.2.4 The wcstoimax and wcstoumax functions -#define wcstoimax _wcstoi64 -#define wcstoumax _wcstoui64 - -#endif // _MSC_VER >= 1800 - -#endif // _MSC_INTTYPES_H_ ] diff --git a/include/rapidjson/msinttypes/stdint.h b/include/rapidjson/msinttypes/stdint.h deleted file mode 100644 index bbad95a..0000000 --- a/include/rapidjson/msinttypes/stdint.h +++ /dev/null @@ -1,296 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2013 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the product nor the names of its contributors may -// be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. -#if _MSC_VER >= 1600 // [ -#include - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -#undef INT8_C -#undef INT16_C -#undef INT32_C -#undef INT64_C -#undef UINT8_C -#undef UINT16_C -#undef UINT32_C -#undef UINT64_C - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -// These #ifndef's are needed to prevent collisions with . -// Check out Issue 9 for the details. -#ifndef INTMAX_C // [ -# define INTMAX_C INT64_C -#endif // INTMAX_C ] -#ifndef UINTMAX_C // [ -# define UINTMAX_C UINT64_C -#endif // UINTMAX_C ] - -#endif // __STDC_CONSTANT_MACROS ] - -#else // ] _MSC_VER >= 1700 [ - -#include - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus -extern "C" { -#endif -# include -#ifdef __cplusplus -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) - typedef signed char int8_t; - typedef signed short int16_t; - typedef signed int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#else - typedef signed __int8 int8_t; - typedef signed __int16 int16_t; - typedef signed __int32 int32_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef signed __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 signed int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -// These #ifndef's are needed to prevent collisions with . -// Check out Issue 9 for the details. -#ifndef INTMAX_C // [ -# define INTMAX_C INT64_C -#endif // INTMAX_C ] -#ifndef UINTMAX_C // [ -# define UINTMAX_C UINT64_C -#endif // UINTMAX_C ] - -#endif // __STDC_CONSTANT_MACROS ] - -#endif // _MSC_VER >= 1600 ] - -#endif // _MSC_STDINT_H_ ] diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h deleted file mode 100644 index ce2dac5..0000000 --- a/include/rapidjson/prettywriter.h +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_PRETTYWRITER_H_ -#define RAPIDJSON_PRETTYWRITER_H_ - -#include "writer.h" - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! Writer with indentation and spacing. -/*! - \tparam OutputStream Type of ouptut os. - \tparam SourceEncoding Encoding of source string. - \tparam TargetEncoding Encoding of output stream. - \tparam StackAllocator Type of allocator for allocating memory of stack. -*/ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> -class PrettyWriter : public Writer { -public: - typedef Writer Base; - typedef typename Base::Ch Ch; - - //! Constructor - /*! \param os Output stream. - \param allocator User supplied allocator. If it is null, it will create a private one. - \param levelDepth Initial capacity of stack. - */ - PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : - Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} - - //! Set custom indentation. - /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). - \param indentCharCount Number of indent characters for each indentation level. - \note The default indentation is 4 spaces. - */ - PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { - RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); - indentChar_ = indentChar; - indentCharCount_ = indentCharCount; - return *this; - } - - /*! @name Implementation of Handler - \see Handler - */ - //@{ - - bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } - bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } - bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } - bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } - bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } - bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } - bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } - - bool String(const Ch* str, SizeType length, bool copy = false) { - (void)copy; - PrettyPrefix(kStringType); - return Base::WriteString(str, length); - } - - bool StartObject() { - PrettyPrefix(kObjectType); - new (Base::level_stack_.template Push()) typename Base::Level(false); - return Base::WriteStartObject(); - } - - bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } - - bool EndObject(SizeType memberCount = 0) { - (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); - bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; - - if (!empty) { - Base::os_->Put('\n'); - WriteIndent(); - } - if (!Base::WriteEndObject()) - return false; - if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); - return true; - } - - bool StartArray() { - PrettyPrefix(kArrayType); - new (Base::level_stack_.template Push()) typename Base::Level(true); - return Base::WriteStartArray(); - } - - bool EndArray(SizeType memberCount = 0) { - (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); - bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; - - if (!empty) { - Base::os_->Put('\n'); - WriteIndent(); - } - if (!Base::WriteEndArray()) - return false; - if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); - return true; - } - - //@} - - /*! @name Convenience extensions */ - //@{ - - //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } - - //@} -protected: - void PrettyPrefix(Type type) { - (void)type; - if (Base::level_stack_.GetSize() != 0) { // this value is not at root - typename Base::Level* level = Base::level_stack_.template Top(); - - if (level->inArray) { - if (level->valueCount > 0) { - Base::os_->Put(','); // add comma if it is not the first element in array - Base::os_->Put('\n'); - } - else - Base::os_->Put('\n'); - WriteIndent(); - } - else { // in object - if (level->valueCount > 0) { - if (level->valueCount % 2 == 0) { - Base::os_->Put(','); - Base::os_->Put('\n'); - } - else { - Base::os_->Put(':'); - Base::os_->Put(' '); - } - } - else - Base::os_->Put('\n'); - - if (level->valueCount % 2 == 0) - WriteIndent(); - } - if (!level->inArray && level->valueCount % 2 == 0) - RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name - level->valueCount++; - } - else { - RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. - Base::hasRoot_ = true; - } - } - - void WriteIndent() { - size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; - PutN(*Base::os_, indentChar_, count); - } - - Ch indentChar_; - unsigned indentCharCount_; - -private: - // Prohibit copy constructor & assignment operator. - PrettyWriter(const PrettyWriter&); - PrettyWriter& operator=(const PrettyWriter&); -}; - -RAPIDJSON_NAMESPACE_END - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h deleted file mode 100644 index e9bfdba..0000000 --- a/include/rapidjson/rapidjson.h +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_RAPIDJSON_H_ -#define RAPIDJSON_RAPIDJSON_H_ - -// Copyright (c) 2011 Milo Yip (miloyip@gmail.com) -// Version 0.1 - -/*!\file rapidjson.h - \brief common definitions and configuration - - \see RAPIDJSON_CONFIG - */ - -/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration - \brief Configuration macros for library features - - Some RapidJSON features are configurable to adapt the library to a wide - variety of platforms, environments and usage scenarios. Most of the - features can be configured in terms of overriden or predefined - preprocessor macros at compile-time. - - Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. - - \note These macros should be given on the compiler command-line - (where applicable) to avoid inconsistent values when compiling - different translation units of a single application. - */ - -#include // malloc(), realloc(), free(), size_t -#include // memset(), memcpy(), memmove(), memcmp() - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_NAMESPACE_(BEGIN|END) -/*! \def RAPIDJSON_NAMESPACE - \ingroup RAPIDJSON_CONFIG - \brief provide custom rapidjson namespace - - In order to avoid symbol clashes and/or "One Definition Rule" errors - between multiple inclusions of (different versions of) RapidJSON in - a single binary, users can customize the name of the main RapidJSON - namespace. - - In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE - to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple - levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref - RAPIDJSON_NAMESPACE_END need to be defined as well: - - \code - // in some .cpp file - #define RAPIDJSON_NAMESPACE my::rapidjson - #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { - #define RAPIDJSON_NAMESPACE_END } } - #include "rapidjson/..." - \endcode - - \see rapidjson - */ -/*! \def RAPIDJSON_NAMESPACE_BEGIN - \ingroup RAPIDJSON_CONFIG - \brief provide custom rapidjson namespace (opening expression) - \see RAPIDJSON_NAMESPACE -*/ -/*! \def RAPIDJSON_NAMESPACE_END - \ingroup RAPIDJSON_CONFIG - \brief provide custom rapidjson namespace (closing expression) - \see RAPIDJSON_NAMESPACE -*/ -#ifndef RAPIDJSON_NAMESPACE -#define RAPIDJSON_NAMESPACE rapidjson -#endif -#ifndef RAPIDJSON_NAMESPACE_BEGIN -#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { -#endif -#ifndef RAPIDJSON_NAMESPACE_END -#define RAPIDJSON_NAMESPACE_END } -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_NO_INT64DEFINE - -/*! \def RAPIDJSON_NO_INT64DEFINE - \ingroup RAPIDJSON_CONFIG - \brief Use external 64-bit integer types. - - RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types - to be available at global scope. - - If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to - prevent RapidJSON from defining its own types. -*/ -#ifndef RAPIDJSON_NO_INT64DEFINE -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#ifdef _MSC_VER -#include "msinttypes/stdint.h" -#include "msinttypes/inttypes.h" -#else -// Other compilers should have this. -#include -#include -#endif -//!@endcond -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_NO_INT64DEFINE -#endif -#endif // RAPIDJSON_NO_INT64TYPEDEF - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_FORCEINLINE - -#ifndef RAPIDJSON_FORCEINLINE -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#ifdef _MSC_VER -#define RAPIDJSON_FORCEINLINE __forceinline -#elif defined(__GNUC__) && __GNUC__ >= 4 -#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) -#else -#define RAPIDJSON_FORCEINLINE -#endif -//!@endcond -#endif // RAPIDJSON_FORCEINLINE - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ENDIAN -#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine -#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine - -//! Endianness of the machine. -/*! - \def RAPIDJSON_ENDIAN - \ingroup RAPIDJSON_CONFIG - - GCC 4.6 provided macro for detecting endianness of the target machine. But other - compilers may not have this. User can define RAPIDJSON_ENDIAN to either - \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. - - Default detection implemented with reference to - \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html - \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp -*/ -#ifndef RAPIDJSON_ENDIAN -// Detect with GCC 4.6's macro -# ifdef __BYTE_ORDER__ -# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN -# else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. -# endif // __BYTE_ORDER__ -// Detect with GLIBC's endian.h -# elif defined(__GLIBC__) -# include -# if (__BYTE_ORDER == __LITTLE_ENDIAN) -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif (__BYTE_ORDER == __BIG_ENDIAN) -# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN -# else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. -# endif // __GLIBC__ -// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro -# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) -# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN -// Detect with architecture macros -# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) -# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN -# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) -# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(RAPIDJSON_DOXYGEN_RUNNING) -# define RAPIDJSON_ENDIAN -# else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. -# endif -#endif // RAPIDJSON_ENDIAN - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_64BIT - -//! Whether using 64-bit architecture -#ifndef RAPIDJSON_64BIT -#if defined(__LP64__) || defined(_WIN64) -#define RAPIDJSON_64BIT 1 -#else -#define RAPIDJSON_64BIT 0 -#endif -#endif // RAPIDJSON_64BIT - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ALIGN - -//! Data alignment of the machine. -/*! \ingroup RAPIDJSON_CONFIG - \param x pointer to align - - Some machines require strict data alignment. Currently the default uses 4 bytes - alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., -*/ -#ifndef RAPIDJSON_ALIGN -#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_UINT64_C2 - -//! Construct a 64-bit literal by a pair of 32-bit integer. -/*! - 64-bit literal with or without ULL suffix is prone to compiler warnings. - UINT64_C() is C macro which cause compilation problems. - Use this macro to define 64-bit constants by a pair of 32-bit integer. -*/ -#ifndef RAPIDJSON_UINT64_C2 -#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD - -/*! \def RAPIDJSON_SIMD - \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2 optimization. - - RapidJSON supports optimized implementations for some parsing operations - based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible - processors. - - To enable these optimizations, two different symbols can be defined; - \code - // Enable SSE2 optimization. - #define RAPIDJSON_SSE2 - - // Enable SSE4.2 optimization. - #define RAPIDJSON_SSE42 - \endcode - - \c RAPIDJSON_SSE42 takes precedence, if both are defined. - - If any of these symbols is defined, RapidJSON defines the macro - \c RAPIDJSON_SIMD to indicate the availability of the optimized code. -*/ -#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_DOXYGEN_RUNNING) -#define RAPIDJSON_SIMD -#endif - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_NO_SIZETYPEDEFINE - -#ifndef RAPIDJSON_NO_SIZETYPEDEFINE -/*! \def RAPIDJSON_NO_SIZETYPEDEFINE - \ingroup RAPIDJSON_CONFIG - \brief User-provided \c SizeType definition. - - In order to avoid using 32-bit size types for indexing strings and arrays, - define this preprocessor symbol and provide the type rapidjson::SizeType - before including RapidJSON: - \code - #define RAPIDJSON_NO_SIZETYPEDEFINE - namespace rapidjson { typedef ::std::size_t SizeType; } - #include "rapidjson/..." - \endcode - - \see rapidjson::SizeType -*/ -#ifdef RAPIDJSON_DOXYGEN_RUNNING -#define RAPIDJSON_NO_SIZETYPEDEFINE -#endif -RAPIDJSON_NAMESPACE_BEGIN -//! Size type (for string lengths, array sizes, etc.) -/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, - instead of using \c size_t. Users may override the SizeType by defining - \ref RAPIDJSON_NO_SIZETYPEDEFINE. -*/ -typedef unsigned SizeType; -RAPIDJSON_NAMESPACE_END -#endif - -// always import std::size_t to rapidjson namespace -RAPIDJSON_NAMESPACE_BEGIN -using std::size_t; -RAPIDJSON_NAMESPACE_END - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_ASSERT - -//! Assertion. -/*! \ingroup RAPIDJSON_CONFIG - By default, rapidjson uses C \c assert() for internal assertions. - User can override it by defining RAPIDJSON_ASSERT(x) macro. - - \note Parsing errors are handled and can be customized by the - \ref RAPIDJSON_ERRORS APIs. -*/ -#ifndef RAPIDJSON_ASSERT -#include -#define RAPIDJSON_ASSERT(x) assert(x) -#endif // RAPIDJSON_ASSERT - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_STATIC_ASSERT - -// Adopt from boost -#ifndef RAPIDJSON_STATIC_ASSERT -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -RAPIDJSON_NAMESPACE_BEGIN -template struct STATIC_ASSERTION_FAILURE; -template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; -RAPIDJSON_NAMESPACE_END - -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y - -#if defined(__GNUC__) -#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) -#else -#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif -//!@endcond - -/*! \def RAPIDJSON_STATIC_ASSERT - \brief (Internal) macro to check for conditions at compile-time - \param x compile-time condition - \hideinitializer - */ -#define RAPIDJSON_STATIC_ASSERT(x) \ - typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ - sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ - RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Helpers - -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN - -#define RAPIDJSON_MULTILINEMACRO_BEGIN do { -#define RAPIDJSON_MULTILINEMACRO_END \ -} while((void)0, 0) - -// adopted from Boost -#define RAPIDJSON_VERSION_CODE(x,y,z) \ - (((x)*100000) + ((y)*100) + (z)) - -// token stringification -#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) -#define RAPIDJSON_DO_STRINGIFY(x) #x - -/////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF - -#if defined(__GNUC__) -#define RAPIDJSON_GNUC \ - RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) -#endif - -#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) - -#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) -#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) -#define RAPIDJSON_DIAG_OFF(x) \ - RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) - -// push/pop support in Clang and GCC>=4.6 -#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) -#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) -#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) -#else // GCC >= 4.2, < 4.6 -#define RAPIDJSON_DIAG_PUSH /* ignored */ -#define RAPIDJSON_DIAG_POP /* ignored */ -#endif - -#elif defined(_MSC_VER) - -// pragma (MSVC specific) -#define RAPIDJSON_PRAGMA(x) __pragma(x) -#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) - -#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) -#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) -#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) - -#else - -#define RAPIDJSON_DIAG_OFF(x) /* ignored */ -#define RAPIDJSON_DIAG_PUSH /* ignored */ -#define RAPIDJSON_DIAG_POP /* ignored */ - -#endif // RAPIDJSON_DIAG_* - -/////////////////////////////////////////////////////////////////////////////// -// C++11 features - -#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS -#if defined(__clang__) -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) - -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 -#else -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 -#endif -#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - -#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT -#if defined(__clang__) -#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) -// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported -#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 -#else -#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 -#endif -#endif -#if RAPIDJSON_HAS_CXX11_NOEXCEPT -#define RAPIDJSON_NOEXCEPT noexcept -#else -#define RAPIDJSON_NOEXCEPT /* noexcept */ -#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT - -// no automatic detection, yet -#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS -#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 -#endif - -//!@endcond - -/////////////////////////////////////////////////////////////////////////////// -// new/delete - -#ifndef RAPIDJSON_NEW -///! customization point for global \c new -#define RAPIDJSON_NEW(x) new x -#endif -#ifndef RAPIDJSON_DELETE -///! customization point for global \c delete -#define RAPIDJSON_DELETE(x) delete x -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Allocators and Encodings - -#include "allocators.h" -#include "encodings.h" - -/*! \namespace rapidjson - \brief main RapidJSON namespace - \see RAPIDJSON_NAMESPACE -*/ -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// Stream - -/*! \class rapidjson::Stream - \brief Concept for reading and writing characters. - - For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). - - For write-only stream, only need to implement Put() and Flush(). - -\code -concept Stream { - typename Ch; //!< Character type of the stream. - - //! Read the current character from stream without moving the read cursor. - Ch Peek() const; - - //! Read the current character from stream and moving the read cursor to next character. - Ch Take(); - - //! Get the current read cursor. - //! \return Number of characters read from start. - size_t Tell(); - - //! Begin writing operation at the current read pointer. - //! \return The begin writer pointer. - Ch* PutBegin(); - - //! Write a character. - void Put(Ch c); - - //! Flush the buffer. - void Flush(); - - //! End the writing operation. - //! \param begin The begin write pointer returned by PutBegin(). - //! \return Number of characters written. - size_t PutEnd(Ch* begin); -} -\endcode -*/ - -//! Provides additional information for stream. -/*! - By using traits pattern, this type provides a default configuration for stream. - For custom stream, this type can be specialized for other configuration. - See TEST(Reader, CustomStringStream) in readertest.cpp for example. -*/ -template -struct StreamTraits { - //! Whether to make local copy of stream for optimization during parsing. - /*! - By default, for safety, streams do not use local copy optimization. - Stream that can be copied fast should specialize this, like StreamTraits. - */ - enum { copyOptimization = 0 }; -}; - -//! Put N copies of a character to a stream. -template -inline void PutN(Stream& stream, Ch c, size_t n) { - for (size_t i = 0; i < n; i++) - stream.Put(c); -} - -/////////////////////////////////////////////////////////////////////////////// -// StringStream - -//! Read-only string stream. -/*! \note implements Stream concept -*/ -template -struct GenericStringStream { - typedef typename Encoding::Ch Ch; - - GenericStringStream(const Ch *src) : src_(src), head_(src) {} - - Ch Peek() const { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() const { return static_cast(src_ - head_); } - - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } - void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } - size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } - - const Ch* src_; //!< Current read position. - const Ch* head_; //!< Original head of the string. -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! String stream with UTF8 encoding. -typedef GenericStringStream > StringStream; - -/////////////////////////////////////////////////////////////////////////////// -// InsituStringStream - -//! A read-write string stream. -/*! This string stream is particularly designed for in-situ parsing. - \note implements Stream concept -*/ -template -struct GenericInsituStringStream { - typedef typename Encoding::Ch Ch; - - GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} - - // Read - Ch Peek() { return *src_; } - Ch Take() { return *src_++; } - size_t Tell() { return static_cast(src_ - head_); } - - // Write - void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } - - Ch* PutBegin() { return dst_ = src_; } - size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } - void Flush() {} - - Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } - void Pop(size_t count) { dst_ -= count; } - - Ch* src_; - Ch* dst_; - Ch* head_; -}; - -template -struct StreamTraits > { - enum { copyOptimization = 1 }; -}; - -//! Insitu string stream with UTF8 encoding. -typedef GenericInsituStringStream > InsituStringStream; - -/////////////////////////////////////////////////////////////////////////////// -// Type - -//! Type of JSON value -enum Type { - kNullType = 0, //!< null - kFalseType = 1, //!< false - kTrueType = 2, //!< true - kObjectType = 3, //!< object - kArrayType = 4, //!< array - kStringType = 5, //!< string - kNumberType = 6 //!< number -}; - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h deleted file mode 100644 index 05c081d..0000000 --- a/include/rapidjson/reader.h +++ /dev/null @@ -1,1446 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_READER_H_ -#define RAPIDJSON_READER_H_ - -/*! \file reader.h */ - -#include "rapidjson.h" -#include "encodings.h" -#include "internal/meta.h" -#include "internal/stack.h" -#include "internal/strtod.h" - -#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) -#include -#pragma intrinsic(_BitScanForward) -#endif -#ifdef RAPIDJSON_SSE42 -#include -#elif defined(RAPIDJSON_SSE2) -#include -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4702) // unreachable code -#endif - -#ifdef __GNUC__ -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(effc++) -#endif - -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#define RAPIDJSON_NOTHING /* deliberately empty */ -#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN -#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ - RAPIDJSON_MULTILINEMACRO_BEGIN \ - if (HasParseError()) { return value; } \ - RAPIDJSON_MULTILINEMACRO_END -#endif -#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) -//!@endcond - -/*! \def RAPIDJSON_PARSE_ERROR_NORETURN - \ingroup RAPIDJSON_ERRORS - \brief Macro to indicate a parse error. - \param parseErrorCode \ref rapidjson::ParseErrorCode of the error - \param offset position of the error in JSON input (\c size_t) - - This macros can be used as a customization point for the internal - error handling mechanism of RapidJSON. - - A common usage model is to throw an exception instead of requiring the - caller to explicitly check the \ref rapidjson::GenericReader::Parse's - return value: - - \code - #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ - throw ParseException(parseErrorCode, #parseErrorCode, offset) - - #include // std::runtime_error - #include "rapidjson/error/error.h" // rapidjson::ParseResult - - struct ParseException : std::runtime_error, rapidjson::ParseResult { - ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) - : std::runtime_error(msg), ParseResult(code, offset) {} - }; - - #include "rapidjson/reader.h" - \endcode - - \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse - */ -#ifndef RAPIDJSON_PARSE_ERROR_NORETURN -#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ - RAPIDJSON_MULTILINEMACRO_BEGIN \ - RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ - SetParseError(parseErrorCode, offset); \ - RAPIDJSON_MULTILINEMACRO_END -#endif - -/*! \def RAPIDJSON_PARSE_ERROR - \ingroup RAPIDJSON_ERRORS - \brief (Internal) macro to indicate and handle a parse error. - \param parseErrorCode \ref rapidjson::ParseErrorCode of the error - \param offset position of the error in JSON input (\c size_t) - - Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. - - \see RAPIDJSON_PARSE_ERROR_NORETURN - \hideinitializer - */ -#ifndef RAPIDJSON_PARSE_ERROR -#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ - RAPIDJSON_MULTILINEMACRO_BEGIN \ - RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ - RAPIDJSON_MULTILINEMACRO_END -#endif - -#include "error/error.h" // ParseErrorCode, ParseResult - -RAPIDJSON_NAMESPACE_BEGIN - -/////////////////////////////////////////////////////////////////////////////// -// ParseFlag - -/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS - \ingroup RAPIDJSON_CONFIG - \brief User-defined kParseDefaultFlags definition. - - User can define this as any \c ParseFlag combinations. -*/ -#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS -#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags -#endif - -//! Combination of parseFlags -/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream - */ -enum ParseFlag { - kParseNoFlags = 0, //!< No flags are set. - kParseInsituFlag = 1, //!< In-situ(destructive) parsing. - kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. - kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. - kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. - kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). - kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS -}; - -/////////////////////////////////////////////////////////////////////////////// -// Handler - -/*! \class rapidjson::Handler - \brief Concept for receiving events from GenericReader upon parsing. - The functions return true if no error occurs. If they return false, - the event publisher should terminate the process. -\code -concept Handler { - typename Ch; - - bool Null(); - bool Bool(bool b); - bool Int(int i); - bool Uint(unsigned i); - bool Int64(int64_t i); - bool Uint64(uint64_t i); - bool Double(double d); - bool String(const Ch* str, SizeType length, bool copy); - bool StartObject(); - bool Key(const Ch* str, SizeType length, bool copy); - bool EndObject(SizeType memberCount); - bool StartArray(); - bool EndArray(SizeType elementCount); -}; -\endcode -*/ -/////////////////////////////////////////////////////////////////////////////// -// BaseReaderHandler - -//! Default implementation of Handler. -/*! This can be used as base class of any reader handler. - \note implements Handler concept -*/ -template, typename Derived = void> -struct BaseReaderHandler { - typedef typename Encoding::Ch Ch; - - typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; - - bool Default() { return true; } - bool Null() { return static_cast(*this).Default(); } - bool Bool(bool) { return static_cast(*this).Default(); } - bool Int(int) { return static_cast(*this).Default(); } - bool Uint(unsigned) { return static_cast(*this).Default(); } - bool Int64(int64_t) { return static_cast(*this).Default(); } - bool Uint64(uint64_t) { return static_cast(*this).Default(); } - bool Double(double) { return static_cast(*this).Default(); } - bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } - bool StartObject() { return static_cast(*this).Default(); } - bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } - bool EndObject(SizeType) { return static_cast(*this).Default(); } - bool StartArray() { return static_cast(*this).Default(); } - bool EndArray(SizeType) { return static_cast(*this).Default(); } -}; - -/////////////////////////////////////////////////////////////////////////////// -// StreamLocalCopy - -namespace internal { - -template::copyOptimization> -class StreamLocalCopy; - -//! Do copy optimization. -template -class StreamLocalCopy { -public: - StreamLocalCopy(Stream& original) : s(original), original_(original) {} - ~StreamLocalCopy() { original_ = s; } - - Stream s; - -private: - StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; - - Stream& original_; -}; - -//! Keep reference. -template -class StreamLocalCopy { -public: - StreamLocalCopy(Stream& original) : s(original) {} - - Stream& s; - -private: - StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; -}; - -} // namespace internal - -/////////////////////////////////////////////////////////////////////////////// -// SkipWhitespace - -//! Skip the JSON white spaces in a stream. -/*! \param is A input stream for skipping white spaces. - \note This function has SSE2/SSE4.2 specialization. -*/ -template -void SkipWhitespace(InputStream& is) { - internal::StreamLocalCopy copy(is); - InputStream& s(copy.s); - - while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') - s.Take(); -} - -#ifdef RAPIDJSON_SSE42 -//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. -inline const char *SkipWhitespace_SIMD(const char* p) { - // Fast return for single non-whitespace - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); - while (p != nextAligned) - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // The rest of string using SIMD - static const char whitespace[16] = " \n\r\t"; - const __m128i w = _mm_loadu_si128((const __m128i *)&whitespace[0]); - - for (;; p += 16) { - const __m128i s = _mm_load_si128((const __m128i *)p); - const unsigned r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); - if (r != 0) { // some of characters is non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } - } -} - -#elif defined(RAPIDJSON_SSE2) - -//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. -inline const char *SkipWhitespace_SIMD(const char* p) { - // Fast return for single non-whitespace - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // 16-byte align to the next boundary - const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); - while (p != nextAligned) - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') - ++p; - else - return p; - - // The rest of string - static const char whitespaces[4][17] = { - " ", - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"}; - - const __m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]); - const __m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]); - const __m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]); - const __m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]); - - for (;; p += 16) { - const __m128i s = _mm_load_si128((const __m128i *)p); - __m128i x = _mm_cmpeq_epi8(s, w0); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); - x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); - unsigned short r = (unsigned short)~_mm_movemask_epi8(x); - if (r != 0) { // some of characters may be non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } - } -} - -#endif // RAPIDJSON_SSE2 - -#ifdef RAPIDJSON_SIMD -//! Template function specialization for InsituStringStream -template<> inline void SkipWhitespace(InsituStringStream& is) { - is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); -} - -//! Template function specialization for StringStream -template<> inline void SkipWhitespace(StringStream& is) { - is.src_ = SkipWhitespace_SIMD(is.src_); -} -#endif // RAPIDJSON_SIMD - -/////////////////////////////////////////////////////////////////////////////// -// GenericReader - -//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. -/*! GenericReader parses JSON text from a stream, and send events synchronously to an - object implementing Handler concept. - - It needs to allocate a stack for storing a single decoded string during - non-destructive parsing. - - For in-situ parsing, the decoded string is directly written to the source - text string, no temporary buffer is required. - - A GenericReader object can be reused for parsing multiple JSON text. - - \tparam SourceEncoding Encoding of the input stream. - \tparam TargetEncoding Encoding of the parse output. - \tparam StackAllocator Allocator type for stack. -*/ -template -class GenericReader { -public: - typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type - - //! Constructor. - /*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) - \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) - */ - GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} - - //! Parse JSON text. - /*! \tparam parseFlags Combination of \ref ParseFlag. - \tparam InputStream Type of input stream, implementing Stream concept. - \tparam Handler Type of handler, implementing Handler concept. - \param is Input stream to be parsed. - \param handler The handler to receive events. - \return Whether the parsing is successful. - */ - template - ParseResult Parse(InputStream& is, Handler& handler) { - if (parseFlags & kParseIterativeFlag) - return IterativeParse(is, handler); - - parseResult_.Clear(); - - ClearStackOnExit scope(*this); - - SkipWhitespace(is); - - if (is.Peek() == '\0') { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - } - else { - ParseValue(is, handler); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - - if (!(parseFlags & kParseStopWhenDoneFlag)) { - SkipWhitespace(is); - - if (is.Peek() != '\0') { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); - } - } - } - - return parseResult_; - } - - //! Parse JSON text (with \ref kParseDefaultFlags) - /*! \tparam InputStream Type of input stream, implementing Stream concept - \tparam Handler Type of handler, implementing Handler concept. - \param is Input stream to be parsed. - \param handler The handler to receive events. - \return Whether the parsing is successful. - */ - template - ParseResult Parse(InputStream& is, Handler& handler) { - return Parse(is, handler); - } - - //! Whether a parse error has occured in the last parsing. - bool HasParseError() const { return parseResult_.IsError(); } - - //! Get the \ref ParseErrorCode of last parsing. - ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } - - //! Get the position of last parsing error in input, 0 otherwise. - size_t GetErrorOffset() const { return parseResult_.Offset(); } - -protected: - void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } - -private: - // Prohibit copy constructor & assignment operator. - GenericReader(const GenericReader&); - GenericReader& operator=(const GenericReader&); - - void ClearStack() { stack_.Clear(); } - - // clear stack on any exit from ParseStream, e.g. due to exception - struct ClearStackOnExit { - explicit ClearStackOnExit(GenericReader& r) : r_(r) {} - ~ClearStackOnExit() { r_.ClearStack(); } - private: - GenericReader& r_; - ClearStackOnExit(const ClearStackOnExit&); - ClearStackOnExit& operator=(const ClearStackOnExit&); - }; - - // Parse object: { string : value, ... } - template - void ParseObject(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == '{'); - is.Take(); // Skip '{' - - if (!handler.StartObject()) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - - SkipWhitespace(is); - - if (is.Peek() == '}') { - is.Take(); - if (!handler.EndObject(0)) // empty object - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - return; - } - - for (SizeType memberCount = 0;;) { - if (is.Peek() != '"') - RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); - - ParseString(is, handler, true); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - SkipWhitespace(is); - - if (is.Take() != ':') - RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); - - SkipWhitespace(is); - - ParseValue(is, handler); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - SkipWhitespace(is); - - ++memberCount; - - switch (is.Take()) { - case ',': SkipWhitespace(is); break; - case '}': - if (!handler.EndObject(memberCount)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - else - return; - default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); - } - } - } - - // Parse array: [ value, ... ] - template - void ParseArray(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == '['); - is.Take(); // Skip '[' - - if (!handler.StartArray()) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - - SkipWhitespace(is); - - if (is.Peek() == ']') { - is.Take(); - if (!handler.EndArray(0)) // empty array - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - return; - } - - for (SizeType elementCount = 0;;) { - ParseValue(is, handler); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - - ++elementCount; - SkipWhitespace(is); - - switch (is.Take()) { - case ',': SkipWhitespace(is); break; - case ']': - if (!handler.EndArray(elementCount)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - else - return; - default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); - } - } - } - - template - void ParseNull(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == 'n'); - is.Take(); - - if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { - if (!handler.Null()) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); - } - - template - void ParseTrue(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == 't'); - is.Take(); - - if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { - if (!handler.Bool(true)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); - } - - template - void ParseFalse(InputStream& is, Handler& handler) { - RAPIDJSON_ASSERT(is.Peek() == 'f'); - is.Take(); - - if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { - if (!handler.Bool(false)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); - } - - // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). - template - unsigned ParseHex4(InputStream& is) { - unsigned codepoint = 0; - for (int i = 0; i < 4; i++) { - Ch c = is.Take(); - codepoint <<= 4; - codepoint += static_cast(c); - if (c >= '0' && c <= '9') - codepoint -= '0'; - else if (c >= 'A' && c <= 'F') - codepoint -= 'A' - 10; - else if (c >= 'a' && c <= 'f') - codepoint -= 'a' - 10; - else { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); - } - } - return codepoint; - } - - template - class StackStream { - public: - typedef CharType Ch; - - StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} - RAPIDJSON_FORCEINLINE void Put(Ch c) { - *stack_.template Push() = c; - ++length_; - } - size_t Length() const { return length_; } - Ch* Pop() { - return stack_.template Pop(length_); - } - - private: - StackStream(const StackStream&); - StackStream& operator=(const StackStream&); - - internal::Stack& stack_; - SizeType length_; - }; - - // Parse string and generate String event. Different code paths for kParseInsituFlag. - template - void ParseString(InputStream& is, Handler& handler, bool isKey = false) { - internal::StreamLocalCopy copy(is); - InputStream& s(copy.s); - - bool success = false; - if (parseFlags & kParseInsituFlag) { - typename InputStream::Ch *head = s.PutBegin(); - ParseStringToStream(s, s); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - size_t length = s.PutEnd(head) - 1; - RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; - success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); - } - else { - StackStream stackStream(stack_); - ParseStringToStream(s, stackStream); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - SizeType length = static_cast(stackStream.Length()) - 1; - const typename TargetEncoding::Ch* const str = stackStream.Pop(); - success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); - } - if (!success) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); - } - - // Parse string to an output is - // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. - template - RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - static const char escape[256] = { - Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', - Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, - 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, - 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 - }; -#undef Z16 -//!@endcond - - RAPIDJSON_ASSERT(is.Peek() == '\"'); - is.Take(); // Skip '\"' - - for (;;) { - Ch c = is.Peek(); - if (c == '\\') { // Escape - is.Take(); - Ch e = is.Take(); - if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { - os.Put(escape[(unsigned char)e]); - } - else if (e == 'u') { // Unicode - unsigned codepoint = ParseHex4(is); - if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { - // Handle UTF-16 surrogate pair - if (is.Take() != '\\' || is.Take() != 'u') - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); - unsigned codepoint2 = ParseHex4(is); - if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) - RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); - codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; - } - TEncoding::Encode(os, codepoint); - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); - } - else if (c == '"') { // Closing double quote - is.Take(); - os.Put('\0'); // null-terminate the string - return; - } - else if (c == '\0') - RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); - else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); - else { - if (parseFlags & kParseValidateEncodingFlag ? - !Transcoder::Validate(is, os) : - !Transcoder::Transcode(is, os)) - RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); - } - } - } - - template - class NumberStream {}; - - template - class NumberStream { - public: - NumberStream(GenericReader& reader, InputStream& is) : is(is) { (void)reader; } - ~NumberStream() {} - - RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } - RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } - RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } - size_t Tell() { return is.Tell(); } - size_t Length() { return 0; } - const char* Pop() { return 0; } - - protected: - NumberStream& operator=(const NumberStream&); - - InputStream& is; - }; - - template - class NumberStream : public NumberStream { - typedef NumberStream Base; - public: - NumberStream(GenericReader& reader, InputStream& is) : NumberStream(reader, is), stackStream(reader.stack_) {} - ~NumberStream() {} - - RAPIDJSON_FORCEINLINE Ch TakePush() { - stackStream.Put((char)Base::is.Peek()); - return Base::is.Take(); - } - - size_t Length() { return stackStream.Length(); } - - const char* Pop() { - stackStream.Put('\0'); - return stackStream.Pop(); - } - - private: - StackStream stackStream; - }; - - template - void ParseNumber(InputStream& is, Handler& handler) { - internal::StreamLocalCopy copy(is); - NumberStream s(*this, copy.s); - - // Parse minus - bool minus = false; - if (s.Peek() == '-') { - minus = true; - s.Take(); - } - - // Parse int: zero / ( digit1-9 *DIGIT ) - unsigned i = 0; - uint64_t i64 = 0; - bool use64bit = false; - int significandDigit = 0; - if (s.Peek() == '0') { - i = 0; - s.TakePush(); - } - else if (s.Peek() >= '1' && s.Peek() <= '9') { - i = static_cast(s.TakePush() - '0'); - - if (minus) - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i >= 214748364) { // 2^31 = 2147483648 - if (i != 214748364 || s.Peek() > '8') { - i64 = i; - use64bit = true; - break; - } - } - i = i * 10 + static_cast(s.TakePush() - '0'); - significandDigit++; - } - else - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i >= 429496729) { // 2^32 - 1 = 4294967295 - if (i != 429496729 || s.Peek() > '5') { - i64 = i; - use64bit = true; - break; - } - } - i = i * 10 + static_cast(s.TakePush() - '0'); - significandDigit++; - } - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); - - // Parse 64bit int - bool useDouble = false; - double d = 0.0; - if (use64bit) { - if (minus) - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 - if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { - d = i64; - useDouble = true; - break; - } - i64 = i64 * 10 + static_cast(s.TakePush() - '0'); - significandDigit++; - } - else - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 - if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { - d = i64; - useDouble = true; - break; - } - i64 = i64 * 10 + static_cast(s.TakePush() - '0'); - significandDigit++; - } - } - - // Force double for big integer - if (useDouble) { - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); - d = d * 10 + (s.TakePush() - '0'); - } - } - - // Parse frac = decimal-point 1*DIGIT - int expFrac = 0; - size_t decimalPosition; - if (s.Peek() == '.') { - s.Take(); - decimalPosition = s.Length(); - - if (!(s.Peek() >= '0' && s.Peek() <= '9')) - RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); - - if (!useDouble) { -#if RAPIDJSON_64BIT - // Use i64 to store significand in 64-bit architecture - if (!use64bit) - i64 = i; - - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path - break; - else { - i64 = i64 * 10 + static_cast(s.TakePush() - '0'); - --expFrac; - if (i64 != 0) - significandDigit++; - } - } - - d = (double)i64; -#else - // Use double to store significand in 32-bit architecture - d = use64bit ? (double)i64 : (double)i; -#endif - useDouble = true; - } - - while (s.Peek() >= '0' && s.Peek() <= '9') { - if (significandDigit < 17) { - d = d * 10.0 + (s.TakePush() - '0'); - --expFrac; - if (d != 0.0) - significandDigit++; - } - else - s.TakePush(); - } - } - else - decimalPosition = s.Length(); // decimal position at the end of integer. - - // Parse exp = e [ minus / plus ] 1*DIGIT - int exp = 0; - if (s.Peek() == 'e' || s.Peek() == 'E') { - if (!useDouble) { - d = use64bit ? i64 : i; - useDouble = true; - } - s.Take(); - - bool expMinus = false; - if (s.Peek() == '+') - s.Take(); - else if (s.Peek() == '-') { - s.Take(); - expMinus = true; - } - - if (s.Peek() >= '0' && s.Peek() <= '9') { - exp = s.Take() - '0'; - while (s.Peek() >= '0' && s.Peek() <= '9') { - exp = exp * 10 + (s.Take() - '0'); - if (exp > 308 && !expMinus) // exp > 308 should be rare, so it should be checked first. - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); - } - } - else - RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); - - if (expMinus) - exp = -exp; - } - - // Finish parsing, call event according to the type of number. - bool cont = true; - size_t length = s.Length(); - const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. - - if (useDouble) { - int p = exp + expFrac; - if (parseFlags & kParseFullPrecisionFlag) - d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); - else - d = internal::StrtodNormalPrecision(d, p); - - cont = handler.Double(minus ? -d : d); - } - else { - if (use64bit) { - if (minus) - cont = handler.Int64(-(int64_t)i64); - else - cont = handler.Uint64(i64); - } - else { - if (minus) - cont = handler.Int(-(int)i); - else - cont = handler.Uint(i); - } - } - if (!cont) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); - } - - // Parse any JSON value - template - void ParseValue(InputStream& is, Handler& handler) { - switch (is.Peek()) { - case 'n': ParseNull (is, handler); break; - case 't': ParseTrue (is, handler); break; - case 'f': ParseFalse (is, handler); break; - case '"': ParseString(is, handler); break; - case '{': ParseObject(is, handler); break; - case '[': ParseArray (is, handler); break; - default : ParseNumber(is, handler); - } - } - - // Iterative Parsing - - // States - enum IterativeParsingState { - IterativeParsingStartState = 0, - IterativeParsingFinishState, - IterativeParsingErrorState, - - // Object states - IterativeParsingObjectInitialState, - IterativeParsingMemberKeyState, - IterativeParsingKeyValueDelimiterState, - IterativeParsingMemberValueState, - IterativeParsingMemberDelimiterState, - IterativeParsingObjectFinishState, - - // Array states - IterativeParsingArrayInitialState, - IterativeParsingElementState, - IterativeParsingElementDelimiterState, - IterativeParsingArrayFinishState, - - // Single value state - IterativeParsingValueState, - - cIterativeParsingStateCount - }; - - // Tokens - enum Token { - LeftBracketToken = 0, - RightBracketToken, - - LeftCurlyBracketToken, - RightCurlyBracketToken, - - CommaToken, - ColonToken, - - StringToken, - FalseToken, - TrueToken, - NullToken, - NumberToken, - - kTokenCount - }; - - RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { - -//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#define N NumberToken -#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N - // Maps from ASCII to Token - static const unsigned char tokenMap[256] = { - N16, // 00~0F - N16, // 10~1F - N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F - N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F - N16, // 40~4F - N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F - N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F - N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F - N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF - }; -#undef N -#undef N16 -//!@endcond - - if (sizeof(Ch) == 1 || unsigned(c) < 256) - return (Token)tokenMap[(unsigned char)c]; - else - return NumberToken; - } - - RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { - // current state x one lookahead token -> new state - static const char G[cIterativeParsingStateCount][kTokenCount] = { - // Start - { - IterativeParsingArrayInitialState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingValueState, // String - IterativeParsingValueState, // False - IterativeParsingValueState, // True - IterativeParsingValueState, // Null - IterativeParsingValueState // Number - }, - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // ObjectInitial - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // MemberKey - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingKeyValueDelimiterState, // Colon - IterativeParsingErrorState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, - // MemberValue - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingMemberDelimiterState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingErrorState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // ObjectFinish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // ArrayInitial - { - IterativeParsingArrayInitialState, // Left bracket(push Element state) - IterativeParsingArrayFinishState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push Element state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingElementState, // String - IterativeParsingElementState, // False - IterativeParsingElementState, // True - IterativeParsingElementState, // Null - IterativeParsingElementState // Number - }, - // Element - { - IterativeParsingErrorState, // Left bracket - IterativeParsingArrayFinishState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingErrorState, // Right curly bracket - IterativeParsingElementDelimiterState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingErrorState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, - // ElementDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push Element state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push Element state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingElementState, // String - IterativeParsingElementState, // False - IterativeParsingElementState, // True - IterativeParsingElementState, // Null - IterativeParsingElementState // Number - }, - // ArrayFinish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Single Value (sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - } - }; // End of G - - return (IterativeParsingState)G[state][token]; - } - - // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). - // May return a new state on state pop. - template - RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { - switch (dst) { - case IterativeParsingStartState: - RAPIDJSON_ASSERT(false); - return IterativeParsingErrorState; - - case IterativeParsingFinishState: - return dst; - - case IterativeParsingErrorState: - return dst; - - case IterativeParsingObjectInitialState: - case IterativeParsingArrayInitialState: - { - // Push the state(Element or MemeberValue) if we are nested in another array or value of member. - // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. - IterativeParsingState n = src; - if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) - n = IterativeParsingElementState; - else if (src == IterativeParsingKeyValueDelimiterState) - n = IterativeParsingMemberValueState; - // Push current state. - *stack_.template Push(1) = n; - // Initialize and push the member/element count. - *stack_.template Push(1) = 0; - // Call handler - bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); - // On handler short circuits the parsing. - if (!hr) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); - return IterativeParsingErrorState; - } - else { - is.Take(); - return dst; - } - } - - case IterativeParsingMemberKeyState: - ParseString(is, handler, true); - if (HasParseError()) - return IterativeParsingErrorState; - else - return dst; - - case IterativeParsingKeyValueDelimiterState: - if (token == ColonToken) { - is.Take(); - return dst; - } - else - return IterativeParsingErrorState; - - case IterativeParsingMemberValueState: - // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. - ParseValue(is, handler); - if (HasParseError()) { - return IterativeParsingErrorState; - } - return dst; - - case IterativeParsingElementState: - // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. - ParseValue(is, handler); - if (HasParseError()) { - return IterativeParsingErrorState; - } - return dst; - - case IterativeParsingMemberDelimiterState: - case IterativeParsingElementDelimiterState: - is.Take(); - // Update member/element count. - *stack_.template Top() = *stack_.template Top() + 1; - return dst; - - case IterativeParsingObjectFinishState: - { - // Get member count. - SizeType c = *stack_.template Pop(1); - // If the object is not empty, count the last member. - if (src == IterativeParsingMemberValueState) - ++c; - // Restore the state. - IterativeParsingState n = static_cast(*stack_.template Pop(1)); - // Transit to Finish state if this is the topmost scope. - if (n == IterativeParsingStartState) - n = IterativeParsingFinishState; - // Call handler - bool hr = handler.EndObject(c); - // On handler short circuits the parsing. - if (!hr) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); - return IterativeParsingErrorState; - } - else { - is.Take(); - return n; - } - } - - case IterativeParsingArrayFinishState: - { - // Get element count. - SizeType c = *stack_.template Pop(1); - // If the array is not empty, count the last element. - if (src == IterativeParsingElementState) - ++c; - // Restore the state. - IterativeParsingState n = static_cast(*stack_.template Pop(1)); - // Transit to Finish state if this is the topmost scope. - if (n == IterativeParsingStartState) - n = IterativeParsingFinishState; - // Call handler - bool hr = handler.EndArray(c); - // On handler short circuits the parsing. - if (!hr) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); - return IterativeParsingErrorState; - } - else { - is.Take(); - return n; - } - } - - case IterativeParsingValueState: - // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. - ParseValue(is, handler); - if (HasParseError()) { - return IterativeParsingErrorState; - } - return IterativeParsingFinishState; - - default: - RAPIDJSON_ASSERT(false); - return IterativeParsingErrorState; - } - } - - template - void HandleError(IterativeParsingState src, InputStream& is) { - if (HasParseError()) { - // Error flag has been set. - return; - } - - switch (src) { - case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); - case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); - case IterativeParsingObjectInitialState: - case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); - case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); - case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); - case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); - default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); - } - } - - template - ParseResult IterativeParse(InputStream& is, Handler& handler) { - parseResult_.Clear(); - ClearStackOnExit scope(*this); - IterativeParsingState state = IterativeParsingStartState; - - SkipWhitespace(is); - while (is.Peek() != '\0') { - Token t = Tokenize(is.Peek()); - IterativeParsingState n = Predict(state, t); - IterativeParsingState d = Transit(state, t, n, is, handler); - - if (d == IterativeParsingErrorState) { - HandleError(state, is); - break; - } - - state = d; - - // Do not further consume streams if a root JSON has been parsed. - if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) - break; - - SkipWhitespace(is); - } - - // Handle the end of file. - if (state != IterativeParsingFinishState) - HandleError(state, is); - - return parseResult_; - } - - static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. - internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. - ParseResult parseResult_; -}; // class GenericReader - -//! Reader with UTF8 encoding and default allocator. -typedef GenericReader, UTF8<> > Reader; - -RAPIDJSON_NAMESPACE_END - -#ifdef __GNUC__ -RAPIDJSON_DIAG_POP -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_READER_H_ diff --git a/include/rapidjson/stringbuffer.h b/include/rapidjson/stringbuffer.h deleted file mode 100644 index 009a518..0000000 --- a/include/rapidjson/stringbuffer.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_STRINGBUFFER_H_ -#define RAPIDJSON_STRINGBUFFER_H_ - -#include "rapidjson.h" - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS -#include // std::move -#endif - -#include "internal/stack.h" - -RAPIDJSON_NAMESPACE_BEGIN - -//! Represents an in-memory output stream. -/*! - \tparam Encoding Encoding of the stream. - \tparam Allocator type for allocating memory buffer. - \note implements Stream concept -*/ -template -class GenericStringBuffer { -public: - typedef typename Encoding::Ch Ch; - - GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} - -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} - GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { - if (&rhs != this) - stack_ = std::move(rhs.stack_); - return *this; - } -#endif - - void Put(Ch c) { *stack_.template Push() = c; } - void Flush() {} - - void Clear() { stack_.Clear(); } - void ShrinkToFit() { - // Push and pop a null terminator. This is safe. - *stack_.template Push() = '\0'; - stack_.ShrinkToFit(); - stack_.template Pop(1); - } - Ch* Push(size_t count) { return stack_.template Push(count); } - void Pop(size_t count) { stack_.template Pop(count); } - - const Ch* GetString() const { - // Push and pop a null terminator. This is safe. - *stack_.template Push() = '\0'; - stack_.template Pop(1); - - return stack_.template Bottom(); - } - - size_t GetSize() const { return stack_.GetSize(); } - - static const size_t kDefaultCapacity = 256; - mutable internal::Stack stack_; - -private: - // Prohibit copy constructor & assignment operator. - GenericStringBuffer(const GenericStringBuffer&); - GenericStringBuffer& operator=(const GenericStringBuffer&); -}; - -//! String buffer with UTF8 encoding -typedef GenericStringBuffer > StringBuffer; - -//! Implement specialized version of PutN() with memset() for better performance. -template<> -inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { - std::memset(stream.stack_.Push(n), c, n * sizeof(c)); -} - -RAPIDJSON_NAMESPACE_END - -#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h deleted file mode 100644 index 6daa783..0000000 --- a/include/rapidjson/writer.h +++ /dev/null @@ -1,391 +0,0 @@ -// Copyright (C) 2011 Milo Yip -// -// 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 RAPIDJSON_WRITER_H_ -#define RAPIDJSON_WRITER_H_ - -#include "rapidjson.h" -#include "internal/stack.h" -#include "internal/strfunc.h" -#include "internal/dtoa.h" -#include "internal/itoa.h" -#include "stringbuffer.h" -#include // placement new - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -#endif - -RAPIDJSON_NAMESPACE_BEGIN - -//! JSON writer -/*! Writer implements the concept Handler. - It generates JSON text by events to an output os. - - User may programmatically calls the functions of a writer to generate JSON text. - - On the other side, a writer can also be passed to objects that generates events, - - for example Reader::Parse() and Document::Accept(). - - \tparam OutputStream Type of output stream. - \tparam SourceEncoding Encoding of source string. - \tparam TargetEncoding Encoding of output stream. - \tparam StackAllocator Type of allocator for allocating memory of stack. - \note implements Handler concept -*/ -template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> -class Writer { -public: - typedef typename SourceEncoding::Ch Ch; - - //! Constructor - /*! \param os Output stream. - \param stackAllocator User supplied allocator. If it is null, it will create a private one. - \param levelDepth Initial capacity of stack. - */ - explicit - Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {} - - explicit - Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : - os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} - - //! Reset the writer with a new stream. - /*! - This function reset the writer with a new stream and default settings, - in order to make a Writer object reusable for output multiple JSONs. - - \param os New output stream. - \code - Writer writer(os1); - writer.StartObject(); - // ... - writer.EndObject(); - - writer.Reset(os2); - writer.StartObject(); - // ... - writer.EndObject(); - \endcode - */ - void Reset(OutputStream& os) { - os_ = &os; - hasRoot_ = false; - level_stack_.Clear(); - } - - //! Checks whether the output is a complete JSON. - /*! - A complete JSON has a complete root object or array. - */ - bool IsComplete() const { - return hasRoot_ && level_stack_.Empty(); - } - - /*!@name Implementation of Handler - \see Handler - */ - //@{ - - bool Null() { Prefix(kNullType); return WriteNull(); } - bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } - bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } - bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } - bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } - bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } - - //! Writes the given \c double value to the stream - /*! - \param d The value to be written. - \return Whether it is succeed. - */ - bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } - - bool String(const Ch* str, SizeType length, bool copy = false) { - (void)copy; - Prefix(kStringType); - return WriteString(str, length); - } - - bool StartObject() { - Prefix(kObjectType); - new (level_stack_.template Push()) Level(false); - return WriteStartObject(); - } - - bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } - - bool EndObject(SizeType memberCount = 0) { - (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); - level_stack_.template Pop(1); - bool ret = WriteEndObject(); - if (level_stack_.Empty()) // end of json text - os_->Flush(); - return ret; - } - - bool StartArray() { - Prefix(kArrayType); - new (level_stack_.template Push()) Level(true); - return WriteStartArray(); - } - - bool EndArray(SizeType elementCount = 0) { - (void)elementCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); - level_stack_.template Pop(1); - bool ret = WriteEndArray(); - if (level_stack_.Empty()) // end of json text - os_->Flush(); - return ret; - } - //@} - - /*! @name Convenience extensions */ - //@{ - - //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } - - //@} - -protected: - //! Information for each nested level - struct Level { - Level(bool inArray_) : valueCount(0), inArray(inArray_) {} - size_t valueCount; //!< number of values in this level - bool inArray; //!< true if in array, otherwise in object - }; - - static const size_t kDefaultLevelDepth = 32; - - bool WriteNull() { - os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; - } - - bool WriteBool(bool b) { - if (b) { - os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); - } - else { - os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); - } - return true; - } - - bool WriteInt(int i) { - char buffer[11]; - const char* end = internal::i32toa(i, buffer); - for (const char* p = buffer; p != end; ++p) - os_->Put(*p); - return true; - } - - bool WriteUint(unsigned u) { - char buffer[10]; - const char* end = internal::u32toa(u, buffer); - for (const char* p = buffer; p != end; ++p) - os_->Put(*p); - return true; - } - - bool WriteInt64(int64_t i64) { - char buffer[21]; - const char* end = internal::i64toa(i64, buffer); - for (const char* p = buffer; p != end; ++p) - os_->Put(*p); - return true; - } - - bool WriteUint64(uint64_t u64) { - char buffer[20]; - char* end = internal::u64toa(u64, buffer); - for (char* p = buffer; p != end; ++p) - os_->Put(*p); - return true; - } - - bool WriteDouble(double d) { - char buffer[25]; - char* end = internal::dtoa(d, buffer); - for (char* p = buffer; p != end; ++p) - os_->Put(*p); - return true; - } - - bool WriteString(const Ch* str, SizeType length) { - static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - static const char escape[256] = { -#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - //0 1 2 3 4 5 6 7 8 9 A B C D E F - 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 - 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 - 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 - Z16, Z16, // 30~4F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 - Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF -#undef Z16 - }; - - os_->Put('\"'); - GenericStringStream is(str); - while (is.Tell() < length) { - const Ch c = is.Peek(); - if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { - // Unicode escaping - unsigned codepoint; - if (!SourceEncoding::Decode(is, &codepoint)) - return false; - os_->Put('\\'); - os_->Put('u'); - if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { - os_->Put(hexDigits[(codepoint >> 12) & 15]); - os_->Put(hexDigits[(codepoint >> 8) & 15]); - os_->Put(hexDigits[(codepoint >> 4) & 15]); - os_->Put(hexDigits[(codepoint ) & 15]); - } - else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) { - // Surrogate pair - unsigned s = codepoint - 0x010000; - unsigned lead = (s >> 10) + 0xD800; - unsigned trail = (s & 0x3FF) + 0xDC00; - os_->Put(hexDigits[(lead >> 12) & 15]); - os_->Put(hexDigits[(lead >> 8) & 15]); - os_->Put(hexDigits[(lead >> 4) & 15]); - os_->Put(hexDigits[(lead ) & 15]); - os_->Put('\\'); - os_->Put('u'); - os_->Put(hexDigits[(trail >> 12) & 15]); - os_->Put(hexDigits[(trail >> 8) & 15]); - os_->Put(hexDigits[(trail >> 4) & 15]); - os_->Put(hexDigits[(trail ) & 15]); - } - else - return false; // invalid code point - } - else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { - is.Take(); - os_->Put('\\'); - os_->Put(escape[(unsigned char)c]); - if (escape[(unsigned char)c] == 'u') { - os_->Put('0'); - os_->Put('0'); - os_->Put(hexDigits[(unsigned char)c >> 4]); - os_->Put(hexDigits[(unsigned char)c & 0xF]); - } - } - else - Transcoder::Transcode(is, *os_); - } - os_->Put('\"'); - return true; - } - - bool WriteStartObject() { os_->Put('{'); return true; } - bool WriteEndObject() { os_->Put('}'); return true; } - bool WriteStartArray() { os_->Put('['); return true; } - bool WriteEndArray() { os_->Put(']'); return true; } - - void Prefix(Type type) { - (void)type; - if (level_stack_.GetSize() != 0) { // this value is not at root - Level* level = level_stack_.template Top(); - if (level->valueCount > 0) { - if (level->inArray) - os_->Put(','); // add comma if it is not the first element in array - else // in object - os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); - } - if (!level->inArray && level->valueCount % 2 == 0) - RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name - level->valueCount++; - } - else { - RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. - hasRoot_ = true; - } - } - - OutputStream* os_; - internal::Stack level_stack_; - bool hasRoot_; - -private: - // Prohibit copy constructor & assignment operator. - Writer(const Writer&); - Writer& operator=(const Writer&); -}; - -// Full specialization for StringStream to prevent memory copying - -template<> -inline bool Writer::WriteInt(int i) { - char *buffer = os_->Push(11); - const char* end = internal::i32toa(i, buffer); - os_->Pop(11 - (end - buffer)); - return true; -} - -template<> -inline bool Writer::WriteUint(unsigned u) { - char *buffer = os_->Push(10); - const char* end = internal::u32toa(u, buffer); - os_->Pop(10 - (end - buffer)); - return true; -} - -template<> -inline bool Writer::WriteInt64(int64_t i64) { - char *buffer = os_->Push(21); - const char* end = internal::i64toa(i64, buffer); - os_->Pop(21 - (end - buffer)); - return true; -} - -template<> -inline bool Writer::WriteUint64(uint64_t u) { - char *buffer = os_->Push(20); - const char* end = internal::u64toa(u, buffer); - os_->Pop(20 - (end - buffer)); - return true; -} - -template<> -inline bool Writer::WriteDouble(double d) { - char *buffer = os_->Push(25); - char* end = internal::dtoa(d, buffer); - os_->Pop(25 - (end - buffer)); - return true; -} - -RAPIDJSON_NAMESPACE_END - -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - -#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/server/ClientsManager.h b/include/server/ClientsManager.h deleted file mode 100644 index 4eaabf5..0000000 --- a/include/server/ClientsManager.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include -#include -#include - -struct ClientInfo -{ - ClientInfo(boost::asio::io_service& io) : connection(io) {} - TcpHandler connection; -}; - -class ClientsManager -{ -public: - ClientsManager(boost::asio::io_service& io); - ~ClientsManager(); - - // Start a new thread to listen to and accept clients - void StartListening(const tcp::endpoint &localEndpoint); - // Start an infinite loop to process each client - void StartProcessing(); - // Get the connected clients - std::vector& GetClients() { return m_clients; } - -private: - boost::asio::io_service &m_ioService; - TcpAcceptor m_acceptor; - TcpRequest m_requests; - boost::mutex m_mutex; - - // Clients List - std::vector m_clients; - std::unordered_map> m_groups; // map groupId to a list of id's of clients - // each client id is index of m_clients vector - - // Handle a newly connected client - void HandleClient(boost::shared_ptr &socket); - // Process each client - void ProcessClients(); - - // Test Method to receive chat messages on GROUP_CHAT request - void ReceiveChat(unsigned int client, unsigned int group); -}; \ No newline at end of file diff --git a/makefile_client b/makefile_client deleted file mode 100644 index cc0c2d9..0000000 --- a/makefile_client +++ /dev/null @@ -1,28 +0,0 @@ -INC_DIR = include -SRC_DIR = src - -CFLAG = `pkg-config --cflags gtk+-3.0` -CFLAGS = --std=c++11 - -CC = g++ - -#for common -SOURCES_COMM = TcpHandler.cpp TcpListener.cpp RequestHandler.cpp ChatMessage.cpp -HEADERS_COMM = TcpHandler.h TcpListner.h RequestHandler.h -OBJECTS_COMM = $(SOURCES_COMM:.cpp=.o) -FSOURCES_COMM := $(addprefix $(SRC_DIR)/common/, $(SOURCES_COMM)) -FHEADERS_COMM := $(addprefix $(INC_DIR)/common/, $(HEADERS_COMM)) - -LDFLAGS_COMM = -lboost_system -lboost_thread -lpthread - -all: client.o common - $(CC) -o output $(SRC_DIR)/client/main.cpp *.o -Iinclude/ $(CFLAGS) $(CFLAG) $(LDFLAGS_COMM) - -common: $(OBJECTS_COMM) - $(CC) -c -o common.o $< - -%.o : src/common/%.cpp - $(CC) -c -o $@ $< $(LDFLAGS_COMM) -Iinclude/ $(CFLAG) $(CFLAGS) - -client.o: src/client/TcpClient.cpp - $(CC) -c -o $@ $< -Iinclude/ $(CFLAG) $(CFLAGS) diff --git a/output b/output deleted file mode 100755 index 9d592d5b7f796ecd59cc2ff7dacd377146290601..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 408519 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 -#include - -ChatMessage::ChatMessage() -{} - -ChatMessage::~ChatMessage() -{} - -void ChatMessage::Send(TcpHandler &tcpHandler) -{ - tcpHandler.Send(m_body.c_str(), m_body.size() + 1); -} - -void ChatMessage::Receive(TcpHandler &tcpHandler, uint32_t size) -{ - char * data = new char[size]; - tcpHandler.Receive(data, size); - m_body = std::string(data); - delete[] data; -} \ No newline at end of file diff --git a/src/common/Control.cpp b/src/common/Control.cpp deleted file mode 100644 index 9ef74c9..0000000 --- a/src/common/Control.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "common/Control.h" - -void Control::PutFixedAt(GtkWidget *fixed, int x, int y) -{ - -} \ No newline at end of file diff --git a/src/common/Page.cpp b/src/common/Page.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/common/RtpHandler.cpp b/src/common/RtpHandler.cpp deleted file mode 100644 index 80a331f..0000000 --- a/src/common/RtpHandler.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -RtpHandler::RtpHandler() -{ -} - -RtpHandler::~RtpHandler() -{ - CleanUp(); -} - -void RtpHandler::Initialize(boost::shared_ptr socket, const udp::endpoint &remoteEndpoint) -{ - m_socket = socket; - m_remoteEndpoint = remoteEndpoint; - m_sequenceNumber = m_timeStamp = 0; - m_timeStampIncrement = 3600; // for 25fps video -} - -void RtpHandler::CleanUp() -{ - m_socket.reset(); - m_remoteEndpoint = udp::endpoint(); -} - -void RtpHandler::Send(const char *data, size_t size) -{ - if (!m_socket) - throw RtpTransmissionException("RTP Transmitter hasn't been properly initialized"); - - std::vector packet; - packet.resize(RTP_HEADER_SIZE); - packet[0] = (char)0x80; // RTP version - packet[1] = m_payloadType; // Payload type - packet[2] = m_sequenceNumber >> 8; // Sequence number of packet - packet[3] = m_sequenceNumber & 0x0FF; - packet[4] = (m_timeStamp & 0xFF000000) >> 24; // Timestamp - packet[5] = (m_timeStamp & 0x00FF0000) >> 16; - packet[6] = (m_timeStamp & 0x0000FF00) >> 8; - packet[7] = (m_timeStamp & 0x000000FF); - packet[8] = (char)0x13; // 4 byte SSRC (sychronization source identifier) - packet[9] = (char)0xf9; // we just an arbitrary number here to keep it simple - packet[10] = (char)0x7e; - packet[11] = (char)0x67; - - for (unsigned int i = 0; i < size; ++i) - packet[RTP_HEADER_SIZE + i] = data[i]; - - m_sequenceNumber++; - m_timeStamp += m_timeStampIncrement; - - m_socket->send_to(boost::asio::buffer(packet), m_remoteEndpoint); -} - - -void RtpHandler::Receive(char* data, size_t maxSize) -{ - if (!m_socket) - throw RtpReceptionError("RTP Receiver hasn't been properly initialized"); - - std::vector packet; - packet.resize(maxSize); - size_t len = m_socket->receive_from(boost::asio::buffer(packet), m_remoteEndpoint); - packet.resize(len); - - char version = packet[0]; - m_payloadType = packet[1]; - m_sequenceNumber = *((uint16_t*)&packet[2]); - m_timeStamp = *((int*)&packet[4]); - int SSRC = *((int*)&packet[8]); - - for (unsigned int i = 0; i < len - RTP_HEADER_SIZE; ++i) - data[i] = packet[i + RTP_HEADER_SIZE]; -} \ No newline at end of file diff --git a/src/common/TcpAcceptor.cpp b/src/common/TcpAcceptor.cpp deleted file mode 100644 index c4d51af..0000000 --- a/src/common/TcpAcceptor.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include - -TcpAcceptor::TcpAcceptor(boost::asio::io_service &io) : m_acceptor(io) -{} - -TcpAcceptor::~TcpAcceptor() -{} - -uint16_t TcpAcceptor::Initialize(const tcp::endpoint &localEndpoint) -{ - // Open and bind the tcp acceptor to the local endpoint - m_acceptor.open(localEndpoint.protocol()); - //m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - m_acceptor.bind(localEndpoint); - // Prepare to listen - m_acceptor.listen(); - return m_acceptor.local_endpoint().port(); -} - -void TcpAcceptor::Listen(boost::function)> callback) -{ - m_callback = callback; - StartListening(); -} - -// Create new thread to listen for incomming connections -void TcpAcceptor::StartListening() -{ - boost::thread t(boost::bind(&TcpAcceptor::ListeningThread, this)); -} - -void TcpAcceptor::ListeningThread() -{ - try - { - while (true) - { - // Create a new socket to represent a new connection - boost::shared_ptr socket(new tcp::socket(m_acceptor.get_io_service())); - // Wait for a connection and accept at the socket - m_acceptor.accept(*socket); - // Send the socket to the handler - m_callback(socket); - } - } - catch (std::exception &ex) - { - std::cout << ex.what() << std::endl; - } -} \ No newline at end of file diff --git a/src/common/TcpRequest.cpp b/src/common/TcpRequest.cpp deleted file mode 100644 index d4432ec..0000000 --- a/src/common/TcpRequest.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include -#include -#include "rapidjson/document.h" -#include "rapidjson/writer.h" -#include "rapidjson/stringbuffer.h" -using namespace rapidjson; - -// Requests are of fixed size (defined by REQUEST_MAX_SIZE) -// This makes it simpler to receive just the required request data -// from the socket and neither more nor less -const size_t REQUEST_MAX_SIZE = 150; - -TcpRequest::TcpRequest() -{} - -TcpRequest::~TcpRequest() -{} - -std::string TcpRequest::GetJsonString() -{ - StringBuffer buffer; - Writer writer(buffer); - m_document.Accept(writer); - return buffer.GetString(); -} - -void TcpRequest::JoinChat(TcpHandler &tcpHandler, uint32_t groupId) -{ - m_document = Document(); // New Document - /* - Example: - { - Request-Type: 1 - Group-Id: 20 - } - */ - m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)JOIN_CHAT), m_document.GetAllocator()); - m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); -} - -void TcpRequest::ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId) -{ - m_document = Document(); // New Document - /* - Example: - { - Request-Type: 2 - Group-Id: 20 - Message-Size: 50 - } - */ - m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)CHAT_MESSAGE), m_document.GetAllocator()); - m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); - m_document.AddMember("Message-Size", Value(messageSize), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); -} - -void TcpRequest::P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp, uint16_t publicPort) -{ - m_document = Document(); // New Document - /* - Example: - { - Request-Type: 3 - Client-Id: 32 - Private-IP: 1.2.3.4 - Private-Port: 1234 - Public-IP: 2.3.5.6 - Public-Port: 2356 - } - */ - m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)P2P_TCP), m_document.GetAllocator()); - m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); - m_document.AddMember("Private-IP", Value(privateIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); - m_document.AddMember("Private-Port", Value((unsigned int)privatePort), m_document.GetAllocator()); - m_document.AddMember("Public-IP", Value(publicIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); - m_document.AddMember("Public-Port", Value((unsigned int)publicPort), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); -} - - -void TcpRequest::ReceiveRequest(TcpHandler &tcpHandler) -{ - // Get the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - tcpHandler.Receive(request, REQUEST_MAX_SIZE); - - // Parse the request - m_document = Document(); - m_document.Parse(request); -} - -TcpRequest::REQUEST_TYPE TcpRequest::GetRequestType() -{ - auto value = m_document.FindMember("Request-Type"); - if (value == m_document.MemberEnd() || !value->value.IsInt()) - return INAVLID_TYPE; - return (REQUEST_TYPE)value->value.GetInt(); -} - -inline Value& TcpRequest::GetValue(const std::string &key) -{ - static Value sval; - auto value = m_document.FindMember(key.c_str()); - if (value == m_document.MemberEnd()) - throw RequestException("Invalid value for '" + key + "' received in the request"); - sval = value->value; - return sval; -} - -uint32_t TcpRequest::GetGroupId() -{ - return GetValue("Group-Id").GetUint(); -} - -uint32_t TcpRequest::GetMessageSize() -{ - return GetValue("Message-Size").GetUint();; -} - -uint32_t TcpRequest::GetClientId() -{ - return GetValue("Client-Id").GetUint(); -} - -std::string TcpRequest::GetPrivateIp() -{ - return GetValue("Private-IP").GetString(); -} - -uint16_t TcpRequest::GetPrivatePort() -{ - return GetValue("Private-Port").GetUint(); -} - -std::string TcpRequest::GetPublicIp() -{ - return GetValue("Public-IP").GetString(); -} - -uint16_t TcpRequest::GetPublicPort() -{ - return GetValue("Public-Port").GetUint(); -} \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp deleted file mode 100644 index 97e4fa7..0000000 --- a/src/server/ClientsManager.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include - -ClientsManager::ClientsManager(boost::asio::io_service& io) -: m_acceptor(io), m_ioService(io) -{} - -ClientsManager::~ClientsManager() -{} - -// Use listener to listen to incoming clients -void ClientsManager::StartListening(const tcp::endpoint &localEndpoint) -{ - m_acceptor.Initialize(localEndpoint); - m_acceptor.Listen(boost::bind(&ClientsManager::HandleClient, this, _1)); -} - -// Add any client that is connected to the clients list -void ClientsManager::HandleClient(boost::shared_ptr &socket) -{ - boost::lock_guard guard(m_mutex); - ClientInfo client(m_ioService); - client.connection.Initialize(socket); - m_clients.push_back(client); - std::cout << "Client #" << m_clients.size() - 1 << " Connected: " << m_clients[m_clients.size() - 1].connection.GetDestinationAddress() << std::endl; -} - -// Start processing each client -void ClientsManager::StartProcessing() -{ - //boost::thread t(boost::bind(&ClientsManager::ProcessClients, this)); - ProcessClients(); -} - -// Process each client -void ClientsManager::ProcessClients() -{ - while (true) - { - // Sleep for some time if no client is available - while (m_clients.size() == 0) - boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - - // Lock the clients-list while processing the clients - m_mutex.lock(); - // Process each client in turn - for (unsigned int i = 0; i < m_clients.size(); ++i) - { - try - { - uint32_t id; - // See if any request is incomming for this client - size_t bytes = m_clients[i].connection.Available(); - if (bytes > 0) - { - // if so receive the request and process accordingly - m_requests.ReceiveRequest(m_clients[i].connection); - - switch (m_requests.GetRequestType()) - { - // Request to join chat, since this is the sever, this is a request to join a group chat - case TcpRequest::JOIN_CHAT: - id = m_requests.GetGroupId(); - // push the client id to the group - m_groups[id].push_back(i); - std::cout << "Connected client #" << i << " to group #" << id << std::endl; - break; - // Request to receive an incoming chat message - case TcpRequest::CHAT_MESSAGE: - id = m_requests.GetGroupId(); - // receive the chat message - ReceiveChat(i, id); - break; - - // Request for a p2p tcp connection to another client - case TcpRequest::P2P_TCP: - id = m_requests.GetClientId(); - if (id <= m_clients.size()) - { - // Send request to second client - m_requests.P2PTcp(m_clients[id].connection, i, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), - m_clients[i].connection.GetIp(), m_clients[i].connection.GetPort()); - // Receive the return request - m_requests.ReceiveRequest(m_clients[id].connection); - // Send back the return request to first client - m_requests.P2PTcp(m_clients[i].connection, id, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), - m_clients[id].connection.GetIp(), m_clients[id].connection.GetPort()); - } - break; - - default: - std::cout << "Invalid Request " << m_requests.GetRequestType() << " from client #" << i << std::endl; - } - } - } - catch (std::exception &ex) - { - std::cout << ex.what() << std::endl; - } - } - m_mutex.unlock(); - } -} - -void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) -{ - ChatMessage chat; - chat.Receive(m_clients[client].connection, m_requests.GetMessageSize()); - //m_mutex.lock(); - - // Send the messsage to each client in the group - for (unsigned int i = 0; i < m_groups[group].size(); ++i) - { - try - { - if (m_groups[group][i] != client) - { - m_requests.ChatMessage(m_clients[i].connection, chat.GetMessage().size() + 1); - chat.Send(m_clients[m_groups[group][i]].connection); - } - } - // client may be disconnected and exception may be thrown - // we catch the exception inside the loop so that - // we can continue sending messages to other clients - catch (std::exception &ex) - { - std::cout << ex.what() << std::endl; - } - } - //m_mutex.unlock(); -} \ No newline at end of file From 80e04e6f53161d74c392c8a42ff7a7afaa916bcd Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Thu, 25 Dec 2014 21:17:41 +0545 Subject: [PATCH 44/54] Revert "okie what is this!!! zzz" This reverts commit 599dd15bf8beae9cbe61e708cdff698b018f687a. --- .gitignore | 1 + include/client/FrameRenderer.h | 2 +- include/client/TcpClient.h | 55 +- include/client/UI/Control.h | 31 - include/client/VideoCapture.h | 12 +- include/{client/UI => common}/Button.h | 4 +- include/common/ChatMessage.h | 19 + include/common/Control.h | 19 + include/common/Page.h | 16 + include/common/RtpHandler.h | 51 + include/common/TcpAcceptor.h | 23 + include/common/TcpRequest.h | 51 + include/rapidjson/allocators.h | 245 +++ include/rapidjson/document.h | 1927 +++++++++++++++++++++++ include/rapidjson/encodedstream.h | 290 ++++ include/rapidjson/encodings.h | 630 ++++++++ include/rapidjson/error/en.h | 71 + include/rapidjson/error/error.h | 150 ++ include/rapidjson/filereadstream.h | 94 ++ include/rapidjson/filestream.h | 73 + include/rapidjson/filewritestream.h | 97 ++ include/rapidjson/internal/biginteger.h | 290 ++++ include/rapidjson/internal/diyfp.h | 268 ++++ include/rapidjson/internal/dtoa.h | 225 +++ include/rapidjson/internal/ieee754.h | 90 ++ include/rapidjson/internal/itoa.h | 306 ++++ include/rapidjson/internal/meta.h | 189 +++ include/rapidjson/internal/pow10.h | 59 + include/rapidjson/internal/stack.h | 183 +++ include/rapidjson/internal/strfunc.h | 43 + include/rapidjson/internal/strtod.h | 285 ++++ include/rapidjson/memorybuffer.h | 76 + include/rapidjson/memorystream.h | 67 + include/rapidjson/msinttypes/inttypes.h | 312 ++++ include/rapidjson/msinttypes/stdint.h | 296 ++++ include/rapidjson/prettywriter.h | 205 +++ include/rapidjson/rapidjson.h | 628 ++++++++ include/rapidjson/reader.h | 1446 +++++++++++++++++ include/rapidjson/stringbuffer.h | 99 ++ include/rapidjson/writer.h | 391 +++++ include/server/ClientsManager.h | 43 + makefile_client | 28 + output | Bin 0 -> 408519 bytes src/common/Button.cpp | 0 src/common/ChatMessage.cpp | 21 + src/common/Control.cpp | 6 + src/common/Page.cpp | 0 src/common/RtpHandler.cpp | 75 + src/common/TcpAcceptor.cpp | 51 + src/common/TcpRequest.cpp | 160 ++ src/server/ClientsManager.cpp | 132 ++ 51 files changed, 9789 insertions(+), 46 deletions(-) delete mode 100644 include/client/UI/Control.h rename include/{client/UI => common}/Button.h (50%) create mode 100644 include/common/ChatMessage.h create mode 100644 include/common/Control.h create mode 100644 include/common/Page.h create mode 100644 include/common/RtpHandler.h create mode 100644 include/common/TcpAcceptor.h create mode 100644 include/common/TcpRequest.h create mode 100644 include/rapidjson/allocators.h create mode 100644 include/rapidjson/document.h create mode 100644 include/rapidjson/encodedstream.h create mode 100644 include/rapidjson/encodings.h create mode 100644 include/rapidjson/error/en.h create mode 100644 include/rapidjson/error/error.h create mode 100644 include/rapidjson/filereadstream.h create mode 100644 include/rapidjson/filestream.h create mode 100644 include/rapidjson/filewritestream.h create mode 100644 include/rapidjson/internal/biginteger.h create mode 100644 include/rapidjson/internal/diyfp.h create mode 100644 include/rapidjson/internal/dtoa.h create mode 100644 include/rapidjson/internal/ieee754.h create mode 100644 include/rapidjson/internal/itoa.h create mode 100644 include/rapidjson/internal/meta.h create mode 100644 include/rapidjson/internal/pow10.h create mode 100644 include/rapidjson/internal/stack.h create mode 100644 include/rapidjson/internal/strfunc.h create mode 100644 include/rapidjson/internal/strtod.h create mode 100644 include/rapidjson/memorybuffer.h create mode 100644 include/rapidjson/memorystream.h create mode 100644 include/rapidjson/msinttypes/inttypes.h create mode 100644 include/rapidjson/msinttypes/stdint.h create mode 100644 include/rapidjson/prettywriter.h create mode 100644 include/rapidjson/rapidjson.h create mode 100644 include/rapidjson/reader.h create mode 100644 include/rapidjson/stringbuffer.h create mode 100644 include/rapidjson/writer.h create mode 100644 include/server/ClientsManager.h create mode 100644 makefile_client create mode 100755 output create mode 100644 src/common/Button.cpp create mode 100644 src/common/ChatMessage.cpp create mode 100644 src/common/Control.cpp create mode 100644 src/common/Page.cpp create mode 100644 src/common/RtpHandler.cpp create mode 100644 src/common/TcpAcceptor.cpp create mode 100644 src/common/TcpRequest.cpp create mode 100644 src/server/ClientsManager.cpp diff --git a/.gitignore b/.gitignore index 8fb39aa..fe70f20 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,4 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store +/MSVC_2013/Mirror-Client diff --git a/include/client/FrameRenderer.h b/include/client/FrameRenderer.h index 02c1017..0ab2834 100644 --- a/include/client/FrameRenderer.h +++ b/include/client/FrameRenderer.h @@ -9,7 +9,7 @@ class FrameRenderer public: FrameRenderer() :m_parentWindow(0), m_drawingArea(0), m_rgbData(0), m_rgbImage(0){} void SetRGBData(unsigned char* rgbData); - void Initialize(GtkWidget* parentWindow, GtkWidget* fixed, int x, int y, int fw, int fh); + void Initialize(GtkWidget* parentWindow, int x, int y, int fw, int fh); static gboolean OnDraw(GtkWidget* widget, cairo_t* cr, gpointer frPointer); private: GtkWidget* m_parentWindow; diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 9f01737..67c962a 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -1,5 +1,8 @@ #pragma once -#include +#include +#include +#include + class TcpClient { @@ -7,11 +10,55 @@ class TcpClient TcpClient(boost::asio::io_service &io); ~TcpClient(); - void StartListening(const tcp::endpoint& server); + // Connect to a peer/server + void Connect(const tcp::endpoint& peer, bool joinChat = false); + // Connect to a peer/server asynchronously + void ConnectAsync(const tcp::endpoint& peer, bool joinChat = false); + // Connect to a peer through server + void Connect(uint32_t clientId); + // Start handling all requests that the client gets + void HandleRequests(); + + // Join chat + void JoinChat(uint32_t connectionId, uint32_t groupId); + // Set username to use while sending messages + void SetName(const std::string &name) { m_name = name; } + + // For console: + // Start Chat Input in a separate thread + void StartChatInput(uint32_t groupId); private: + // Connection representing a tcp-connection with a peer/server + struct Connection + { + Connection(boost::asio::io_service &io) + : tcpHandler(io) + {} + TcpHandler tcpHandler; + // maybe store userid and other stuffs here... + }; + boost::asio::io_service &m_io; - TcpListener m_listener; - void ListenerHandler(boost::shared_ptr socket); + // List of the connections + std::vector m_connections; + // Mutex to lock 'm_connections' list as well as 'm_request' request-processor + boost::mutex m_mutex; + + TcpRequest m_request; + std::string m_name; + + // For P2P: + bool m_p2pConnected; + void HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); + void HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); + boost::shared_ptr m_acceptor; + void P2PListen(const tcp::endpoint &localEndpoint); + void P2PConnect(tcp::endpoint &remoteEndpoint); + + // For console: + int m_currentConnection; + // Separate thread for inputting chat messages + void ChatInput(uint32_t groupId); }; \ No newline at end of file diff --git a/include/client/UI/Control.h b/include/client/UI/Control.h deleted file mode 100644 index 2c13638..0000000 --- a/include/client/UI/Control.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef __CONTROL__ -#define __CONTROL__ - -#include -#include - -enum CONTROL{ BUTTON, LABEL, TEXTEDIT, STATUSBAR}; - -// base class for UI control elements -class Control -{ -public: - - Control() :m_handle(0){} - void SetID(int id); - int GetID(); - void PutFixedAt(GtkWidget *fixed, int x, int y); - void PutFixedRelativeTo(Control* obj, GtkWidget* fixed, int xOffset, int yOffset); - void Show(); - void Hide(); - GtkWidget* GetHandle(); - // void SetEventHandler(); -protected: - GtkWidget* m_handle; - int m_x, m_y, m_w, m_h; - std::string m_label; - int m_type; - int m_id; -}; - -#endif \ No newline at end of file diff --git a/include/client/VideoCapture.h b/include/client/VideoCapture.h index 7c6a4e2..625cb07 100644 --- a/include/client/VideoCapture.h +++ b/include/client/VideoCapture.h @@ -12,28 +12,24 @@ class VideoCaptureDevice }; - +/* class VideoCapture { public: - VideoCapture():m_fw(0), m_fh(0), m_fps(0){} + VideoCapture():m_fw(0), m_fh(0){} std::vector Enumerate(); - void CaptureFrame(); unsigned char* GetBGRAFrame(); int GetFrameWidth(); int GetFrameHeight(); void SetFrameWidth(); void SetFrameHeight(); void Initialize(int deviceID = 0); - double GetFPS(); - ~VideoCapture(); private: cv::VideoCapture m_captureDevice; - cv::VideoWriter m_videoWriter; cv::Mat m_capturedFrame; int m_fw, m_fh; - double m_fps; }; -#endif \ No newline at end of file +*/ +#endif diff --git a/include/client/UI/Button.h b/include/common/Button.h similarity index 50% rename from include/client/UI/Button.h rename to include/common/Button.h index aac23a9..d8de677 100644 --- a/include/client/UI/Button.h +++ b/include/common/Button.h @@ -1,13 +1,13 @@ #ifndef __BUTTON__ #define __BUTTON__ -#include "client/UI/Control.h" +#include "Control.h" class Button : public Control { public: Button(); - void Set(std::string label, GtkWidget* fixed, int x, int y, int w, int h); + Button(std::string label, int x, int y, int w, int h); private: }; diff --git a/include/common/ChatMessage.h b/include/common/ChatMessage.h new file mode 100644 index 0000000..39bec04 --- /dev/null +++ b/include/common/ChatMessage.h @@ -0,0 +1,19 @@ +#pragma once +#include "TcpHandler.h" + +// This class is used to send and receive chat messages with header through a TcpHandler +class ChatMessage +{ +public: + ChatMessage(); + ~ChatMessage(); + + const std::string& GetMessage() const { return m_body; } + void SetMessage(const std::string& message) { m_body = message; } + + void Send(TcpHandler &tcpHandler); + void Receive(TcpHandler &tcpHandler, uint32_t size); + +private:; + std::string m_body; +}; \ No newline at end of file diff --git a/include/common/Control.h b/include/common/Control.h new file mode 100644 index 0000000..cb70a2a --- /dev/null +++ b/include/common/Control.h @@ -0,0 +1,19 @@ +#ifndef __CONTROL__ +#define __CONTROL__ + +#include +#include + +class Control +{ +public: + Control() :m_handle(0){} + void PutFixedAt(GtkWidget *fixed, int x, int y); + void PutFixedRelativeTo(GtkWidget* fixed, int xOffset, int yOffset); +protected: + GtkWidget* m_handle; + int m_x, m_y, m_w, m_h; + std::string m_label; +}; + +#endif \ No newline at end of file diff --git a/include/common/Page.h b/include/common/Page.h new file mode 100644 index 0000000..2b29682 --- /dev/null +++ b/include/common/Page.h @@ -0,0 +1,16 @@ +#ifndef __PAGE__ +#define __PAGE__ + +#include "Control.h" + +#include +#include + +class Page +{ +public: +protected: + std::vector m_controls; +}; + +#endif \ No newline at end of file diff --git a/include/common/RtpHandler.h b/include/common/RtpHandler.h new file mode 100644 index 0000000..9246ce3 --- /dev/null +++ b/include/common/RtpHandler.h @@ -0,0 +1,51 @@ +#pragma once + +class RtpTransmissionException : public Exception +{ +public: + RtpTransmissionException(const std::string &errorString) + : Exception("RTP Transmission Error: " + errorString) + {} +}; +class RtpReceptionError : public Exception +{ +public: + RtpReceptionError(std::string errorString) + : Exception("RTP Reception Error: " + errorString) + {} +}; + +const size_t RTP_HEADER_SIZE = 12; +class RtpHandler +{ +public: + RtpHandler(); + ~RtpHandler(); + + void Initialize(boost::shared_ptr socket, const udp::endpoint &remoteEndpoint); + void CleanUp(); + + int GetTimeStamp() const { return m_timeStamp; } + void SetTimeStamp(int timeStamp) { m_timeStamp = timeStamp; } + + int GetTimeStampIncrement() const { return m_timeStampIncrement; } + void SetTimeStampIncrement(int timeStampIncrement) { m_timeStampIncrement = timeStampIncrement; } + + uint16_t GetSequenceNumber() const { return m_sequenceNumber; } + void SetSequenceNumber(uint16_t sequenceNumber) { m_sequenceNumber = sequenceNumber; } + + uint8_t GetPayloadType() const { return m_payloadType; } + void SetPayloadType(uint8_t payloadType) { m_payloadType = payloadType; } + + const udp::endpoint& GetRemoteEndpoint() const { return m_remoteEndpoint; } + + void Send(const char *data, size_t size); + void Receive(char* data, size_t maxSize = 1500); +private: + boost::shared_ptr m_socket; + udp::endpoint m_remoteEndpoint; + + uint16_t m_sequenceNumber; + int m_timeStamp, m_timeStampIncrement; + uint8_t m_payloadType; +}; \ No newline at end of file diff --git a/include/common/TcpAcceptor.h b/include/common/TcpAcceptor.h new file mode 100644 index 0000000..981f114 --- /dev/null +++ b/include/common/TcpAcceptor.h @@ -0,0 +1,23 @@ +#pragma once + +class TcpAcceptor +{ +public: + TcpAcceptor(boost::asio::io_service &io); + ~TcpAcceptor(); + + // Initialize the acceptor to listen at given local endpoint + uint16_t Initialize(const tcp::endpoint &localEndpoint); + // Listen for incomming connections in a new thread + // and call 'callback' for each connection + void Listen(boost::function)> callback); + + tcp::acceptor &GetSocket() { return m_acceptor; } + +private: + tcp::acceptor m_acceptor; + boost::function)> m_callback; + + void StartListening(); + void ListeningThread(); +}; \ No newline at end of file diff --git a/include/common/TcpRequest.h b/include/common/TcpRequest.h new file mode 100644 index 0000000..d21c375 --- /dev/null +++ b/include/common/TcpRequest.h @@ -0,0 +1,51 @@ +#pragma once +#include "TcpHandler.h" +#include "rapidjson/document.h" + +class RequestException : public Exception +{ +public: + RequestException(const std::string &errorString) + :Exception("Error processing request: "+errorString) + {} +}; +class TcpRequest +{ +public: + enum REQUEST_TYPE{ + INAVLID_TYPE = 0, + JOIN_CHAT, CHAT_MESSAGE, + P2P_TCP, + }; + + TcpRequest(); + ~TcpRequest(); + + // Request to join a chat + void JoinChat(TcpHandler &tcpHandler, uint32_t groupId = 0); + // Request to wait for incoming chat message + void ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId = 0); + // Request to establish a P2P TCP connection with a client + // Contains private and public addess-port pairs and client-id + void P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp = "", uint16_t publicPort = 0); + + // Receive a request + void ReceiveRequest(TcpHandler &tcpHandler); + + // Request Data + REQUEST_TYPE GetRequestType(); + uint32_t GetGroupId(); + uint32_t GetMessageSize(); + uint32_t GetClientId(); + std::string GetPrivateIp(); + uint16_t GetPrivatePort(); + std::string GetPublicIp(); + uint16_t GetPublicPort(); +private: + // The Json Document that holds the request + rapidjson::Document m_document; + // Get string from the Json Doucment + std::string GetJsonString(); + // Helper function get a json value from a request + inline rapidjson::Value& GetValue(const std::string &key); +}; diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h new file mode 100644 index 0000000..0bd2d28 --- /dev/null +++ b/include/rapidjson/allocators.h @@ -0,0 +1,245 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { return std::malloc(size); } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return std::realloc(originalPtr, newSize); } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while(chunkHead_ != 0 && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); + + void *buffer = reinterpret_cast(chunkHead_ + 1) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + increment = RAPIDJSON_ALIGN(increment); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + void* newBuffer = Malloc(newSize); + RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. + return std::memcpy(newBuffer, originalPtr, originalSize); + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + */ + void AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h new file mode 100644 index 0000000..07e3fe3 --- /dev/null +++ b/include/rapidjson/document.h @@ -0,0 +1,1927 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#include +#endif // RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::iterator, std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +struct GenericMember { + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator + : public std::iterator >::Type> { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef std::iterator BaseType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + //! Pointer to (const) GenericMember + typedef typename BaseType::pointer Pointer; + //! Reference to (const) GenericMember + typedef typename BaseType::reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef typename BaseType::difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } + bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } + bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } + bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } + bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } + bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +struct GenericMemberIterator; + +//! non-const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } + + //! Create constant string reference from pointer and length + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow copy-assignment + GenericStringRef operator=(const GenericStringRef&); + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; +}; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template > +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { + rhs.flags_ = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { + static const unsigned defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kConstStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_ASSERT(type <= kNumberType); + flags_ = defaultFlags[type]; + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \see CopyFrom() + */ + template< typename SourceAllocator > + GenericValue(const GenericValue& rhs, Allocator & allocator); + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { + data_.n.i64 = i; + if (i >= 0) + flags_ |= kUintFlag | kUint64Flag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { + data_.n.u64 = u; + if (!(u & 0x80000000)) + flags_ |= kIntFlag | kInt64Flag; + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { + data_.n.i64 = i64; + if (i64 >= 0) { + flags_ |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { + data_.n.u64 = u64; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + flags_ |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(flags_) { + case kArrayFlag: + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(data_.a.elements); + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(data_.o.members); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(data_.s.str)); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + RAPIDJSON_ASSERT(this != &rhs); + this->~GenericValue(); + RawAssign(rhs); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) + return GetDouble() == rhs.GetDouble(); // May convert one operand from integer to double. + else + return data_.n.u64 == rhs.data_.n.u64; + + default: // kTrueType, kFalseType, kNullType + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(flags_ & kTypeMask); } + bool IsNull() const { return flags_ == kNullFlag; } + bool IsFalse() const { return flags_ == kFalseFlag; } + bool IsTrue() const { return flags_ == kTrueFlag; } + bool IsBool() const { return (flags_ & kBoolFlag) != 0; } + bool IsObject() const { return flags_ == kObjectFlag; } + bool IsArray() const { return flags_ == kArrayFlag; } + bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } + bool IsInt() const { return (flags_ & kIntFlag) != 0; } + bool IsUint() const { return (flags_ & kUintFlag) != 0; } + bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } + bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } + bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } + bool IsString() const { return (flags_ & kStringFlag) != 0; } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + static GenericValue NullValue; + return NullValue; + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + Object& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); + } + } + o.members[o.size].name.RawAssign(name); + o.members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + GenericValue v(value); + return AddMember(n, v, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(data_.o.members + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) { + // Move the last one to this place + *m = *last; + } + else { + // Only one left, just destroy + m->~Member(); + } + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member)); + data_.o.size -= (last - first); + return pos; + } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + for (SizeType i = 0; i < data_.a.size; ++i) + data_.a.elements[i].~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return data_.a.elements[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + data_.a.elements[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + data_.a.elements[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(data_.a.elements != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(pos, last, (End() - last) * sizeof(GenericValue)); + data_.a.size -= (last - first); + return pos; + } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } + + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision) + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), s.size(), allocator); } +#endif + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (!handler.StartObject()) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) + return false; + if (!m->value.Accept(handler)) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (!handler.StartArray()) + return false; + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + if (!v->Accept(handler)) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); + + case kNumberType: + if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else if (IsUint64()) return handler.Uint64(data_.n.u64); + else return handler.Double(data_.n.d); + + default: + RAPIDJSON_ASSERT(false); + } + return false; + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x100, + kNumberFlag = 0x200, + kIntFlag = 0x400, + kUintFlag = 0x800, + kInt64Flag = 0x1000, + kUint64Flag = 0x2000, + kDoubleFlag = 0x4000, + kStringFlag = 0x100000, + kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, + + // Initial flags of different types. + kNullFlag = kNullType, + kTrueFlag = kTrueType | kBoolFlag, + kFalseFlag = kFalseType | kBoolFlag, + kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, + kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, + kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, + kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, + kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, + kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, + kConstStringFlag = kStringType | kStringFlag, + kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct String { + const Ch* str; + SizeType length; + unsigned hashcode; //!< reserved + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode + // inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } + inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct Object { + Member* members; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct Array { + GenericValue* elements; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + Object o; + Array a; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + flags_ = kArrayFlag; + data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); + std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + flags_ = kObjectFlag; + data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); + std::memcpy(data_.o.members, members, count * sizeof(Member)); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + flags_ = kConstStringFlag; + data_.s.str = s; + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = NULL; + if(ShortString::Usable(s.length)) { + flags_ = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + flags_ = kCopyStringFlag; + data_.s.length = s.length; + str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); + data_.s.str = str; + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + flags_ = rhs.flags_; + rhs.flags_ = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; + unsigned flags_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template , typename StackAllocator = CrtAllocator> +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::move(rhs)), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + ValueType::SetNull(); // Remove existing root if exist + GenericReader reader(&GetAllocator()); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Transcoding from input Encoding + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { return *allocator_; } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + +// defined here due to the dependency on GenericDocument +template +template +inline +GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop(1)); + } + break; + case kStringType: + if (rhs.flags_ == kConstStringFlag) { + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: // kNumberType, kTrueType, kFalseType, kNullType + flags_ = rhs.flags_; + data_ = *reinterpret_cast(&rhs.data_); + } +} + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h new file mode 100644 index 0000000..ee8caa0 --- /dev/null +++ b/include/rapidjson/encodedstream.h @@ -0,0 +1,290 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); } + Ch Take() { RAPIDJSON_ASSERT(false); } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = (const unsigned char *)is_->Peek4(); + if (!c) + return; + + unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + switch (type_) { + case kUTF8: + // Do nothing + break; + case kUTF16LE: + case kUTF16BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + break; + case kUTF32LE: + case kUTF32BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + break; + default: + RAPIDJSON_ASSERT(false); // Invalid type + } + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam InputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. + switch (type_) { + case kUTF16LE: + case kUTF16BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + break; + case kUTF32LE: + case kUTF32BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + break; + case kUTF8: + // Do nothing + break; + default: + RAPIDJSON_ASSERT(false); // Invalid UTFType + } + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); } + Ch Take() { RAPIDJSON_ASSERT(false); } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h new file mode 100644 index 0000000..71595f7 --- /dev/null +++ b/include/rapidjson/encodings.h @@ -0,0 +1,630 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = (unsigned char)c; + return true; + } + + unsigned char type = GetRange((unsigned char)c); + *codepoint = (0xFF >> type) & (unsigned char)c; + bool result = true; + switch (type) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define COPY() os.Put(c = is.Take()) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange((unsigned char)c)) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + Ch c = Take(is); + if ((unsigned char)c != 0xEFu) return c; + c = is.Take(); + if ((unsigned char)c != 0xBBu) return c; + c = is.Take(); + if ((unsigned char)c != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return is.Take(); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put((v & 0x3FF) | 0xDC00); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = c; + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (c & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (c & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + Ch c; + os.Put(c = is.Take()); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned short)c == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFFu); os.Put(0xFEu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned short)c == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFEu); os.Put(0xFFu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put((c >> 8) & 0xFFu); + os.Put(c & 0xFFu); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 24; + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 24) & 0xFFu); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take() << 24; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put((c >> 24) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put(c & 0xFFu); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + unsigned char c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + unsigned char c = is.Take(); + os.Put(c); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + Ch c = Take(is); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return is.Take(); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSV_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h new file mode 100644 index 0000000..0171183 --- /dev/null +++ b/include/rapidjson/error/en.h @@ -0,0 +1,71 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ERROR_EN_H__ +#define RAPIDJSON_ERROR_EN_H__ + +#include "error.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: + return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ERROR_EN_H__ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h new file mode 100644 index 0000000..161ef87 --- /dev/null +++ b/include/rapidjson/error/error.h @@ -0,0 +1,150 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ERROR_ERROR_H__ +#define RAPIDJSON_ERROR_ERROR_H__ + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError, //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ERROR_ERROR_H__ diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h new file mode 100644 index 0000000..5af9be5 --- /dev/null +++ b/include/rapidjson/filereadstream.h @@ -0,0 +1,94 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "rapidjson.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filestream.h b/include/rapidjson/filestream.h new file mode 100644 index 0000000..a370c60 --- /dev/null +++ b/include/rapidjson/filestream.h @@ -0,0 +1,73 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_FILESTREAM_H_ +#define RAPIDJSON_FILESTREAM_H_ + +#include "rapidjson.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN + +//! (Deprecated) Wrapper of C file stream for input or output. +/*! + This simple wrapper does not check the validity of the stream. + \note implements Stream concept + \note deprecated: This was only for basic testing in version 0.1, it is found that the performance is very low by using fgetc(). Use FileReadStream instead. +*/ +class FileStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileStream(std::FILE* fp) : fp_(fp), current_('\0'), count_(0) { Read(); } + char Peek() const { return current_; } + char Take() { char c = current_; Read(); return c; } + size_t Tell() const { return count_; } + void Put(char c) { fputc(c, fp_); } + void Flush() { fflush(fp_); } + + // Not implemented + char* PutBegin() { return 0; } + size_t PutEnd(char*) { return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileStream(const FileStream&); + FileStream& operator=(const FileStream&); + + void Read() { + RAPIDJSON_ASSERT(fp_ != 0); + int c = fgetc(fp_); + if (c != EOF) { + current_ = (char)c; + count_++; + } + else if (current_ != '\0') + current_ = '\0'; + } + + std::FILE* fp_; + char current_; + size_t count_; +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filewritestream.h b/include/rapidjson/filewritestream.h new file mode 100644 index 0000000..4352c8f --- /dev/null +++ b/include/rapidjson/filewritestream.h @@ -0,0 +1,97 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "rapidjson.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h new file mode 100644 index 0000000..06a442b --- /dev/null +++ b/include/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint32_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= 1220703125u; // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Return false if this < rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + if (cmp == 0) { + *out = BigInteger(0); + return false; + } + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10 + (*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + unsigned __int128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = p >> 64; + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static Type FullAdd(Type a, Type b, bool inCarry, bool* outCarry) { + Type c = a + b + (inCarry ? 1 : 0); + *outCarry = c < a; + return c; + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h new file mode 100644 index 0000000..174b9fa --- /dev/null +++ b/include/rapidjson/internal/diyfp.h @@ -0,0 +1,268 @@ +// Copyright (C) 2011 Milo Yip +// +// 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. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#if defined(_MSC_VER) +#include +#if defined(_M_AMD64) +#pragma intrinsic(_BitScanReverse64) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +struct DiyFp { + DiyFp() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + unsigned __int128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = p >> 64; + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) && __GNUC__ >= 4 + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & (static_cast(1) << 63))) { + res.f <<= 1; + res.e--; + } + return res; +#endif + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + uint64_t significand = f; + int exponent = e; + while (significand > kDpHiddenBit + kDpSignificandMask) { + significand >>= 1; + exponent++; + } + while (exponent > kDpDenormalExponent && (significand & kDpHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + if (exponent >= kDpMaxExponent) { + u.u64 = kDpExponentMask; // Infinity + return u.d; + } + else if (exponent < kDpDenormalExponent) + return 0.0; + const uint64_t be = (exponent == kDpDenormalExponent && (significand & kDpHiddenBit) == 0) ? 0 : + static_cast(exponent + kDpExponentBias); + u.u64 = (significand & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (k != dk) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + unsigned index = (exp + 348) / 8; + *outExp = -348 + index * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h new file mode 100644 index 0000000..c0fa2b8 --- /dev/null +++ b/include/rapidjson/internal/dtoa.h @@ -0,0 +1,225 @@ +// Copyright (C) 2011 Milo Yip +// +// 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. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + if (n < 1000000000) return 9; + return 10; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); + *len = 0; + + while (kappa > 0) { + uint32_t d; + switch (kappa) { + case 10: d = p1 / 1000000000; p1 %= 1000000000; break; + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default: +#if defined(_MSC_VER) + __assume(0); +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + __builtin_unreachable(); +#else + d = 0; +#endif + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (length <= kk && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], length - kk); + buffer[kk] = '.'; + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], length); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + return &buffer[length + offset]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], length - 1); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer) { + if (value == 0) { + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h new file mode 100644 index 0000000..ab65cc9 --- /dev/null +++ b/include/rapidjson/internal/ieee754.h @@ -0,0 +1,90 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d(d) {} + Double(uint64_t u) : u(u) {} + + double Value() const { return d; } + uint64_t Uint64Value() const { return u; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u + 1).Value(); + } + + double PreviousPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + if (d == 0.0) + return 0.0; + else + return Double(u - 1).Value(); + } + + bool Sign() const { return (u & kSignMask) != 0; } + uint64_t Significand() const { return u & kSignificandMask; } + int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; } + + bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNormal() const { return (u & kExponentMask) != 0 || Significand() == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; } + + static unsigned EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d; + uint64_t u; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h new file mode 100644 index 0000000..3684f07 --- /dev/null +++ b/include/rapidjson/internal/itoa.h @@ -0,0 +1,306 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + + return u32toa(static_cast(value), buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + if (value >= kTen8) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + + return u64toa(static_cast(value), buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h new file mode 100644 index 0000000..c33f607 --- /dev/null +++ b/include/rapidjson/internal/meta.h @@ -0,0 +1,189 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#error not yet included. Do not include this file directly. +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/include/rapidjson/internal/pow10.h b/include/rapidjson/internal/pow10.h new file mode 100644 index 0000000..91cf647 --- /dev/null +++ b/include/rapidjson/internal/pow10.h @@ -0,0 +1,59 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h new file mode 100644 index 0000000..62ae7aa --- /dev/null +++ b/include/rapidjson/internal/stack.h @@ -0,0 +1,183 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + RAPIDJSON_ASSERT(stackCapacity > 0); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + // Expand the stack if needed + if (stackTop_ + sizeof(T) * count >= stackEnd_) + Expand(count); + + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* Bottom() { return (T*)stack_; } + + Allocator& GetAllocator() { return *allocator_; } + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STACK_H_ diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h new file mode 100644 index 0000000..734adc3 --- /dev/null +++ b/include/rapidjson/internal/strfunc.h @@ -0,0 +1,43 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h new file mode 100644 index 0000000..1fc6050 --- /dev/null +++ b/include/rapidjson/internal/strtod.h @@ -0,0 +1,285 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "../rapidjson.h" +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adjustToNegative) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2; + + BigInteger bS(bInt); + bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2; + + BigInteger hS(1); + hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; + + BigInteger delta(0); + *adjustToNegative = dS.Difference(bS, &delta); + + int cmp = delta.Compare(hS); + // If delta is within 1/2 ULP, check for special case when significand is power of two. + // In this case, need to compare with 1/2h in the lower bound. + if (cmp < 0 && *adjustToNegative && // within and dS < bS + db.IsNormal() && (bInt & (bInt - 1)) == 0 && // Power of 2 + db.Uint64Value() != RAPIDJSON_UINT64_C2(0x00100000, 0x00000000)) // minimum normal number must not do this + { + delta <<= 1; + return delta.Compare(hS); + } + return cmp; +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { + uint64_t significand = 0; + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < length; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10 + (decimals[i] - '0'); + } + + if (i < length && decimals[i] >= '5') // Rounding + significand++; + + size_t remaining = length - i; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = (int)decimalPosition - (int)i + exp; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp - 1; + RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); + v = v * kPow10[adjustment]; + if (length + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + unsigned scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + error) + rounded.f++; + + *result = rounded.ToDouble(); + + return halfWay - error >= precisionBits || precisionBits >= halfWay + error; +} + +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { + const BigInteger dInt(decimals, length); + const int dExp = (int)decimalPosition - (int)length + exp; + Double a(approx); + for (int i = 0; i < 10; i++) { + bool adjustToNegative; + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp, &adjustToNegative); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + a = adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); + } + + // This should not happen, but in case there is really a bug, break the infinite-loop + return a.Value(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result; + if (StrtodFast(d, p, &result)) + return result; + + // Trim leading zeros + while (*decimals == '0' && length > 1) { + length--; + decimals++; + decimalPosition--; + } + + // Trim trailing zeros + while (decimals[length - 1] == '0' && length > 1) { + length--; + decimalPosition--; + exp++; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 780; + if ((int)length > kMaxDecimalDigit) { + exp += (int(length) - kMaxDecimalDigit); + length = kMaxDecimalDigit; + } + + // If too small, underflow to zero + if (int(length) + exp < -324) + return 0.0; + + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/include/rapidjson/memorybuffer.h b/include/rapidjson/memorybuffer.h new file mode 100644 index 0000000..95c68a3 --- /dev/null +++ b/include/rapidjson/memorybuffer.h @@ -0,0 +1,76 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "rapidjson.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/memorystream.h b/include/rapidjson/memorystream.h new file mode 100644 index 0000000..f994a12 --- /dev/null +++ b/include/rapidjson/memorystream.h @@ -0,0 +1,67 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return (src_ == end_) ? '\0' : *src_; } + Ch Take() { return (src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/msinttypes/inttypes.h b/include/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000..af713c9 --- /dev/null +++ b/include/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,312 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/include/rapidjson/msinttypes/stdint.h b/include/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000..bbad95a --- /dev/null +++ b/include/rapidjson/msinttypes/stdint.h @@ -0,0 +1,296 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h new file mode 100644 index 0000000..ce2dac5 --- /dev/null +++ b/include/rapidjson/prettywriter.h @@ -0,0 +1,205 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of ouptut os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kStringType); + return Base::WriteString(str, length); + } + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + if (!Base::WriteEndObject()) + return false; + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + if (!Base::WriteEndArray()) + return false; + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + Base::os_->Put('\n'); + } + else + Base::os_->Put('\n'); + WriteIndent(); + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, indentChar_, count); + } + + Ch indentChar_; + unsigned indentCharCount_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h new file mode 100644 index 0000000..e9bfdba --- /dev/null +++ b/include/rapidjson/rapidjson.h @@ -0,0 +1,628 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +// Copyright (c) 2011 Milo Yip (miloyip@gmail.com) +// Version 0.1 + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overriden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#ifdef _MSC_VER +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#ifdef _MSC_VER +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || defined(_WIN64) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. Currently the default uses 4 bytes + alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2 optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. + + To enable these optimizations, two different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + \c RAPIDJSON_SSE42 takes precedence, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef RAPIDJSON_STATIC_ASSERT +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#if defined(__GNUC__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +//!@endcond + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(x) new x +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Allocators and Encodings + +#include "allocators.h" +#include "encodings.h" + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + for (size_t i = 0; i < n; i++) + stream.Put(c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +/////////////////////////////////////////////////////////////////////////////// +// Type + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h new file mode 100644 index 0000000..05c081d --- /dev/null +++ b/include/rapidjson/reader.h @@ -0,0 +1,1446 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "rapidjson.h" +#include "encodings.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (HasParseError()) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128((const __m128i *)&whitespace[0]); + + for (;; p += 16) { + const __m128i s = _mm_load_si128((const __m128i *)p); + const unsigned r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & ~15); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + static const char whitespaces[4][17] = { + " ", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"}; + + const __m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]); + const __m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]); + const __m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]); + const __m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]); + + for (;; p += 16) { + const __m128i s = _mm_load_si128((const __m128i *)p); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = (unsigned short)~_mm_movemask_epi8(x); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +#endif // RAPIDJSON_SSE2 + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespace(is); + + if (is.Peek() == '\0') { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespace(is); + + if (is.Peek() != '\0') { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (!handler.StartObject()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); + + if (is.Peek() == '}') { + is.Take(); + if (!handler.EndObject(0)) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (is.Peek() != '"') + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespace(is); + + if (is.Take() != ':') + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespace(is); + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespace(is); + + ++memberCount; + + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case '}': + if (!handler.EndObject(memberCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + else + return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (!handler.StartArray()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); + + if (is.Peek() == ']') { + is.Take(); + if (!handler.EndArray(0)) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespace(is); + + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case ']': + if (!handler.EndArray(elementCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + else + return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { + if (!handler.Null()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { + if (!handler.Bool(true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { + if (!handler.Bool(false)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Take(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + size_t Length() const { return length_; } + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (!success) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + RAPIDJSON_ASSERT(is.Peek() == '\"'); + is.Take(); // Skip '\"' + + for (;;) { + Ch c = is.Peek(); + if (c == '\\') { // Escape + is.Take(); + Ch e = is.Take(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { + os.Put(escape[(unsigned char)e]); + } + else if (e == 'u') { // Unicode + unsigned codepoint = ParseHex4(is); + if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { + // Handle UTF-16 surrogate pair + if (is.Take() != '\\' || is.Take() != 'u') + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + unsigned codepoint2 = ParseHex4(is); + if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + } + else if (c == '"') { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); + else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + else { + if (parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + } + } + + template + class NumberStream {}; + + template + class NumberStream { + public: + NumberStream(GenericReader& reader, InputStream& is) : is(is) { (void)reader; } + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : NumberStream(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put((char)Base::is.Peek()); + return Base::is.Take(); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + // Parse minus + bool minus = false; + if (s.Peek() == '-') { + minus = true; + s.Take(); + } + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (s.Peek() == '0') { + i = 0; + s.TakePush(); + } + else if (s.Peek() >= '1' && s.Peek() <= '9') { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 214748364) { // 2^31 = 2147483648 + if (i != 214748364 || s.Peek() > '8') { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 429496729) { // 2^32 - 1 = 4294967295 + if (i != 429496729 || s.Peek() > '5') { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + double d = 0.0; + if (use64bit) { + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 + if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { + d = i64; + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 + if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { + d = i64; + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (s.Peek() == '.') { + s.Take(); + decimalPosition = s.Length(); + + if (!(s.Peek() >= '0' && s.Peek() <= '9')) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = (double)i64; +#else + // Use double to store significand in 32-bit architecture + d = use64bit ? (double)i64 : (double)i; +#endif + useDouble = true; + } + + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (d != 0.0) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (s.Peek() == 'e' || s.Peek() == 'E') { + if (!useDouble) { + d = use64bit ? i64 : i; + useDouble = true; + } + s.Take(); + + bool expMinus = false; + if (s.Peek() == '+') + s.Take(); + else if (s.Peek() == '-') { + s.Take(); + expMinus = true; + } + + if (s.Peek() >= '0' && s.Peek() <= '9') { + exp = s.Take() - '0'; + while (s.Peek() >= '0' && s.Peek() <= '9') { + exp = exp * 10 + (s.Take() - '0'); + if (exp > 308 && !expMinus) // exp > 308 should be rare, so it should be checked first. + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(-(int64_t)i64); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(-(int)i); + else + cont = handler.Uint(i); + } + } + if (!cont) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : ParseNumber(is, handler); + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, + IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingElementDelimiterState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || unsigned(c) < 256) + return (Token)tokenMap[(unsigned char)c]; + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + } + }; // End of G + + return (IterativeParsingState)G[state][token]; + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + switch (dst) { + case IterativeParsingStartState: + RAPIDJSON_ASSERT(false); + return IterativeParsingErrorState; + + case IterativeParsingFinishState: + return dst; + + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + if (token == ColonToken) { + is.Take(); + return dst; + } + else + return IterativeParsingErrorState; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + + default: + RAPIDJSON_ASSERT(false); + return IterativeParsingErrorState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + } + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespace(is); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespace(is); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/include/rapidjson/stringbuffer.h b/include/rapidjson/stringbuffer.h new file mode 100644 index 0000000..009a518 --- /dev/null +++ b/include/rapidjson/stringbuffer.h @@ -0,0 +1,99 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "rapidjson.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h new file mode 100644 index 0000000..6daa783 --- /dev/null +++ b/include/rapidjson/writer.h @@ -0,0 +1,391 @@ +// Copyright (C) 2011 Milo Yip +// +// 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 RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "rapidjson.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return WriteNull(); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } + bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } + bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } + bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kStringType); + return WriteString(str, length); + } + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + bool ret = WriteEndObject(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + bool ret = WriteEndArray(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + static const size_t kDefaultLevelDepth = 32; + + bool WriteNull() { + os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); + } + else { + os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + for (char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteDouble(double d) { + char buffer[25]; + char* end = internal::dtoa(d, buffer); + for (char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + os_->Put('\"'); + GenericStringStream is(str); + while (is.Tell() < length) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + os_->Put('\\'); + os_->Put('u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + os_->Put(hexDigits[(codepoint >> 12) & 15]); + os_->Put(hexDigits[(codepoint >> 8) & 15]); + os_->Put(hexDigits[(codepoint >> 4) & 15]); + os_->Put(hexDigits[(codepoint ) & 15]); + } + else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) { + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + os_->Put(hexDigits[(lead >> 12) & 15]); + os_->Put(hexDigits[(lead >> 8) & 15]); + os_->Put(hexDigits[(lead >> 4) & 15]); + os_->Put(hexDigits[(lead ) & 15]); + os_->Put('\\'); + os_->Put('u'); + os_->Put(hexDigits[(trail >> 12) & 15]); + os_->Put(hexDigits[(trail >> 8) & 15]); + os_->Put(hexDigits[(trail >> 4) & 15]); + os_->Put(hexDigits[(trail ) & 15]); + } + else + return false; // invalid code point + } + else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { + is.Take(); + os_->Put('\\'); + os_->Put(escape[(unsigned char)c]); + if (escape[(unsigned char)c] == 'u') { + os_->Put('0'); + os_->Put('0'); + os_->Put(hexDigits[(unsigned char)c >> 4]); + os_->Put(hexDigits[(unsigned char)c & 0xF]); + } + } + else + Transcoder::Transcode(is, *os_); + } + os_->Put('\"'); + return true; + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + void Prefix(Type type) { + (void)type; + if (level_stack_.GetSize() != 0) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + OutputStream* os_; + internal::Stack level_stack_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(11 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(10 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(21 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(20 - (end - buffer)); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer); + os_->Pop(25 - (end - buffer)); + return true; +} + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/server/ClientsManager.h b/include/server/ClientsManager.h new file mode 100644 index 0000000..4eaabf5 --- /dev/null +++ b/include/server/ClientsManager.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include + +struct ClientInfo +{ + ClientInfo(boost::asio::io_service& io) : connection(io) {} + TcpHandler connection; +}; + +class ClientsManager +{ +public: + ClientsManager(boost::asio::io_service& io); + ~ClientsManager(); + + // Start a new thread to listen to and accept clients + void StartListening(const tcp::endpoint &localEndpoint); + // Start an infinite loop to process each client + void StartProcessing(); + // Get the connected clients + std::vector& GetClients() { return m_clients; } + +private: + boost::asio::io_service &m_ioService; + TcpAcceptor m_acceptor; + TcpRequest m_requests; + boost::mutex m_mutex; + + // Clients List + std::vector m_clients; + std::unordered_map> m_groups; // map groupId to a list of id's of clients + // each client id is index of m_clients vector + + // Handle a newly connected client + void HandleClient(boost::shared_ptr &socket); + // Process each client + void ProcessClients(); + + // Test Method to receive chat messages on GROUP_CHAT request + void ReceiveChat(unsigned int client, unsigned int group); +}; \ No newline at end of file diff --git a/makefile_client b/makefile_client new file mode 100644 index 0000000..cc0c2d9 --- /dev/null +++ b/makefile_client @@ -0,0 +1,28 @@ +INC_DIR = include +SRC_DIR = src + +CFLAG = `pkg-config --cflags gtk+-3.0` +CFLAGS = --std=c++11 + +CC = g++ + +#for common +SOURCES_COMM = TcpHandler.cpp TcpListener.cpp RequestHandler.cpp ChatMessage.cpp +HEADERS_COMM = TcpHandler.h TcpListner.h RequestHandler.h +OBJECTS_COMM = $(SOURCES_COMM:.cpp=.o) +FSOURCES_COMM := $(addprefix $(SRC_DIR)/common/, $(SOURCES_COMM)) +FHEADERS_COMM := $(addprefix $(INC_DIR)/common/, $(HEADERS_COMM)) + +LDFLAGS_COMM = -lboost_system -lboost_thread -lpthread + +all: client.o common + $(CC) -o output $(SRC_DIR)/client/main.cpp *.o -Iinclude/ $(CFLAGS) $(CFLAG) $(LDFLAGS_COMM) + +common: $(OBJECTS_COMM) + $(CC) -c -o common.o $< + +%.o : src/common/%.cpp + $(CC) -c -o $@ $< $(LDFLAGS_COMM) -Iinclude/ $(CFLAG) $(CFLAGS) + +client.o: src/client/TcpClient.cpp + $(CC) -c -o $@ $< -Iinclude/ $(CFLAG) $(CFLAGS) diff --git a/output b/output new file mode 100755 index 0000000000000000000000000000000000000000..9d592d5b7f796ecd59cc2ff7dacd377146290601 GIT binary patch literal 408519 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 +#include + +ChatMessage::ChatMessage() +{} + +ChatMessage::~ChatMessage() +{} + +void ChatMessage::Send(TcpHandler &tcpHandler) +{ + tcpHandler.Send(m_body.c_str(), m_body.size() + 1); +} + +void ChatMessage::Receive(TcpHandler &tcpHandler, uint32_t size) +{ + char * data = new char[size]; + tcpHandler.Receive(data, size); + m_body = std::string(data); + delete[] data; +} \ No newline at end of file diff --git a/src/common/Control.cpp b/src/common/Control.cpp new file mode 100644 index 0000000..9ef74c9 --- /dev/null +++ b/src/common/Control.cpp @@ -0,0 +1,6 @@ +#include "common/Control.h" + +void Control::PutFixedAt(GtkWidget *fixed, int x, int y) +{ + +} \ No newline at end of file diff --git a/src/common/Page.cpp b/src/common/Page.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/common/RtpHandler.cpp b/src/common/RtpHandler.cpp new file mode 100644 index 0000000..80a331f --- /dev/null +++ b/src/common/RtpHandler.cpp @@ -0,0 +1,75 @@ +#include +#include + +RtpHandler::RtpHandler() +{ +} + +RtpHandler::~RtpHandler() +{ + CleanUp(); +} + +void RtpHandler::Initialize(boost::shared_ptr socket, const udp::endpoint &remoteEndpoint) +{ + m_socket = socket; + m_remoteEndpoint = remoteEndpoint; + m_sequenceNumber = m_timeStamp = 0; + m_timeStampIncrement = 3600; // for 25fps video +} + +void RtpHandler::CleanUp() +{ + m_socket.reset(); + m_remoteEndpoint = udp::endpoint(); +} + +void RtpHandler::Send(const char *data, size_t size) +{ + if (!m_socket) + throw RtpTransmissionException("RTP Transmitter hasn't been properly initialized"); + + std::vector packet; + packet.resize(RTP_HEADER_SIZE); + packet[0] = (char)0x80; // RTP version + packet[1] = m_payloadType; // Payload type + packet[2] = m_sequenceNumber >> 8; // Sequence number of packet + packet[3] = m_sequenceNumber & 0x0FF; + packet[4] = (m_timeStamp & 0xFF000000) >> 24; // Timestamp + packet[5] = (m_timeStamp & 0x00FF0000) >> 16; + packet[6] = (m_timeStamp & 0x0000FF00) >> 8; + packet[7] = (m_timeStamp & 0x000000FF); + packet[8] = (char)0x13; // 4 byte SSRC (sychronization source identifier) + packet[9] = (char)0xf9; // we just an arbitrary number here to keep it simple + packet[10] = (char)0x7e; + packet[11] = (char)0x67; + + for (unsigned int i = 0; i < size; ++i) + packet[RTP_HEADER_SIZE + i] = data[i]; + + m_sequenceNumber++; + m_timeStamp += m_timeStampIncrement; + + m_socket->send_to(boost::asio::buffer(packet), m_remoteEndpoint); +} + + +void RtpHandler::Receive(char* data, size_t maxSize) +{ + if (!m_socket) + throw RtpReceptionError("RTP Receiver hasn't been properly initialized"); + + std::vector packet; + packet.resize(maxSize); + size_t len = m_socket->receive_from(boost::asio::buffer(packet), m_remoteEndpoint); + packet.resize(len); + + char version = packet[0]; + m_payloadType = packet[1]; + m_sequenceNumber = *((uint16_t*)&packet[2]); + m_timeStamp = *((int*)&packet[4]); + int SSRC = *((int*)&packet[8]); + + for (unsigned int i = 0; i < len - RTP_HEADER_SIZE; ++i) + data[i] = packet[i + RTP_HEADER_SIZE]; +} \ No newline at end of file diff --git a/src/common/TcpAcceptor.cpp b/src/common/TcpAcceptor.cpp new file mode 100644 index 0000000..c4d51af --- /dev/null +++ b/src/common/TcpAcceptor.cpp @@ -0,0 +1,51 @@ +#include +#include + +TcpAcceptor::TcpAcceptor(boost::asio::io_service &io) : m_acceptor(io) +{} + +TcpAcceptor::~TcpAcceptor() +{} + +uint16_t TcpAcceptor::Initialize(const tcp::endpoint &localEndpoint) +{ + // Open and bind the tcp acceptor to the local endpoint + m_acceptor.open(localEndpoint.protocol()); + //m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + m_acceptor.bind(localEndpoint); + // Prepare to listen + m_acceptor.listen(); + return m_acceptor.local_endpoint().port(); +} + +void TcpAcceptor::Listen(boost::function)> callback) +{ + m_callback = callback; + StartListening(); +} + +// Create new thread to listen for incomming connections +void TcpAcceptor::StartListening() +{ + boost::thread t(boost::bind(&TcpAcceptor::ListeningThread, this)); +} + +void TcpAcceptor::ListeningThread() +{ + try + { + while (true) + { + // Create a new socket to represent a new connection + boost::shared_ptr socket(new tcp::socket(m_acceptor.get_io_service())); + // Wait for a connection and accept at the socket + m_acceptor.accept(*socket); + // Send the socket to the handler + m_callback(socket); + } + } + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; + } +} \ No newline at end of file diff --git a/src/common/TcpRequest.cpp b/src/common/TcpRequest.cpp new file mode 100644 index 0000000..d4432ec --- /dev/null +++ b/src/common/TcpRequest.cpp @@ -0,0 +1,160 @@ +#include +#include +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +using namespace rapidjson; + +// Requests are of fixed size (defined by REQUEST_MAX_SIZE) +// This makes it simpler to receive just the required request data +// from the socket and neither more nor less +const size_t REQUEST_MAX_SIZE = 150; + +TcpRequest::TcpRequest() +{} + +TcpRequest::~TcpRequest() +{} + +std::string TcpRequest::GetJsonString() +{ + StringBuffer buffer; + Writer writer(buffer); + m_document.Accept(writer); + return buffer.GetString(); +} + +void TcpRequest::JoinChat(TcpHandler &tcpHandler, uint32_t groupId) +{ + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 1 + Group-Id: 20 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)JOIN_CHAT), m_document.GetAllocator()); + m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} + +void TcpRequest::ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId) +{ + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 2 + Group-Id: 20 + Message-Size: 50 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)CHAT_MESSAGE), m_document.GetAllocator()); + m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); + m_document.AddMember("Message-Size", Value(messageSize), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} + +void TcpRequest::P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp, uint16_t publicPort) +{ + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 3 + Client-Id: 32 + Private-IP: 1.2.3.4 + Private-Port: 1234 + Public-IP: 2.3.5.6 + Public-Port: 2356 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)P2P_TCP), m_document.GetAllocator()); + m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); + m_document.AddMember("Private-IP", Value(privateIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); + m_document.AddMember("Private-Port", Value((unsigned int)privatePort), m_document.GetAllocator()); + m_document.AddMember("Public-IP", Value(publicIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); + m_document.AddMember("Public-Port", Value((unsigned int)publicPort), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} + + +void TcpRequest::ReceiveRequest(TcpHandler &tcpHandler) +{ + // Get the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + tcpHandler.Receive(request, REQUEST_MAX_SIZE); + + // Parse the request + m_document = Document(); + m_document.Parse(request); +} + +TcpRequest::REQUEST_TYPE TcpRequest::GetRequestType() +{ + auto value = m_document.FindMember("Request-Type"); + if (value == m_document.MemberEnd() || !value->value.IsInt()) + return INAVLID_TYPE; + return (REQUEST_TYPE)value->value.GetInt(); +} + +inline Value& TcpRequest::GetValue(const std::string &key) +{ + static Value sval; + auto value = m_document.FindMember(key.c_str()); + if (value == m_document.MemberEnd()) + throw RequestException("Invalid value for '" + key + "' received in the request"); + sval = value->value; + return sval; +} + +uint32_t TcpRequest::GetGroupId() +{ + return GetValue("Group-Id").GetUint(); +} + +uint32_t TcpRequest::GetMessageSize() +{ + return GetValue("Message-Size").GetUint();; +} + +uint32_t TcpRequest::GetClientId() +{ + return GetValue("Client-Id").GetUint(); +} + +std::string TcpRequest::GetPrivateIp() +{ + return GetValue("Private-IP").GetString(); +} + +uint16_t TcpRequest::GetPrivatePort() +{ + return GetValue("Private-Port").GetUint(); +} + +std::string TcpRequest::GetPublicIp() +{ + return GetValue("Public-IP").GetString(); +} + +uint16_t TcpRequest::GetPublicPort() +{ + return GetValue("Public-Port").GetUint(); +} \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp new file mode 100644 index 0000000..97e4fa7 --- /dev/null +++ b/src/server/ClientsManager.cpp @@ -0,0 +1,132 @@ +#include +#include +#include + +ClientsManager::ClientsManager(boost::asio::io_service& io) +: m_acceptor(io), m_ioService(io) +{} + +ClientsManager::~ClientsManager() +{} + +// Use listener to listen to incoming clients +void ClientsManager::StartListening(const tcp::endpoint &localEndpoint) +{ + m_acceptor.Initialize(localEndpoint); + m_acceptor.Listen(boost::bind(&ClientsManager::HandleClient, this, _1)); +} + +// Add any client that is connected to the clients list +void ClientsManager::HandleClient(boost::shared_ptr &socket) +{ + boost::lock_guard guard(m_mutex); + ClientInfo client(m_ioService); + client.connection.Initialize(socket); + m_clients.push_back(client); + std::cout << "Client #" << m_clients.size() - 1 << " Connected: " << m_clients[m_clients.size() - 1].connection.GetDestinationAddress() << std::endl; +} + +// Start processing each client +void ClientsManager::StartProcessing() +{ + //boost::thread t(boost::bind(&ClientsManager::ProcessClients, this)); + ProcessClients(); +} + +// Process each client +void ClientsManager::ProcessClients() +{ + while (true) + { + // Sleep for some time if no client is available + while (m_clients.size() == 0) + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + + // Lock the clients-list while processing the clients + m_mutex.lock(); + // Process each client in turn + for (unsigned int i = 0; i < m_clients.size(); ++i) + { + try + { + uint32_t id; + // See if any request is incomming for this client + size_t bytes = m_clients[i].connection.Available(); + if (bytes > 0) + { + // if so receive the request and process accordingly + m_requests.ReceiveRequest(m_clients[i].connection); + + switch (m_requests.GetRequestType()) + { + // Request to join chat, since this is the sever, this is a request to join a group chat + case TcpRequest::JOIN_CHAT: + id = m_requests.GetGroupId(); + // push the client id to the group + m_groups[id].push_back(i); + std::cout << "Connected client #" << i << " to group #" << id << std::endl; + break; + // Request to receive an incoming chat message + case TcpRequest::CHAT_MESSAGE: + id = m_requests.GetGroupId(); + // receive the chat message + ReceiveChat(i, id); + break; + + // Request for a p2p tcp connection to another client + case TcpRequest::P2P_TCP: + id = m_requests.GetClientId(); + if (id <= m_clients.size()) + { + // Send request to second client + m_requests.P2PTcp(m_clients[id].connection, i, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), + m_clients[i].connection.GetIp(), m_clients[i].connection.GetPort()); + // Receive the return request + m_requests.ReceiveRequest(m_clients[id].connection); + // Send back the return request to first client + m_requests.P2PTcp(m_clients[i].connection, id, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), + m_clients[id].connection.GetIp(), m_clients[id].connection.GetPort()); + } + break; + + default: + std::cout << "Invalid Request " << m_requests.GetRequestType() << " from client #" << i << std::endl; + } + } + } + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; + } + } + m_mutex.unlock(); + } +} + +void ClientsManager::ReceiveChat(unsigned int client, unsigned int group) +{ + ChatMessage chat; + chat.Receive(m_clients[client].connection, m_requests.GetMessageSize()); + //m_mutex.lock(); + + // Send the messsage to each client in the group + for (unsigned int i = 0; i < m_groups[group].size(); ++i) + { + try + { + if (m_groups[group][i] != client) + { + m_requests.ChatMessage(m_clients[i].connection, chat.GetMessage().size() + 1); + chat.Send(m_clients[m_groups[group][i]].connection); + } + } + // client may be disconnected and exception may be thrown + // we catch the exception inside the loop so that + // we can continue sending messages to other clients + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; + } + } + //m_mutex.unlock(); +} \ No newline at end of file From e885895e0bc2abb82fd0b9ac40ca0ae95ef8ac15 Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Thu, 25 Dec 2014 21:19:21 +0545 Subject: [PATCH 45/54] Revert "Simple TCP hole punching... remains to be tested" This reverts commit 7a6b0e54de379019f0937b7f7fd7bc7ca6c9c802. --- include/client/TcpClient.h | 21 +++--- include/common/TcpRequest.h | 18 ++--- src/client/TcpClient.cpp | 134 +++++++++++----------------------- src/client/main.cpp | 2 +- src/common/TcpAcceptor.cpp | 2 +- src/common/TcpHandler.cpp | 3 +- src/common/TcpRequest.cpp | 59 ++++++++------- src/server/ClientsManager.cpp | 16 ++-- 8 files changed, 100 insertions(+), 155 deletions(-) diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index 67c962a..b697b57 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -10,14 +10,16 @@ class TcpClient TcpClient(boost::asio::io_service &io); ~TcpClient(); + // Start listening for incoming connections in a new thread + void StartAccepting(); // Connect to a peer/server void Connect(const tcp::endpoint& peer, bool joinChat = false); // Connect to a peer/server asynchronously void ConnectAsync(const tcp::endpoint& peer, bool joinChat = false); + // Handle all requests that the client gets + void HandleRequests(); // Connect to a peer through server void Connect(uint32_t clientId); - // Start handling all requests that the client gets - void HandleRequests(); // Join chat void JoinChat(uint32_t connectionId, uint32_t groupId); @@ -40,22 +42,17 @@ class TcpClient }; boost::asio::io_service &m_io; + TcpAcceptor m_acceptor; - // List of the connections std::vector m_connections; - // Mutex to lock 'm_connections' list as well as 'm_request' request-processor - boost::mutex m_mutex; + // Mutex to lock m_connections vector + boost::mutex m_connectionsMutex; TcpRequest m_request; std::string m_name; - // For P2P: - bool m_p2pConnected; - void HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); - void HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); - boost::shared_ptr m_acceptor; - void P2PListen(const tcp::endpoint &localEndpoint); - void P2PConnect(tcp::endpoint &remoteEndpoint); + void AcceptorHandler(boost::shared_ptr socket); + // For console: int m_currentConnection; diff --git a/include/common/TcpRequest.h b/include/common/TcpRequest.h index d21c375..f323fba 100644 --- a/include/common/TcpRequest.h +++ b/include/common/TcpRequest.h @@ -15,7 +15,7 @@ class TcpRequest enum REQUEST_TYPE{ INAVLID_TYPE = 0, JOIN_CHAT, CHAT_MESSAGE, - P2P_TCP, + SEND_CLIENT_ADDR, RECEIVE_CLIENT_ADDR, }; TcpRequest(); @@ -25,10 +25,12 @@ class TcpRequest void JoinChat(TcpHandler &tcpHandler, uint32_t groupId = 0); // Request to wait for incoming chat message void ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId = 0); - // Request to establish a P2P TCP connection with a client - // Contains private and public addess-port pairs and client-id - void P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp = "", uint16_t publicPort = 0); - + // Request to send a client address (ip and port) + // For server, provide a valid clientId + void SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId); + // Request to receive a client address (ip and port) as response for a SEND_CLIENT_ADDR request + // For server, provide a valid clientId + void ReceiveClientAddr(TcpHandler &tcpHandler, uint32_t clientId, const std::string &ip, uint16_t port); // Receive a request void ReceiveRequest(TcpHandler &tcpHandler); @@ -37,10 +39,8 @@ class TcpRequest uint32_t GetGroupId(); uint32_t GetMessageSize(); uint32_t GetClientId(); - std::string GetPrivateIp(); - uint16_t GetPrivatePort(); - std::string GetPublicIp(); - uint16_t GetPublicPort(); + std::string GetIp(); + uint16_t GetPort(); private: // The Json Document that holds the request rapidjson::Document m_document; diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index 0a6731d..2e7eaaa 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -3,17 +3,40 @@ #include TcpClient::TcpClient(boost::asio::io_service &io) -:m_io(io), /*m_acceptor(io),*/ m_currentConnection(-1) +:m_io(io), m_acceptor(io), m_currentConnection(-1) {} TcpClient::~TcpClient() {} +void TcpClient::StartAccepting() +{ + uint16_t port = m_acceptor.Initialize(tcp::endpoint(tcp::v4(), 0)); + std::cout << "Listening at port: " << port << std::endl; + + // Listen method will start listening in new thread + m_acceptor.Listen(boost::bind(&TcpClient::AcceptorHandler, this, _1)); +} + +// This is the handler called when the acceptor accepts a new connection +void TcpClient::AcceptorHandler(boost::shared_ptr socket) +{ + // Lock the connections list not to process it while adding a connection + boost::lock_guard guard(m_connectionsMutex); + + // Add new connection + m_connections.push_back(Connection(m_io)); + TcpHandler &handler = m_connections[m_connections.size() - 1].tcpHandler; + handler.Initialize(socket); + + std::cout << "Connected to: " << handler.GetDestinationAddress() << std::endl; +} + // Connecting to some remote peer (server/client) void TcpClient::Connect(const tcp::endpoint& peer, bool joinChat) { // Lock the connections list not to process it while adding a connection - boost::lock_guard guard(m_mutex); + boost::lock_guard guard(m_connectionsMutex); // Add new connection m_connections.push_back(Connection(m_io)); @@ -35,86 +58,14 @@ void TcpClient::ConnectAsync(const tcp::endpoint& peer, bool joinChat) // Use server to connect to a client (for P2P) void TcpClient::Connect(uint32_t clientId) { - // make sure no requests are being processed during this time - boost::lock_guard guard(m_mutex); - - // First send a P2P tcp connection request to server and receive back the returning request - TcpHandler& handler = m_connections[0].tcpHandler; - m_request.P2PTcp(handler, clientId, handler.GetIp(), handler.GetPort()); - m_request.ReceiveRequest(handler); - - HandleP2PRequest(m_request.GetClientId(), - tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPrivateIp()), m_request.GetPrivatePort()), - tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort())); -} - -void TcpClient::HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint) -{ - TcpHandler& handler = m_connections[0].tcpHandler; - // Start listening at same local endpoint as which is connected to the server - boost::thread t1(boost::bind(&TcpClient::P2PListen, this, _1), handler.GetSocket()->local_endpoint()); - // Try connecting to both private and public endpoints of other peer - boost::thread t2(boost::bind(&TcpClient::P2PConnect, this, _1), privateEndpoint); - boost::thread t3(boost::bind(&TcpClient::P2PConnect, this, _1), publicEndpoint); - - while (!m_p2pConnected); - if (m_acceptor) - m_acceptor->cancel(); -} - -void TcpClient::HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint) -{ - boost::thread t([this, clientId, privateEndpoint, publicEndpoint]() - { - boost::lock_guard guard(m_mutex); - HandleP2PRequest(clientId, privateEndpoint, publicEndpoint); - }); -} - -void TcpClient::P2PListen(const tcp::endpoint &localEndpoint) -{ - try - { - m_acceptor.reset(new tcp::acceptor(m_io, localEndpoint, true)); - // Create a new socket to represent a new connection - boost::shared_ptr socket(new tcp::socket(m_acceptor->get_io_service())); - // Wait for a connection and accept at the socket - m_acceptor->accept(*socket); - - /* - TODO: Need to verify if this is the required peer - */ - Connection c(m_io); - c.tcpHandler.Initialize(socket); - m_connections.push_back(c); - m_p2pConnected = true; - - m_acceptor.reset(); - } - catch (std::exception &ex) - { - //std::cout << ex.what() << std::endl; - } -} -; -void TcpClient::P2PConnect(tcp::endpoint &remoteEndpoint) -{ - while (!m_p2pConnected) - { - try - { - // Try connecting at the given remote endpoint - Connection c(m_io); - c.tcpHandler.Initialize(remoteEndpoint); - m_connections.push_back(c); - m_p2pConnected = true; - JoinChat(m_connections.size() - 1, 0); - } - catch (std::exception &ex) - { - //std::cout << ex.what() << std::endl; - } - } + // Send a request to get a client-address; assuming server id first element in the connections-list + m_request.SendClientAddr(m_connections[0].tcpHandler, clientId); + + // Receive for the response and use it to connect to the client + m_request.ReceiveRequest(m_connections[0].tcpHandler); + if (m_request.GetRequestType() != TcpRequest::RECEIVE_CLIENT_ADDR) + throw Exception("Expecting a client address but getting some other request"); + Connect(tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetIp()), m_request.GetPort()), true); } // An infinite loop to handle incoming requests @@ -127,7 +78,7 @@ void TcpClient::HandleRequests() boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); // While processing connections, lock the list so no new connection is added at the time - m_mutex.lock(); + m_connectionsMutex.lock(); for (unsigned int i = 0; i < m_connections.size(); ++i) { try @@ -135,7 +86,6 @@ void TcpClient::HandleRequests() // See if any request is incomming for this connection size_t bytes = m_connections[i].tcpHandler.Available(); ChatMessage chat; - uint32_t id; // if so, process accordingly if (bytes > 0) { @@ -152,15 +102,13 @@ void TcpClient::HandleRequests() m_currentConnection = i; std::cout << "\n\n" << chat.GetMessage() << "\n\nYou: "; break; - // Request for a p2p tcp connection - case TcpRequest::P2P_TCP: - id = m_request.GetClientId(); - HandleP2PRequestAsync(id, - tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPrivateIp()), m_request.GetPrivatePort()), - tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort())); - m_request.P2PTcp(m_connections[0].tcpHandler, id, - m_connections[0].tcpHandler.GetIp(), m_connections[0].tcpHandler.GetPort()); + // Request to send the client address + case TcpRequest::SEND_CLIENT_ADDR: + // Since this IS the client, the other connection already has an IP + // There is no need to send ip, just the port + m_request.ReceiveClientAddr(m_connections[i].tcpHandler, m_request.GetClientId(), "", m_acceptor.GetSocket().local_endpoint().port()); break; + default: std::cout << "Invalid Request " << m_request.GetRequestType() << std::endl; } @@ -171,7 +119,7 @@ void TcpClient::HandleRequests() std::cout << ex.what() << std::endl; } } - m_mutex.unlock(); + m_connectionsMutex.unlock(); } } diff --git a/src/client/main.cpp b/src/client/main.cpp index 0dca2a5..500e687 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { boost::asio::io_service io; TcpClient client(io); - //client.StartAccepting(); + client.StartAccepting(); // Connect to another peer /*{ diff --git a/src/common/TcpAcceptor.cpp b/src/common/TcpAcceptor.cpp index c4d51af..23d9272 100644 --- a/src/common/TcpAcceptor.cpp +++ b/src/common/TcpAcceptor.cpp @@ -11,7 +11,7 @@ uint16_t TcpAcceptor::Initialize(const tcp::endpoint &localEndpoint) { // Open and bind the tcp acceptor to the local endpoint m_acceptor.open(localEndpoint.protocol()); - //m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); m_acceptor.bind(localEndpoint); // Prepare to listen m_acceptor.listen(); diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index c4ebc18..0b4d2a8 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -25,8 +25,7 @@ void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) { if (m_socket) throw TcpHandlerException("Socket alread created"); - m_socket.reset(new tcp::socket(m_ioService, destEndpoint.protocol())); - m_socket->set_option(tcp::socket::reuse_address(true)); + m_socket.reset(new tcp::socket(m_ioService)); m_socket->connect(destEndpoint); } diff --git a/src/common/TcpRequest.cpp b/src/common/TcpRequest.cpp index d4432ec..5e37bab 100644 --- a/src/common/TcpRequest.cpp +++ b/src/common/TcpRequest.cpp @@ -66,27 +66,19 @@ void TcpRequest::ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint3 tcpHandler.Send(request, REQUEST_MAX_SIZE); } -void TcpRequest::P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp, uint16_t publicPort) +void TcpRequest::SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId) { m_document = Document(); // New Document /* Example: { Request-Type: 3 - Client-Id: 32 - Private-IP: 1.2.3.4 - Private-Port: 1234 - Public-IP: 2.3.5.6 - Public-Port: 2356 + Client-Id: 5 } */ m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)P2P_TCP), m_document.GetAllocator()); + m_document.AddMember("Request-Type", Value((int)SEND_CLIENT_ADDR), m_document.GetAllocator()); m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); - m_document.AddMember("Private-IP", Value(privateIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); - m_document.AddMember("Private-Port", Value((unsigned int)privatePort), m_document.GetAllocator()); - m_document.AddMember("Public-IP", Value(publicIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); - m_document.AddMember("Public-Port", Value((unsigned int)publicPort), m_document.GetAllocator()); // Send the "fixed-size" request char request[REQUEST_MAX_SIZE]; @@ -94,6 +86,29 @@ void TcpRequest::P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::st tcpHandler.Send(request, REQUEST_MAX_SIZE); } +void TcpRequest::ReceiveClientAddr(TcpHandler &tcpHandler, uint32_t clientId, const std::string &ip, uint16_t port) +{ + m_document = Document(); // New Document + /* + Example: + { + Request-Type: 4 + Client-Id: 5 + IP: 23.123.2.3 + Port: 23456 + } + */ + m_document.SetObject(); + m_document.AddMember("Request-Type", Value((int)RECEIVE_CLIENT_ADDR), m_document.GetAllocator()); + m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); + m_document.AddMember("IP", Value(ip.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); + m_document.AddMember("Port", Value((unsigned int)port), m_document.GetAllocator()); + + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} void TcpRequest::ReceiveRequest(TcpHandler &tcpHandler) { @@ -134,27 +149,17 @@ uint32_t TcpRequest::GetMessageSize() return GetValue("Message-Size").GetUint();; } -uint32_t TcpRequest::GetClientId() -{ - return GetValue("Client-Id").GetUint(); -} - -std::string TcpRequest::GetPrivateIp() -{ - return GetValue("Private-IP").GetString(); -} - -uint16_t TcpRequest::GetPrivatePort() +uint16_t TcpRequest::GetPort() { - return GetValue("Private-Port").GetUint(); + return GetValue("Port").GetUint(); } -std::string TcpRequest::GetPublicIp() +uint32_t TcpRequest::GetClientId() { - return GetValue("Public-IP").GetString(); + return GetValue("Client-Id").GetUint(); } -uint16_t TcpRequest::GetPublicPort() +std::string TcpRequest::GetIp() { - return GetValue("Public-Port").GetUint(); + return GetValue("IP").GetString(); } \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 97e4fa7..7da71ed 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -72,20 +72,16 @@ void ClientsManager::ProcessClients() // receive the chat message ReceiveChat(i, id); break; - - // Request for a p2p tcp connection to another client - case TcpRequest::P2P_TCP: + // Request to send a client address + case TcpRequest::SEND_CLIENT_ADDR: id = m_requests.GetClientId(); if (id <= m_clients.size()) { - // Send request to second client - m_requests.P2PTcp(m_clients[id].connection, i, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), - m_clients[i].connection.GetIp(), m_clients[i].connection.GetPort()); - // Receive the return request + // Request the client for the address + m_requests.SendClientAddr(m_clients[id].connection, id); m_requests.ReceiveRequest(m_clients[id].connection); - // Send back the return request to first client - m_requests.P2PTcp(m_clients[i].connection, id, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), - m_clients[id].connection.GetIp(), m_clients[id].connection.GetPort()); + // Send back a request filled with required info + m_requests.ReceiveClientAddr(m_clients[i].connection, id, m_clients[id].connection.GetIp(), m_requests.GetPort()); } break; From b4bf1f42ff8659e8c47a046833a36ed28d36605e Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Fri, 26 Dec 2014 20:30:45 +0545 Subject: [PATCH 46/54] Rerverting the faulty commits --- include/client/TcpClient.h | 21 +++++++------ include/common/TcpHandler.h | 2 +- include/common/TcpRequest.h | 18 +++++------ src/client/TcpClient.cpp | 32 ++++++------------- src/client/main.cpp | 2 +- src/common/TcpAcceptor.cpp | 2 +- src/common/TcpHandler.cpp | 3 +- src/common/TcpRequest.cpp | 59 ++++++++++++++++------------------- src/server/ClientsManager.cpp | 18 ++++++----- 9 files changed, 74 insertions(+), 83 deletions(-) diff --git a/include/client/TcpClient.h b/include/client/TcpClient.h index b697b57..67c962a 100644 --- a/include/client/TcpClient.h +++ b/include/client/TcpClient.h @@ -10,16 +10,14 @@ class TcpClient TcpClient(boost::asio::io_service &io); ~TcpClient(); - // Start listening for incoming connections in a new thread - void StartAccepting(); // Connect to a peer/server void Connect(const tcp::endpoint& peer, bool joinChat = false); // Connect to a peer/server asynchronously void ConnectAsync(const tcp::endpoint& peer, bool joinChat = false); - // Handle all requests that the client gets - void HandleRequests(); // Connect to a peer through server void Connect(uint32_t clientId); + // Start handling all requests that the client gets + void HandleRequests(); // Join chat void JoinChat(uint32_t connectionId, uint32_t groupId); @@ -42,17 +40,22 @@ class TcpClient }; boost::asio::io_service &m_io; - TcpAcceptor m_acceptor; + // List of the connections std::vector m_connections; - // Mutex to lock m_connections vector - boost::mutex m_connectionsMutex; + // Mutex to lock 'm_connections' list as well as 'm_request' request-processor + boost::mutex m_mutex; TcpRequest m_request; std::string m_name; - void AcceptorHandler(boost::shared_ptr socket); - + // For P2P: + bool m_p2pConnected; + void HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); + void HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); + boost::shared_ptr m_acceptor; + void P2PListen(const tcp::endpoint &localEndpoint); + void P2PConnect(tcp::endpoint &remoteEndpoint); // For console: int m_currentConnection; diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index a53f5ec..a4d7860 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -17,7 +17,7 @@ class TcpHandler // Initialize with an already created socket void Initialize(boost::shared_ptr socket); - // Connect to given endpoint and initialize by creating new socket + // Connect to given endpoint and initialize by creating new socker void Initialize(const tcp::endpoint &destEndpoint); // Send data of given size diff --git a/include/common/TcpRequest.h b/include/common/TcpRequest.h index f323fba..d21c375 100644 --- a/include/common/TcpRequest.h +++ b/include/common/TcpRequest.h @@ -15,7 +15,7 @@ class TcpRequest enum REQUEST_TYPE{ INAVLID_TYPE = 0, JOIN_CHAT, CHAT_MESSAGE, - SEND_CLIENT_ADDR, RECEIVE_CLIENT_ADDR, + P2P_TCP, }; TcpRequest(); @@ -25,12 +25,10 @@ class TcpRequest void JoinChat(TcpHandler &tcpHandler, uint32_t groupId = 0); // Request to wait for incoming chat message void ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId = 0); - // Request to send a client address (ip and port) - // For server, provide a valid clientId - void SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId); - // Request to receive a client address (ip and port) as response for a SEND_CLIENT_ADDR request - // For server, provide a valid clientId - void ReceiveClientAddr(TcpHandler &tcpHandler, uint32_t clientId, const std::string &ip, uint16_t port); + // Request to establish a P2P TCP connection with a client + // Contains private and public addess-port pairs and client-id + void P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp = "", uint16_t publicPort = 0); + // Receive a request void ReceiveRequest(TcpHandler &tcpHandler); @@ -39,8 +37,10 @@ class TcpRequest uint32_t GetGroupId(); uint32_t GetMessageSize(); uint32_t GetClientId(); - std::string GetIp(); - uint16_t GetPort(); + std::string GetPrivateIp(); + uint16_t GetPrivatePort(); + std::string GetPublicIp(); + uint16_t GetPublicPort(); private: // The Json Document that holds the request rapidjson::Document m_document; diff --git a/src/client/TcpClient.cpp b/src/client/TcpClient.cpp index 6f9eb4a..0a6731d 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/TcpClient.cpp @@ -35,13 +35,12 @@ void TcpClient::ConnectAsync(const tcp::endpoint& peer, bool joinChat) // Use server to connect to a client (for P2P) void TcpClient::Connect(uint32_t clientId) { - m_p2pConnected = false; // make sure no requests are being processed during this time boost::lock_guard guard(m_mutex); // First send a P2P tcp connection request to server and receive back the returning request TcpHandler& handler = m_connections[0].tcpHandler; - m_request.P2PTcp(handler, clientId, handler.GetSocket()->local_endpoint().address().to_string(), handler.GetSocket()->local_endpoint().port()); + m_request.P2PTcp(handler, clientId, handler.GetIp(), handler.GetPort()); m_request.ReceiveRequest(handler); HandleP2PRequest(m_request.GetClientId(), @@ -55,8 +54,7 @@ void TcpClient::HandleP2PRequest(uint32_t clientId, const tcp::endpoint &private // Start listening at same local endpoint as which is connected to the server boost::thread t1(boost::bind(&TcpClient::P2PListen, this, _1), handler.GetSocket()->local_endpoint()); // Try connecting to both private and public endpoints of other peer - if (privateEndpoint!=publicEndpoint) - boost::thread t2(boost::bind(&TcpClient::P2PConnect, this, _1), privateEndpoint); + boost::thread t2(boost::bind(&TcpClient::P2PConnect, this, _1), privateEndpoint); boost::thread t3(boost::bind(&TcpClient::P2PConnect, this, _1), publicEndpoint); while (!m_p2pConnected); @@ -68,15 +66,8 @@ void TcpClient::HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &pr { boost::thread t([this, clientId, privateEndpoint, publicEndpoint]() { - try - { - boost::lock_guard guard(m_mutex); - HandleP2PRequest(clientId, privateEndpoint, publicEndpoint); - } - catch (std::exception &ex) - { - std::cout << ex.what() << std::endl; - } + boost::lock_guard guard(m_mutex); + HandleP2PRequest(clientId, privateEndpoint, publicEndpoint); }); } @@ -89,8 +80,7 @@ void TcpClient::P2PListen(const tcp::endpoint &localEndpoint) boost::shared_ptr socket(new tcp::socket(m_acceptor->get_io_service())); // Wait for a connection and accept at the socket m_acceptor->accept(*socket); - if (m_p2pConnected) return; - + /* TODO: Need to verify if this is the required peer */ @@ -98,10 +88,12 @@ void TcpClient::P2PListen(const tcp::endpoint &localEndpoint) c.tcpHandler.Initialize(socket); m_connections.push_back(c); m_p2pConnected = true; + + m_acceptor.reset(); } catch (std::exception &ex) { - std::cout << ex.what() << std::endl; + //std::cout << ex.what() << std::endl; } } ; @@ -114,15 +106,13 @@ void TcpClient::P2PConnect(tcp::endpoint &remoteEndpoint) // Try connecting at the given remote endpoint Connection c(m_io); c.tcpHandler.Initialize(remoteEndpoint); - if (m_p2pConnected) return; - m_connections.push_back(c); m_p2pConnected = true; JoinChat(m_connections.size() - 1, 0); } catch (std::exception &ex) { - std::cout << ex.what() << std::endl; + //std::cout << ex.what() << std::endl; } } } @@ -165,13 +155,11 @@ void TcpClient::HandleRequests() // Request for a p2p tcp connection case TcpRequest::P2P_TCP: id = m_request.GetClientId(); - m_p2pConnected = false; HandleP2PRequestAsync(id, tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPrivateIp()), m_request.GetPrivatePort()), tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort())); m_request.P2PTcp(m_connections[0].tcpHandler, id, - m_connections[0].tcpHandler.GetSocket()->local_endpoint().address().to_string(), m_connections[0].tcpHandler.GetSocket()->local_endpoint().port() - ); + m_connections[0].tcpHandler.GetIp(), m_connections[0].tcpHandler.GetPort()); break; default: std::cout << "Invalid Request " << m_request.GetRequestType() << std::endl; diff --git a/src/client/main.cpp b/src/client/main.cpp index 500e687..0dca2a5 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { boost::asio::io_service io; TcpClient client(io); - client.StartAccepting(); + //client.StartAccepting(); // Connect to another peer /*{ diff --git a/src/common/TcpAcceptor.cpp b/src/common/TcpAcceptor.cpp index 23d9272..c4d51af 100644 --- a/src/common/TcpAcceptor.cpp +++ b/src/common/TcpAcceptor.cpp @@ -11,7 +11,7 @@ uint16_t TcpAcceptor::Initialize(const tcp::endpoint &localEndpoint) { // Open and bind the tcp acceptor to the local endpoint m_acceptor.open(localEndpoint.protocol()); - m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + //m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); m_acceptor.bind(localEndpoint); // Prepare to listen m_acceptor.listen(); diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index 0b4d2a8..c4ebc18 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -25,7 +25,8 @@ void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) { if (m_socket) throw TcpHandlerException("Socket alread created"); - m_socket.reset(new tcp::socket(m_ioService)); + m_socket.reset(new tcp::socket(m_ioService, destEndpoint.protocol())); + m_socket->set_option(tcp::socket::reuse_address(true)); m_socket->connect(destEndpoint); } diff --git a/src/common/TcpRequest.cpp b/src/common/TcpRequest.cpp index 5e37bab..d4432ec 100644 --- a/src/common/TcpRequest.cpp +++ b/src/common/TcpRequest.cpp @@ -66,19 +66,27 @@ void TcpRequest::ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint3 tcpHandler.Send(request, REQUEST_MAX_SIZE); } -void TcpRequest::SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId) +void TcpRequest::P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp, uint16_t publicPort) { m_document = Document(); // New Document /* Example: { Request-Type: 3 - Client-Id: 5 + Client-Id: 32 + Private-IP: 1.2.3.4 + Private-Port: 1234 + Public-IP: 2.3.5.6 + Public-Port: 2356 } */ m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)SEND_CLIENT_ADDR), m_document.GetAllocator()); + m_document.AddMember("Request-Type", Value((int)P2P_TCP), m_document.GetAllocator()); m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); + m_document.AddMember("Private-IP", Value(privateIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); + m_document.AddMember("Private-Port", Value((unsigned int)privatePort), m_document.GetAllocator()); + m_document.AddMember("Public-IP", Value(publicIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); + m_document.AddMember("Public-Port", Value((unsigned int)publicPort), m_document.GetAllocator()); // Send the "fixed-size" request char request[REQUEST_MAX_SIZE]; @@ -86,29 +94,6 @@ void TcpRequest::SendClientAddr(TcpHandler &tcpHandler, uint32_t clientId) tcpHandler.Send(request, REQUEST_MAX_SIZE); } -void TcpRequest::ReceiveClientAddr(TcpHandler &tcpHandler, uint32_t clientId, const std::string &ip, uint16_t port) -{ - m_document = Document(); // New Document - /* - Example: - { - Request-Type: 4 - Client-Id: 5 - IP: 23.123.2.3 - Port: 23456 - } - */ - m_document.SetObject(); - m_document.AddMember("Request-Type", Value((int)RECEIVE_CLIENT_ADDR), m_document.GetAllocator()); - m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); - m_document.AddMember("IP", Value(ip.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); - m_document.AddMember("Port", Value((unsigned int)port), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); -} void TcpRequest::ReceiveRequest(TcpHandler &tcpHandler) { @@ -149,17 +134,27 @@ uint32_t TcpRequest::GetMessageSize() return GetValue("Message-Size").GetUint();; } -uint16_t TcpRequest::GetPort() +uint32_t TcpRequest::GetClientId() { - return GetValue("Port").GetUint(); + return GetValue("Client-Id").GetUint(); } -uint32_t TcpRequest::GetClientId() +std::string TcpRequest::GetPrivateIp() { - return GetValue("Client-Id").GetUint(); + return GetValue("Private-IP").GetString(); +} + +uint16_t TcpRequest::GetPrivatePort() +{ + return GetValue("Private-Port").GetUint(); +} + +std::string TcpRequest::GetPublicIp() +{ + return GetValue("Public-IP").GetString(); } -std::string TcpRequest::GetIp() +uint16_t TcpRequest::GetPublicPort() { - return GetValue("IP").GetString(); + return GetValue("Public-Port").GetUint(); } \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 478c149..97e4fa7 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -59,7 +59,7 @@ void ClientsManager::ProcessClients() switch (m_requests.GetRequestType()) { - // Request to join chat, since this is the server, this is a request to join a group chat + // Request to join chat, since this is the sever, this is a request to join a group chat case TcpRequest::JOIN_CHAT: id = m_requests.GetGroupId(); // push the client id to the group @@ -72,16 +72,20 @@ void ClientsManager::ProcessClients() // receive the chat message ReceiveChat(i, id); break; - // Request to send a client address - case TcpRequest::SEND_CLIENT_ADDR: + + // Request for a p2p tcp connection to another client + case TcpRequest::P2P_TCP: id = m_requests.GetClientId(); if (id <= m_clients.size()) { - // Request the client for the address - m_requests.SendClientAddr(m_clients[id].connection, id); + // Send request to second client + m_requests.P2PTcp(m_clients[id].connection, i, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), + m_clients[i].connection.GetIp(), m_clients[i].connection.GetPort()); + // Receive the return request m_requests.ReceiveRequest(m_clients[id].connection); - // Send back a request filled with required info - m_requests.ReceiveClientAddr(m_clients[i].connection, id, m_clients[id].connection.GetIp(), m_requests.GetPort()); + // Send back the return request to first client + m_requests.P2PTcp(m_clients[i].connection, id, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), + m_clients[id].connection.GetIp(), m_clients[id].connection.GetPort()); } break; From 861d8ccca76204eb93656d6a58c72b085aa54093 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Sat, 27 Dec 2014 12:34:00 +0545 Subject: [PATCH 47/54] Few Improvements --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 8 +- .../Mirror-Client.vcxproj.filters | 20 +-- .../Mirror-Server.vcxproj.filters | 8 +- include/client/{TcpClient.h => Client.h} | 32 ++--- include/common/TcpHandler.h | 2 +- include/common/TcpRequest.h | 6 + src/client/{TcpClient.cpp => Client.cpp} | 125 +++++++++++------- src/client/main.cpp | 12 +- src/common/TcpHandler.cpp | 3 +- src/common/TcpRequest.cpp | 55 +++++--- src/server/ClientsManager.cpp | 9 +- 11 files changed, 170 insertions(+), 110 deletions(-) rename include/client/{TcpClient.h => Client.h} (59%) rename src/client/{TcpClient.cpp => Client.cpp} (60%) diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 05a5a23..72ed479 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -73,23 +73,23 @@ - + + - - + + - diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index 0aa54bb..e5dcfce 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -30,29 +30,26 @@ include\common\Network - - include - include\common\Network include\common\Network - + include\common\Network - + include\common\Network + + include + src - - src - src\common\Network @@ -62,11 +59,14 @@ src\common\Network - + src\common\Network - + src\common\Network + + src + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index 73f6865..da7b073 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -36,10 +36,10 @@ src\common\Network - + src\common\Network - + src\common\Network @@ -62,10 +62,10 @@ include\common\Network - + include\common\Network - + include\common\Network diff --git a/include/client/TcpClient.h b/include/client/Client.h similarity index 59% rename from include/client/TcpClient.h rename to include/client/Client.h index 67c962a..0883d4a 100644 --- a/include/client/TcpClient.h +++ b/include/client/Client.h @@ -1,26 +1,28 @@ #pragma once -#include #include #include -class TcpClient +class Client { public: - TcpClient(boost::asio::io_service &io); - ~TcpClient(); + Client(); + ~Client(); - // Connect to a peer/server - void Connect(const tcp::endpoint& peer, bool joinChat = false); + // Boost IO service + boost::asio::io_service& GetIOService() { return m_io; } + + // Connect to a peer/server, + size_t Connect(const tcp::endpoint& peer, bool* successful = NULL); // Connect to a peer/server asynchronously - void ConnectAsync(const tcp::endpoint& peer, bool joinChat = false); + void ConnectAsync(const tcp::endpoint& peer, bool* successful = NULL); // Connect to a peer through server - void Connect(uint32_t clientId); + size_t Connect(uint32_t clientId, bool* successful = NULL); // Start handling all requests that the client gets void HandleRequests(); - // Join chat - void JoinChat(uint32_t connectionId, uint32_t groupId); + // Join chat, for p2p set groupId to 0 + void JoinChat(uint32_t connectionId, uint32_t groupId = 0); // Set username to use while sending messages void SetName(const std::string &name) { m_name = name; } @@ -39,20 +41,20 @@ class TcpClient // maybe store userid and other stuffs here... }; - boost::asio::io_service &m_io; + boost::asio::io_service m_io; // List of the connections std::vector m_connections; - // Mutex to lock 'm_connections' list as well as 'm_request' request-processor + // Mutex to lock the use of common variables (m_connections/m_request/...) during multithreading boost::mutex m_mutex; TcpRequest m_request; std::string m_name; // For P2P: - bool m_p2pConnected; - void HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); - void HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint); + bool m_p2pConnecting; + size_t HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint, bool* successful = NULL); + void HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint, bool* successful = NULL); boost::shared_ptr m_acceptor; void P2PListen(const tcp::endpoint &localEndpoint); void P2PConnect(tcp::endpoint &remoteEndpoint); diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index a4d7860..a53f5ec 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -17,7 +17,7 @@ class TcpHandler // Initialize with an already created socket void Initialize(boost::shared_ptr socket); - // Connect to given endpoint and initialize by creating new socker + // Connect to given endpoint and initialize by creating new socket void Initialize(const tcp::endpoint &destEndpoint); // Send data of given size diff --git a/include/common/TcpRequest.h b/include/common/TcpRequest.h index d21c375..2dd9773 100644 --- a/include/common/TcpRequest.h +++ b/include/common/TcpRequest.h @@ -28,6 +28,8 @@ class TcpRequest // Request to establish a P2P TCP connection with a client // Contains private and public addess-port pairs and client-id void P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp = "", uint16_t publicPort = 0); + // Send an invalid request + void Invalid(TcpHandler& tcpHandler); // Receive a request void ReceiveRequest(TcpHandler &tcpHandler); @@ -48,4 +50,8 @@ class TcpRequest std::string GetJsonString(); // Helper function get a json value from a request inline rapidjson::Value& GetValue(const std::string &key); + // Helper function to initialize new request + inline void New(); + // Helper function to send the request + inline void Send(TcpHandler& tcpHandler); }; diff --git a/src/client/TcpClient.cpp b/src/client/Client.cpp similarity index 60% rename from src/client/TcpClient.cpp rename to src/client/Client.cpp index 0a6731d..3fcb703 100644 --- a/src/client/TcpClient.cpp +++ b/src/client/Client.cpp @@ -1,19 +1,19 @@ #include -#include +#include #include -TcpClient::TcpClient(boost::asio::io_service &io) -:m_io(io), /*m_acceptor(io),*/ m_currentConnection(-1) +Client::Client() +: m_currentConnection(-1) {} -TcpClient::~TcpClient() +Client::~Client() {} // Connecting to some remote peer (server/client) -void TcpClient::Connect(const tcp::endpoint& peer, bool joinChat) +size_t Client::Connect(const tcp::endpoint& peer, bool* successful) { // Lock the connections list not to process it while adding a connection - boost::lock_guard guard(m_mutex); + boost::lock_guard guard(m_mutex); // Add new connection m_connections.push_back(Connection(m_io)); @@ -21,57 +21,87 @@ void TcpClient::Connect(const tcp::endpoint& peer, bool joinChat) handler.Initialize(peer); std::cout << "Connected to: " << handler.GetDestinationAddress() << std::endl; - // Join chat in the new connection - if (joinChat) - JoinChat(m_connections.size() - 1, 0); + // Set successful variable to true + if (successful) + *successful = true; + return m_connections.size() - 1; } // Perform above function asynchronously -void TcpClient::ConnectAsync(const tcp::endpoint& peer, bool joinChat) +void Client::ConnectAsync(const tcp::endpoint& peer, bool* successful) { - boost::thread t(boost::bind((void(TcpClient::*)(const tcp::endpoint&, bool))&TcpClient::Connect, this, _1, _2), peer, joinChat); + boost::thread t(boost::bind((uint32_t(Client::*)(const tcp::endpoint&, bool*))&Client::Connect, this, _1, _2), peer, successful); } // Use server to connect to a client (for P2P) -void TcpClient::Connect(uint32_t clientId) +size_t Client::Connect(uint32_t clientId, bool* successful) { // make sure no requests are being processed during this time boost::lock_guard guard(m_mutex); // First send a P2P tcp connection request to server and receive back the returning request TcpHandler& handler = m_connections[0].tcpHandler; - m_request.P2PTcp(handler, clientId, handler.GetIp(), handler.GetPort()); + m_request.P2PTcp(handler, clientId, handler.GetSocket()->local_endpoint().address().to_string(), handler.GetSocket()->local_endpoint().port()); m_request.ReceiveRequest(handler); - HandleP2PRequest(m_request.GetClientId(), + // If invalid request return now + if (m_request.GetRequestType() != TcpRequest::P2P_TCP) + { + if (successful) + *successful = false; + return 0; + } + + // Use the return request parameters to try connecting to other peer + return HandleP2PRequest(m_request.GetClientId(), tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPrivateIp()), m_request.GetPrivatePort()), - tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort())); + tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort()), successful); } -void TcpClient::HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint) +size_t Client::HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint, bool* successful) { + m_p2pConnecting = true; + // The '0' connection is assumed to be a TCP connection with the server TcpHandler& handler = m_connections[0].tcpHandler; // Start listening at same local endpoint as which is connected to the server - boost::thread t1(boost::bind(&TcpClient::P2PListen, this, _1), handler.GetSocket()->local_endpoint()); + boost::thread t1(boost::bind(&Client::P2PListen, this, _1), handler.GetSocket()->local_endpoint()); // Try connecting to both private and public endpoints of other peer - boost::thread t2(boost::bind(&TcpClient::P2PConnect, this, _1), privateEndpoint); - boost::thread t3(boost::bind(&TcpClient::P2PConnect, this, _1), publicEndpoint); - - while (!m_p2pConnected); - if (m_acceptor) - m_acceptor->cancel(); + if (privateEndpoint != publicEndpoint) + boost::thread t2(boost::bind(&Client::P2PConnect, this, _1), privateEndpoint); + boost::thread t3(boost::bind(&Client::P2PConnect, this, _1), publicEndpoint); + + /* + TODO: Wait for connection for certain time only after which, set m_p2pConnecting to false + */ + while (m_p2pConnecting); + try + { + if (m_acceptor) + m_acceptor->cancel(); + } + catch (...) {} + // Set successful variable to true + if (successful) *successful = true; + return m_connections.size() - 1; } -void TcpClient::HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint) +void Client::HandleP2PRequestAsync(uint32_t clientId, const tcp::endpoint &privateEndpoint, const tcp::endpoint &publicEndpoint, bool* successful) { - boost::thread t([this, clientId, privateEndpoint, publicEndpoint]() + boost::thread t([this, clientId, privateEndpoint, publicEndpoint, successful]() /* C++11 lambda function */ { - boost::lock_guard guard(m_mutex); - HandleP2PRequest(clientId, privateEndpoint, publicEndpoint); + try + { + boost::lock_guard guard(m_mutex); + HandleP2PRequest(clientId, privateEndpoint, publicEndpoint, successful); + } + catch (std::exception &ex) + { + std::cout << ex.what() << std::endl; + } }); } -void TcpClient::P2PListen(const tcp::endpoint &localEndpoint) +void Client::P2PListen(const tcp::endpoint &localEndpoint) { try { @@ -80,45 +110,45 @@ void TcpClient::P2PListen(const tcp::endpoint &localEndpoint) boost::shared_ptr socket(new tcp::socket(m_acceptor->get_io_service())); // Wait for a connection and accept at the socket m_acceptor->accept(*socket); - + if (!m_p2pConnecting) return; + /* TODO: Need to verify if this is the required peer */ Connection c(m_io); c.tcpHandler.Initialize(socket); m_connections.push_back(c); - m_p2pConnected = true; - - m_acceptor.reset(); + m_p2pConnecting = false; } catch (std::exception &ex) { - //std::cout << ex.what() << std::endl; + std::cout << ex.what() << std::endl; } } ; -void TcpClient::P2PConnect(tcp::endpoint &remoteEndpoint) +void Client::P2PConnect(tcp::endpoint &remoteEndpoint) { - while (!m_p2pConnected) + while (m_p2pConnecting) { try { // Try connecting at the given remote endpoint Connection c(m_io); c.tcpHandler.Initialize(remoteEndpoint); + if (!m_p2pConnecting) return; + m_connections.push_back(c); - m_p2pConnected = true; - JoinChat(m_connections.size() - 1, 0); + m_p2pConnecting = false; } catch (std::exception &ex) { - //std::cout << ex.what() << std::endl; + std::cout << ex.what() << std::endl; } } } // An infinite loop to handle incoming requests -void TcpClient::HandleRequests() +void Client::HandleRequests() { while (true) { @@ -146,12 +176,14 @@ void TcpClient::HandleRequests() case TcpRequest::JOIN_CHAT: m_currentConnection = i; break; - // Request to wait for incoming chat message + + // Request to receive an incoming chat message case TcpRequest::CHAT_MESSAGE: chat.Receive(m_connections[i].tcpHandler, m_request.GetMessageSize()); m_currentConnection = i; std::cout << "\n\n" << chat.GetMessage() << "\n\nYou: "; break; + // Request for a p2p tcp connection case TcpRequest::P2P_TCP: id = m_request.GetClientId(); @@ -159,7 +191,8 @@ void TcpClient::HandleRequests() tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPrivateIp()), m_request.GetPrivatePort()), tcp::endpoint(boost::asio::ip::address::from_string(m_request.GetPublicIp()), m_request.GetPublicPort())); m_request.P2PTcp(m_connections[0].tcpHandler, id, - m_connections[0].tcpHandler.GetIp(), m_connections[0].tcpHandler.GetPort()); + m_connections[0].tcpHandler.GetSocket()->local_endpoint().address().to_string(), m_connections[0].tcpHandler.GetSocket()->local_endpoint().port() + ); break; default: std::cout << "Invalid Request " << m_request.GetRequestType() << std::endl; @@ -175,24 +208,24 @@ void TcpClient::HandleRequests() } } -void TcpClient::StartChatInput(uint32_t groupId) +void Client::StartChatInput(uint32_t groupId) { - boost::thread t(boost::bind(&TcpClient::ChatInput, this, _1), groupId); + boost::thread t(boost::bind(&Client::ChatInput, this, _1), groupId); } -void TcpClient::JoinChat(uint32_t connectionId, uint32_t groupId) +void Client::JoinChat(uint32_t connectionId, uint32_t groupId) { m_request.JoinChat(m_connections[connectionId].tcpHandler, groupId); m_currentConnection = connectionId; } -void TcpClient::ChatInput(uint32_t groupId) +void Client::ChatInput(uint32_t groupId) { while (true) { while (m_currentConnection == -1) boost::this_thread::sleep(boost::posix_time::milliseconds(500)); - + // Take input std::cout << "\nYou: "; fflush(stdin); diff --git a/src/client/main.cpp b/src/client/main.cpp index 0dca2a5..43566fa 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -1,15 +1,14 @@ /* Client - main.cpp */ #include -#include +#include int main(int argc, char *argv[]) { try { - boost::asio::io_service io; - TcpClient client(io); + Client client; //client.StartAccepting(); // Connect to another peer @@ -49,7 +48,12 @@ int main(int argc, char *argv[]) { uint32_t cid; std::cout << "Enter client-id of peer: "; std::cin >> cid; - client.Connect(cid); + bool successfull; + uint32_t id = client.Connect(cid, &successfull); + if (successfull) + client.JoinChat(id); + else + std::cout << "Invalid peer; couldn't connect" << std::endl; } client.StartChatInput(groupId); client.HandleRequests(); diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index c4ebc18..7b91daa 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -25,8 +25,7 @@ void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) { if (m_socket) throw TcpHandlerException("Socket alread created"); - m_socket.reset(new tcp::socket(m_ioService, destEndpoint.protocol())); - m_socket->set_option(tcp::socket::reuse_address(true)); + m_socket.reset(new tcp::socket(m_ioService)); m_socket->connect(destEndpoint); } diff --git a/src/common/TcpRequest.cpp b/src/common/TcpRequest.cpp index d4432ec..9670d80 100644 --- a/src/common/TcpRequest.cpp +++ b/src/common/TcpRequest.cpp @@ -24,29 +24,37 @@ std::string TcpRequest::GetJsonString() return buffer.GetString(); } +void TcpRequest::New() +{ + m_document = Document(); + m_document.SetObject(); +} + +void TcpRequest::Send(TcpHandler& tcpHandler) +{ + // Send the "fixed-size" request + char request[REQUEST_MAX_SIZE]; + strcpy(request, GetJsonString().c_str()); + tcpHandler.Send(request, REQUEST_MAX_SIZE); +} + void TcpRequest::JoinChat(TcpHandler &tcpHandler, uint32_t groupId) { - m_document = Document(); // New Document /* Example: { Request-Type: 1 Group-Id: 20 } - */ - m_document.SetObject(); + */ + New(); m_document.AddMember("Request-Type", Value((int)JOIN_CHAT), m_document.GetAllocator()); m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); + Send(tcpHandler); } void TcpRequest::ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint32_t groupId) { - m_document = Document(); // New Document /* Example: { @@ -55,20 +63,15 @@ void TcpRequest::ChatMessage(TcpHandler &tcpHandler, uint32_t messageSize, uint3 Message-Size: 50 } */ - m_document.SetObject(); + New(); m_document.AddMember("Request-Type", Value((int)CHAT_MESSAGE), m_document.GetAllocator()); m_document.AddMember("Group-Id", Value(groupId), m_document.GetAllocator()); m_document.AddMember("Message-Size", Value(messageSize), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); + Send(tcpHandler);; } void TcpRequest::P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::string &privateIp, uint16_t privatePort, const std::string &publicIp, uint16_t publicPort) { - m_document = Document(); // New Document /* Example: { @@ -80,20 +83,28 @@ void TcpRequest::P2PTcp(TcpHandler& tcpHandler, uint32_t clientId, const std::st Public-Port: 2356 } */ - m_document.SetObject(); + New(); m_document.AddMember("Request-Type", Value((int)P2P_TCP), m_document.GetAllocator()); m_document.AddMember("Client-Id", Value(clientId), m_document.GetAllocator()); m_document.AddMember("Private-IP", Value(privateIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); m_document.AddMember("Private-Port", Value((unsigned int)privatePort), m_document.GetAllocator()); m_document.AddMember("Public-IP", Value(publicIp.c_str(), m_document.GetAllocator()), m_document.GetAllocator()); m_document.AddMember("Public-Port", Value((unsigned int)publicPort), m_document.GetAllocator()); - - // Send the "fixed-size" request - char request[REQUEST_MAX_SIZE]; - strcpy(request, GetJsonString().c_str()); - tcpHandler.Send(request, REQUEST_MAX_SIZE); + Send(tcpHandler); } +void TcpRequest::Invalid(TcpHandler& tcpHandler) +{ + /* + Example: + { + Request-Type: 0 + } + */ + New(); + m_document.AddMember("Request-Type", Value((int)INAVLID_TYPE), m_document.GetAllocator()); + Send(tcpHandler); +} void TcpRequest::ReceiveRequest(TcpHandler &tcpHandler) { diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 97e4fa7..39f3c22 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -59,7 +59,7 @@ void ClientsManager::ProcessClients() switch (m_requests.GetRequestType()) { - // Request to join chat, since this is the sever, this is a request to join a group chat + // Request to join chat, since this is the server, this is a request to join a group chat case TcpRequest::JOIN_CHAT: id = m_requests.GetGroupId(); // push the client id to the group @@ -76,7 +76,7 @@ void ClientsManager::ProcessClients() // Request for a p2p tcp connection to another client case TcpRequest::P2P_TCP: id = m_requests.GetClientId(); - if (id <= m_clients.size()) + if (id < m_clients.size() && id != i) { // Send request to second client m_requests.P2PTcp(m_clients[id].connection, i, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), @@ -87,6 +87,11 @@ void ClientsManager::ProcessClients() m_requests.P2PTcp(m_clients[i].connection, id, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), m_clients[id].connection.GetIp(), m_clients[id].connection.GetPort()); } + else + { + // Send an invalid request + m_requests.Invalid(m_clients[i].connection); + } break; default: From 33b72d0ffc734bc718ea6c92eee0fcfc3a7ad8ce Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Sun, 28 Dec 2014 18:16:12 +0545 Subject: [PATCH 48/54] Add UdpHandler class --- MSVC_2013/Mirror-Client/Mirror-Client.vcxproj | 2 + .../Mirror-Client.vcxproj.filters | 6 +++ MSVC_2013/Mirror-Server/Mirror-Server.vcxproj | 2 + .../Mirror-Server.vcxproj.filters | 6 +++ include/common/RtpHandler.h | 5 ++- include/common/TcpHandler.h | 44 +++++++++---------- include/common/UdpHandler.h | 30 +++++++++++++ src/common/RtpHandler.cpp | 14 +++--- src/common/TcpHandler.cpp | 16 ++++--- src/common/UdpHandler.cpp | 38 ++++++++++++++++ src/server/ClientsManager.cpp | 4 +- 11 files changed, 128 insertions(+), 39 deletions(-) create mode 100644 include/common/UdpHandler.h create mode 100644 src/common/UdpHandler.cpp diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj index 72ed479..29c9169 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj @@ -81,6 +81,7 @@ + @@ -90,6 +91,7 @@ + diff --git a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters index e5dcfce..0617b05 100644 --- a/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters +++ b/MSVC_2013/Mirror-Client/Mirror-Client.vcxproj.filters @@ -45,6 +45,9 @@ include + + include\common\Network + @@ -68,5 +71,8 @@ src + + src\common\Network + \ No newline at end of file diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj index 7deb028..3b44b56 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj @@ -75,6 +75,7 @@ + @@ -86,6 +87,7 @@ + diff --git a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters index da7b073..9ff48c9 100644 --- a/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters +++ b/MSVC_2013/Mirror-Server/Mirror-Server.vcxproj.filters @@ -42,6 +42,9 @@ src\common\Network + + src\common\Network + @@ -68,5 +71,8 @@ include\common\Network + + include\common\Network + \ No newline at end of file diff --git a/include/common/RtpHandler.h b/include/common/RtpHandler.h index 9246ce3..9640544 100644 --- a/include/common/RtpHandler.h +++ b/include/common/RtpHandler.h @@ -1,4 +1,5 @@ #pragma once +#include "UdpHandler.h" class RtpTransmissionException : public Exception { @@ -22,7 +23,7 @@ class RtpHandler RtpHandler(); ~RtpHandler(); - void Initialize(boost::shared_ptr socket, const udp::endpoint &remoteEndpoint); + void Initialize(boost::shared_ptr udpHandler, const udp::endpoint &remoteEndpoint); void CleanUp(); int GetTimeStamp() const { return m_timeStamp; } @@ -42,7 +43,7 @@ class RtpHandler void Send(const char *data, size_t size); void Receive(char* data, size_t maxSize = 1500); private: - boost::shared_ptr m_socket; + boost::shared_ptr m_udpHandler; udp::endpoint m_remoteEndpoint; uint16_t m_sequenceNumber; diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index a53f5ec..45a17ee 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -11,30 +11,30 @@ class TcpHandlerException : public Exception class TcpHandler { - public: - TcpHandler(boost::asio::io_service &ioService); - ~TcpHandler(); +public: + TcpHandler(boost::asio::io_service &ioService); + ~TcpHandler(); - // Initialize with an already created socket - void Initialize(boost::shared_ptr socket); - // Connect to given endpoint and initialize by creating new socket - void Initialize(const tcp::endpoint &destEndpoint); + // Initialize with an already created socket + void Initialize(boost::shared_ptr socket); + // Connect to given endpoint and initialize by creating new socket + void Initialize(const tcp::endpoint &destEndpoint); - // Send data of given size - void Send(const char* data, size_t size); - // Receive data of given size - void Receive(char* data, size_t size); - // Receive data of given size or less - void ReceiveSome(char* data, size_t max_size); - // Check if the socket has data to be read - size_t Available(); + // Send data of given size + void Send(const char* data, size_t size); + // Receive data of given size + void Receive(char* data, size_t size); + // Receive data of given size or less + size_t ReceiveSome(char* data, size_t max_size); + // Check if the socket has data to be read + size_t Available(); - std::string GetDestinationAddress() const; - std::string GetIp() const { return m_socket->remote_endpoint().address().to_string(); } - uint16_t GetPort() const { return m_socket->remote_endpoint().port(); } - boost::shared_ptr GetSocket() const { return m_socket; } + std::string GetDestinationAddress() const; + std::string GetRemoteIp() const { return m_socket->remote_endpoint().address().to_string(); } + uint16_t GetRemotePort() const { return m_socket->remote_endpoint().port(); } + boost::shared_ptr GetSocket() const { return m_socket; } - private: - boost::shared_ptr m_socket; - boost::asio::io_service &m_ioService; +private: + boost::shared_ptr m_socket; + boost::asio::io_service &m_ioService; }; diff --git a/include/common/UdpHandler.h b/include/common/UdpHandler.h new file mode 100644 index 0000000..79f5272 --- /dev/null +++ b/include/common/UdpHandler.h @@ -0,0 +1,30 @@ +#pragma once + +class UdpHandlerException : public Exception +{ +public: + UdpHandlerException(const std::string &errorString) : + Exception("UDP Handler Error: " + errorString) + {} +}; + +class UdpHandler +{ +public: + UdpHandler(boost::asio::io_service &ioService); + ~UdpHandler(); + + // Initialize with an already created socket + void Initialize(boost::shared_ptr socket); + // Initialize with a socket bound to given local endpoint + void Initialize(const udp::endpoint &localEndpoint); + + // Send data to a remote endpoint + void Send(const udp::endpoint &remoteEndpoint, const char* data, size_t size); + // Receive data from a remote endpoint; the remoteEndpoint is automatically populated + size_t Receive(udp::endpoint &remoteEndpoint, char* data, size_t maxSize); + +private: + boost::shared_ptr m_socket; + boost::asio::io_service &m_ioService; +}; \ No newline at end of file diff --git a/src/common/RtpHandler.cpp b/src/common/RtpHandler.cpp index 80a331f..fa20f41 100644 --- a/src/common/RtpHandler.cpp +++ b/src/common/RtpHandler.cpp @@ -10,9 +10,9 @@ RtpHandler::~RtpHandler() CleanUp(); } -void RtpHandler::Initialize(boost::shared_ptr socket, const udp::endpoint &remoteEndpoint) +void RtpHandler::Initialize(boost::shared_ptr udpHandler, const udp::endpoint &remoteEndpoint) { - m_socket = socket; + m_udpHandler = udpHandler; m_remoteEndpoint = remoteEndpoint; m_sequenceNumber = m_timeStamp = 0; m_timeStampIncrement = 3600; // for 25fps video @@ -20,13 +20,13 @@ void RtpHandler::Initialize(boost::shared_ptr socket, const udp::en void RtpHandler::CleanUp() { - m_socket.reset(); + m_udpHandler.reset(); m_remoteEndpoint = udp::endpoint(); } void RtpHandler::Send(const char *data, size_t size) { - if (!m_socket) + if (!m_udpHandler) throw RtpTransmissionException("RTP Transmitter hasn't been properly initialized"); std::vector packet; @@ -50,18 +50,18 @@ void RtpHandler::Send(const char *data, size_t size) m_sequenceNumber++; m_timeStamp += m_timeStampIncrement; - m_socket->send_to(boost::asio::buffer(packet), m_remoteEndpoint); + m_udpHandler->Send(m_remoteEndpoint, &packet[0], packet.size()); } void RtpHandler::Receive(char* data, size_t maxSize) { - if (!m_socket) + if (!m_udpHandler) throw RtpReceptionError("RTP Receiver hasn't been properly initialized"); std::vector packet; packet.resize(maxSize); - size_t len = m_socket->receive_from(boost::asio::buffer(packet), m_remoteEndpoint); + size_t len = m_udpHandler->Receive(m_remoteEndpoint, &packet[0], packet.size()); packet.resize(len); char version = packet[0]; diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index 7b91daa..8eca292 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -32,26 +32,30 @@ void TcpHandler::Initialize(const tcp::endpoint &destEndpoint) // void TcpHandler::Send(const char* data, size_t size) { - if (!m_socket) return; + if (!m_socket) + throw TcpHandlerException("Socket not created"); boost::asio::write(*m_socket, boost::asio::buffer(data, size)); } // void TcpHandler::Receive(char* data, size_t size) { - if (!m_socket) return; + if (!m_socket) + throw TcpHandlerException("Socket not created"); boost::asio::read(*m_socket, boost::asio::buffer(data, size)); } -void TcpHandler::ReceiveSome(char* data, size_t max_size) +size_t TcpHandler::ReceiveSome(char* data, size_t max_size) { - if (!m_socket) return; - m_socket->read_some(boost::asio::buffer(data, max_size)); + if (!m_socket) + throw TcpHandlerException("Socket not created"); + return m_socket->read_some(boost::asio::buffer(data, max_size)); } size_t TcpHandler::Available() { - if (!m_socket) return 0; + if (!m_socket) + throw TcpHandlerException("Socket not created"); return m_socket->available(); } diff --git a/src/common/UdpHandler.cpp b/src/common/UdpHandler.cpp new file mode 100644 index 0000000..2033291 --- /dev/null +++ b/src/common/UdpHandler.cpp @@ -0,0 +1,38 @@ +#include +#include + +UdpHandler::UdpHandler(boost::asio::io_service &ioService) +: m_ioService(ioService) +{} + +UdpHandler::~UdpHandler() +{} + + +void UdpHandler::Initialize(boost::shared_ptr socket) +{ + if (m_socket) + throw UdpHandlerException("Socket alread created"); + m_socket = socket; +} + +void UdpHandler::Initialize(const udp::endpoint &localEndpoint) +{ + if (m_socket) + throw UdpHandlerException("Socket alread created"); + m_socket.reset(new udp::socket(m_ioService, localEndpoint)); +} + +void UdpHandler::Send(const udp::endpoint &remoteEndpoint, const char* data, size_t size) +{ + if (m_socket) + throw UdpHandlerException("Socket not created"); + m_socket->send_to(boost::asio::buffer(data, size), remoteEndpoint); +} + +size_t UdpHandler::Receive(udp::endpoint &remoteEndpoint, char* data, size_t maxSize) +{ + if (m_socket) + throw UdpHandlerException("Socket not created"); + return m_socket->receive_from(boost::asio::buffer(data, maxSize), remoteEndpoint); +} \ No newline at end of file diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index 39f3c22..dcef56c 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -80,12 +80,12 @@ void ClientsManager::ProcessClients() { // Send request to second client m_requests.P2PTcp(m_clients[id].connection, i, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), - m_clients[i].connection.GetIp(), m_clients[i].connection.GetPort()); + m_clients[i].connection.GetRemoteIp(), m_clients[i].connection.GetRemotePort()); // Receive the return request m_requests.ReceiveRequest(m_clients[id].connection); // Send back the return request to first client m_requests.P2PTcp(m_clients[i].connection, id, m_requests.GetPrivateIp(), m_requests.GetPrivatePort(), - m_clients[id].connection.GetIp(), m_clients[id].connection.GetPort()); + m_clients[id].connection.GetRemoteIp(), m_clients[id].connection.GetRemotePort()); } else { From b797b41c6e872d0c5a8d08d19a48fd4ec6274119 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Mon, 29 Dec 2014 10:58:38 +0545 Subject: [PATCH 49/54] Client Event Handlers added --- include/client/Client.h | 35 ++++++--- src/client/Client.cpp | 65 ++++++++-------- src/client/main.cpp | 141 ++++++++++++++++++++++++---------- src/server/ClientsManager.cpp | 1 + 4 files changed, 157 insertions(+), 85 deletions(-) diff --git a/include/client/Client.h b/include/client/Client.h index 0883d4a..d0f60f9 100644 --- a/include/client/Client.h +++ b/include/client/Client.h @@ -2,7 +2,15 @@ #include #include +/* Data passed to the message event handler */ +struct MessageEventData +{ + /* peer-id; 0 is for server itself */ + size_t senderId; + std::string message; +}; +/* Class to handle networking connections in client application */ class Client { public: @@ -21,16 +29,25 @@ class Client // Start handling all requests that the client gets void HandleRequests(); - // Join chat, for p2p set groupId to 0 - void JoinChat(uint32_t connectionId, uint32_t groupId = 0); + // Set the event handler to handle incoming chat messages + void SetMessageEventHandler(std::function handler) { m_messageHandler = handler; } + // Send chat message to a connection (receiverId = 0 for server) + void SendMessage(size_t receiverId, const std::string& message, uint32_t groupId = 0); + // Event handler to handle a join chat request from a peer (for p2p only) + void SetJoinChatEventHandler(std::function handler) { m_joinChatHandler = handler; } + + // Send a join chat request; on successful returns true + bool JoinChat(uint32_t connectionId, uint32_t groupId = 0); // Set username to use while sending messages void SetName(const std::string &name) { m_name = name; } - - // For console: - // Start Chat Input in a separate thread - void StartChatInput(uint32_t groupId); + const std::string& GetName() { return m_name; } private: + boost::asio::io_service m_io; + // Event handlers + std::function m_messageHandler; + std::function m_joinChatHandler; + // Connection representing a tcp-connection with a peer/server struct Connection { @@ -41,8 +58,6 @@ class Client // maybe store userid and other stuffs here... }; - boost::asio::io_service m_io; - // List of the connections std::vector m_connections; // Mutex to lock the use of common variables (m_connections/m_request/...) during multithreading @@ -59,8 +74,4 @@ class Client void P2PListen(const tcp::endpoint &localEndpoint); void P2PConnect(tcp::endpoint &remoteEndpoint); - // For console: - int m_currentConnection; - // Separate thread for inputting chat messages - void ChatInput(uint32_t groupId); }; \ No newline at end of file diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 3fcb703..838b9c3 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -3,7 +3,6 @@ #include Client::Client() -: m_currentConnection(-1) {} Client::~Client() @@ -105,6 +104,8 @@ void Client::P2PListen(const tcp::endpoint &localEndpoint) { try { + if (m_acceptor) + m_acceptor->cancel(); m_acceptor.reset(new tcp::acceptor(m_io, localEndpoint, true)); // Create a new socket to represent a new connection boost::shared_ptr socket(new tcp::socket(m_acceptor->get_io_service())); @@ -166,6 +167,7 @@ void Client::HandleRequests() size_t bytes = m_connections[i].tcpHandler.Available(); ChatMessage chat; uint32_t id; + bool joinChat; // if so, process accordingly if (bytes > 0) { @@ -174,14 +176,26 @@ void Client::HandleRequests() { // Request to join chat conversation case TcpRequest::JOIN_CHAT: - m_currentConnection = i; + if (m_joinChatHandler) + joinChat = m_joinChatHandler(i); + else + joinChat = true; + if (joinChat) + m_request.JoinChat(m_connections[i].tcpHandler, m_request.GetGroupId()); + else + m_request.Invalid(m_connections[i].tcpHandler); break; // Request to receive an incoming chat message case TcpRequest::CHAT_MESSAGE: chat.Receive(m_connections[i].tcpHandler, m_request.GetMessageSize()); - m_currentConnection = i; - std::cout << "\n\n" << chat.GetMessage() << "\n\nYou: "; + if (m_messageHandler) + { + MessageEventData msgData; + msgData.senderId = i; + msgData.message = chat.GetMessage(); + m_messageHandler(msgData); + } break; // Request for a p2p tcp connection @@ -208,36 +222,23 @@ void Client::HandleRequests() } } -void Client::StartChatInput(uint32_t groupId) -{ - boost::thread t(boost::bind(&Client::ChatInput, this, _1), groupId); -} - -void Client::JoinChat(uint32_t connectionId, uint32_t groupId) +bool Client::JoinChat(uint32_t connectionId, uint32_t groupId) { m_request.JoinChat(m_connections[connectionId].tcpHandler, groupId); - m_currentConnection = connectionId; + // We need a JOIN_CHAT request back from other peer/server to ensure chat is joined + m_request.ReceiveRequest(m_connections[connectionId].tcpHandler); + if (m_request.GetRequestType() != TcpRequest::JOIN_CHAT) + return false; + return true; } -void Client::ChatInput(uint32_t groupId) +void Client::SendMessage(size_t receiverId, const std::string& message, uint32_t groupId) { - while (true) - { - while (m_currentConnection == -1) - boost::this_thread::sleep(boost::posix_time::milliseconds(500)); - - // Take input - std::cout << "\nYou: "; - fflush(stdin); - char input[1024]; - - std::cin.getline(input, 1024); - std::string message = m_name + ": " + input; - - // Send the chat message after a GROUP_CHAT request - m_request.ChatMessage(m_connections[m_currentConnection].tcpHandler, message.size() + 1, groupId); - ChatMessage chat; - chat.SetMessage(message); - chat.Send(m_connections[m_currentConnection].tcpHandler); - } -} + if (receiverId >= m_connections.size()) + throw Exception("Invalid Connection-Id to send message to"); + // Send the chat message after a CHAT_MESSAGE request + m_request.ChatMessage(m_connections[receiverId].tcpHandler, message.size() + 1, groupId); + ChatMessage chat; + chat.SetMessage(message); + chat.Send(m_connections[receiverId].tcpHandler); +} \ No newline at end of file diff --git a/src/client/main.cpp b/src/client/main.cpp index 43566fa..eb1c276 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -3,62 +3,77 @@ #include #include +// Since this is console, we need to keep track of which connection is going to receive chat messages entered +size_t chatConnectionId = -1; // (#CONSOLE_PROBLEMS) + +// A function to continuously take chat inputs and send it to peer/server defined by chatConnectionId +void ChatInput(Client& client, size_t groupId); +// Function to handle JOIN_CHAT requests +bool JoinChatEventHandler(size_t id); +// Function to handler incoming chat messages +void MessageEventHandler(MessageEventData& data); int main(int argc, char *argv[]) { try { Client client; - //client.StartAccepting(); - - // Connect to another peer - /*{ - std::cout << "Enter port to connect to: "; - std::cin >> port; - client.Connect(tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), port)); - while (true); - }*/ - // Connect to server - { - std::string ip, name; - std::cout << "Enter name: "; - std::fflush(stdin); - char namec[200]; - std::cin.getline(namec, 200); - name = std::string(namec); - client.SetName(name); + /* Set Event Handlers */ + client.SetJoinChatEventHandler(JoinChatEventHandler); + client.SetMessageEventHandler(MessageEventHandler); + std::string ip, name; + /* Get name of user */ + std::cout << "Enter name: "; + std::fflush(stdin); + char namec[200]; + std::cin.getline(namec, 200); + name = std::string(namec); + client.SetName(name); - std::cout << "Enter ip of server: "; std::cin >> ip; - client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 10011)); + /* Connection to server */ + std::cout << "Enter ip of server: "; std::cin >> ip; + client.Connect(tcp::endpoint(boost::asio::ip::address::from_string(ip), 10011)); - int choice; - std::cout << "Enter 0 for group chat, 1 for P2P, 2 to just wait: "; - std::cin >> choice; + /* Menu of choices */ + int choice; + std::cout << "Enter 0 for group chat, 1 for P2P, 2 to just wait: "; + std::cin >> choice; - uint32_t groupId = 0; - if (choice == 0) - { - std::cout << "Enter group-id to join: "; - std::cin >> groupId; - client.JoinChat(0, groupId); - } - else if (choice == 1) + uint32_t groupId = 0; + if (choice == 0) + { + /* For group chat, send JOIN_CHAT request to server */ + /* The server will redirect all chat messages to every client that has JOINed_CHAT with it on same groupId */ + std::cout << "Enter group-id to join: "; + std::cin >> groupId; + client.JoinChat(0, groupId); + chatConnectionId = 0; + } + else if (choice == 1) + { + /* For p2p request, first connect to the peer...*/ + uint32_t cid; + std::cout << "Enter client-id of peer: "; std::cin >> cid; + bool successfull; + uint32_t id = client.Connect(cid, &successfull); + /* ... and if successfull, send JOIN_CHAT request to it*/ + if (successfull) { - uint32_t cid; - std::cout << "Enter client-id of peer: "; std::cin >> cid; - bool successfull; - uint32_t id = client.Connect(cid, &successfull); - if (successfull) - client.JoinChat(id); + if (!client.JoinChat(id)) // this is handled in other peer through JoinChatEventHandler + std::cout << "Couldn't connect; other side may not have accepted the request" << std::endl; else - std::cout << "Invalid peer; couldn't connect" << std::endl; + chatConnectionId = id; } - client.StartChatInput(groupId); - client.HandleRequests(); - + else + std::cout << "Invalid peer; couldn't connect" << std::endl; } + + boost::thread t(boost::bind(&ChatInput, _1, _2), boost::ref(client), groupId); + client.HandleRequests(); + + } catch (std::exception &ex) { @@ -68,3 +83,47 @@ int main(int argc, char *argv[]) std::cin.get(); return 0; } + + +bool JoinChatEventHandler(size_t id) +{ + char c; + std::cout << "Connection #" << id << " wants to join a conversation. Accept? (y/n) "; std::cin >> c; + if (c == 'y') + { + chatConnectionId = id; + return true; + } + return false; +} + +void MessageEventHandler(MessageEventData& data) +{ + // When connected to multiple peers, set chatConnectedId (id to send messages to) + // to whoever sent the last message (#CONSOLE_PROBLEMS) + chatConnectionId = data.senderId; + std::cout << "\n\n" << data.message << "\n\nYou: "; +} + +void ChatInput(Client& client, size_t groupId) +{ + while (true) + { + // Don't take input, unless someone is conneced + while (chatConnectionId == -1) + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + + // Take input + std::cout << "\nYou: "; + fflush(stdin); + char input[1024]; + + // Create message along with client's name + // TODO: donot send message with name: instead append the name on receiving side + std::cin.getline(input, 1024); + std::string message = client.GetName() + ": " + input; + + // Send the message + client.SendMessage(chatConnectionId, message, groupId); + } +} diff --git a/src/server/ClientsManager.cpp b/src/server/ClientsManager.cpp index dcef56c..a12edea 100644 --- a/src/server/ClientsManager.cpp +++ b/src/server/ClientsManager.cpp @@ -62,6 +62,7 @@ void ClientsManager::ProcessClients() // Request to join chat, since this is the server, this is a request to join a group chat case TcpRequest::JOIN_CHAT: id = m_requests.GetGroupId(); + m_requests.JoinChat(m_clients[i].connection, id); // push the client id to the group m_groups[id].push_back(i); std::cout << "Connected client #" << i << " to group #" << id << std::endl; From 159edcd1c89b57afe995e0dcb02ed1015495ae85 Mon Sep 17 00:00:00 2001 From: Bibek Dahal Date: Mon, 29 Dec 2014 11:28:11 +0545 Subject: [PATCH 50/54] Few errors fixed --- src/client/Client.cpp | 18 ++++++++++-------- src/client/main.cpp | 10 +++++----- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 838b9c3..d4d412d 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -76,11 +76,15 @@ size_t Client::HandleP2PRequest(uint32_t clientId, const tcp::endpoint &privateE try { if (m_acceptor) - m_acceptor->cancel(); + m_acceptor->close(); } catch (...) {} // Set successful variable to true - if (successful) *successful = true; + if (successful) + { + *successful = true; + std::cout << "Connected to client #" << clientId << " at connection: #" << m_connections.size() - 1; + } return m_connections.size() - 1; } @@ -104,8 +108,6 @@ void Client::P2PListen(const tcp::endpoint &localEndpoint) { try { - if (m_acceptor) - m_acceptor->cancel(); m_acceptor.reset(new tcp::acceptor(m_io, localEndpoint, true)); // Create a new socket to represent a new connection boost::shared_ptr socket(new tcp::socket(m_acceptor->get_io_service())); @@ -121,9 +123,9 @@ void Client::P2PListen(const tcp::endpoint &localEndpoint) m_connections.push_back(c); m_p2pConnecting = false; } - catch (std::exception &ex) + catch (/*std::exception &ex*/...) { - std::cout << ex.what() << std::endl; + //std::cout << "Listener: " << ex.what() << std::endl; } } ; @@ -141,9 +143,9 @@ void Client::P2PConnect(tcp::endpoint &remoteEndpoint) m_connections.push_back(c); m_p2pConnecting = false; } - catch (std::exception &ex) + catch (/*std::exception &ex*/...) { - std::cout << ex.what() << std::endl; + //std::cout << "Connector: " << ex.what() << std::endl; } } } diff --git a/src/client/main.cpp b/src/client/main.cpp index eb1c276..06ed2dc 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -56,10 +56,10 @@ int main(int argc, char *argv[]) /* For p2p request, first connect to the peer...*/ uint32_t cid; std::cout << "Enter client-id of peer: "; std::cin >> cid; - bool successfull; - uint32_t id = client.Connect(cid, &successfull); - /* ... and if successfull, send JOIN_CHAT request to it*/ - if (successfull) + bool successful; + uint32_t id = client.Connect(cid, &successful); + /* ... and if successful, send JOIN_CHAT request to it*/ + if (successful) { if (!client.JoinChat(id)) // this is handled in other peer through JoinChatEventHandler std::cout << "Couldn't connect; other side may not have accepted the request" << std::endl; @@ -89,7 +89,7 @@ bool JoinChatEventHandler(size_t id) { char c; std::cout << "Connection #" << id << " wants to join a conversation. Accept? (y/n) "; std::cin >> c; - if (c == 'y') + if (c != 'n') { chatConnectionId = id; return true; From 8729ff146245a87930c0f7db1a616e2eaae2b032 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Mon, 29 Dec 2014 20:04:46 +0545 Subject: [PATCH 51/54] little modifications --- include/common/HttpHandler.h | 6 ++-- include/common/TcpHandler.h | 2 +- include/common/common.h | 2 +- makefileHttp | 21 +++++++++---- output | Bin 408519 -> 372230 bytes src/common/ChatMessage.cpp | 2 +- src/common/HttpHandler.cpp | 57 ++++++++++++++++++++++++++++++----- src/common/TcpHandler.cpp | 2 +- src/common/abc.cpp | 26 ++++++++++++++++ 9 files changed, 98 insertions(+), 20 deletions(-) create mode 100644 src/common/abc.cpp diff --git a/include/common/HttpHandler.h b/include/common/HttpHandler.h index 3e7503e..248f843 100644 --- a/include/common/HttpHandler.h +++ b/include/common/HttpHandler.h @@ -26,13 +26,13 @@ class HttpHandler : public TcpHandler // send the GET request void SendGetRequest(std::string location); // send the POST request - void SendPostRequest(std::string location, rapidjson::Document d); + void SendPostRequest(std::string location, std::string jsonString); // get the response void GetResponse(); private: - boost::asio::io_service& m_ioService; - boost::shared_ptr m_socket; + //boost::asio::io_service& m_ioService; + //boost::shared_ptr m_socket; boost::asio::streambuf m_serverResponse; std::string m_hostName, m_port; }; diff --git a/include/common/TcpHandler.h b/include/common/TcpHandler.h index 45a17ee..643c2bb 100644 --- a/include/common/TcpHandler.h +++ b/include/common/TcpHandler.h @@ -34,7 +34,7 @@ class TcpHandler uint16_t GetRemotePort() const { return m_socket->remote_endpoint().port(); } boost::shared_ptr GetSocket() const { return m_socket; } -private: +protected: boost::shared_ptr m_socket; boost::asio::io_service &m_ioService; }; diff --git a/include/common/common.h b/include/common/common.h index 22594c2..486ace8 100644 --- a/include/common/common.h +++ b/include/common/common.h @@ -9,7 +9,7 @@ #include #include -#include +//#include //#include diff --git a/makefileHttp b/makefileHttp index cf393cc..a9155e8 100644 --- a/makefileHttp +++ b/makefileHttp @@ -4,19 +4,28 @@ SRC_DIR = src CFLAG = `pkg-config --cflags gtk+-3.0` CFLAGS = --std=c++11 -CC = clang++ +CC = g++ #for common -SOURCES_COMM = TcpHandler.cpp TcpListener.cpp RequestHandler.cpp ChatMessage.cpp -HEADERS_COMM = TcpHandler.h TcpListner.h RequestHandler.h +SOURCES_COMM = TcpHandler.cpp HttpHandler.cpp +HEADERS_COMM = TcpHandler.h HttpHandler.h OBJECTS_COMM = $(SOURCES_COMM:.cpp=.o) FSOURCES_COMM := $(addprefix $(SRC_DIR)/common/, $(SOURCES_COMM)) FHEADERS_COMM := $(addprefix $(INC_DIR)/common/, $(HEADERS_COMM)) LDFLAGS_COMM = -lboost_system -lboost_thread -lpthread -all: HttpHandler.o - $(CC) -o http $(SRC_DIR)/common/main.cpp $< -Iinclude/ $(CFLAGS) $(CFLAG) $(LDFLAGS_COMM) +all: common + $(CC) -o output $(SRC_DIR)/common/abc.cpp *.o -Iinclude/ $(CFLAGS) $(CFLAG) $(LDFLAGS_COMM) -HttpHandler.o : src/common/HttpHandler.cpp +common: $(OBJECTS_COMM) + $(CC) -c -o common.o $< + +%.o : src/common/%.cpp $(CC) -c -o $@ $< $(LDFLAGS_COMM) -Iinclude/ $(CFLAG) $(CFLAGS) + +client.o: src/client/TcpClient.cpp + $(CC) -c -o $@ $< -Iinclude/ $(CFLAG) $(CFLAGS) + +clean: + rm *.o diff --git a/output b/output index 9d592d5b7f796ecd59cc2ff7dacd377146290601..8bc8711ee3cca04d7e06c19ae1d293ab99946c5c 100755 GIT binary patch literal 372230 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

%KZlA?P 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 mLe!n4OUi~Ih~r#@+Y~(o# literal 408519 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 +#include "common/common.h" #include "common/HttpHandler.h" // the construtor, takes reference to io_service as argument -HttpHandler::HttpHandler(boost::asio::io_service& ioService):m_ioService(ioService), TcpHandler(ioService) +HttpHandler::HttpHandler(boost::asio::io_service& ioService):TcpHandler(ioService) { } @@ -14,7 +14,7 @@ HttpHandler::~HttpHandler() void HttpHandler:: SendGetRequest(std::string location) { // create a GET request - std::string requestStr = "GET " + location + " HTTP/1.1\r\n"; + std::string requestStr = "GET " + location + " HTTP/1.0\r\n"; requestStr += "Host: " + m_hostName + "\r\n"; requestStr += "Accept: */*\r\n"; requestStr += "Connection: Keep-Alive\r\n\r\n"; @@ -40,19 +40,18 @@ boost::shared_ptr HttpHandler::CreateSocket(boost::asio::io_service tcp::resolver::iterator end; //try each endpoint until we successfully establish a connection - tcp::socket socket(ioService); + boost::shared_ptr sptr(new tcp::socket(ioService)); boost::system::error_code error = boost::asio::error::host_not_found; while (error && endpt_iterator != end) { - socket.close(); - socket.connect(*endpt_iterator++, error); + (*sptr).close(); + (*sptr).connect(*endpt_iterator++, error); } if (error) { throw boost::system::system_error(error); } - boost::shared_ptr sptr(&socket); return sptr; } // the initialize function @@ -69,3 +68,47 @@ void HttpHandler::Initialize(std::string hostName, std::string port) } +// sending the post request +void HttpHandler::SendPostRequest(std::string location, std::string jsonString) +{ + std::string requestStr = "POST " + location + " HTTP/1.0\r\n"; + requestStr += "Host: " + m_hostName + "\r\n"; + requestStr += "Content-Type: application/jsonrequest\r\n"; + requestStr += "Accept: */*\r\n"; + requestStr += "Content-Length: " + std::to_string(jsonString.length()) + "\r\n"; + requestStr += "Connection: Keep-Alive\r\n\r\n"; + requestStr += jsonString; + + // convert requestStr to char* + int length = requestStr.length() + 1; + // first create a temporary char* + char *temp = new char[length]; + strcpy(temp, requestStr.c_str()); + // convert it to const char* bacause base class's Send requires const char* + const char*postRequest = (const char*)temp; + + // send through base class's send method + TcpHandler::Send(postRequest, length); + //std::cout << Available() << std::endl; +} + + +// response after a GET or POST +void HttpHandler::GetResponse() +{ + try + { + boost::asio::read_until(*m_socket, m_serverResponse, "\r\n\r\n"); + } + catch (Exception &e) + { + boost::asio::read_until(*m_socket, m_serverResponse, "\r\n"); + } + // following printing is just for the test purpose + std::istream responseStream(&m_serverResponse); + std::string test; + while(!std::getline(responseStream, test).eof()) + { + std::cout << test << std::endl; + } +} diff --git a/src/common/TcpHandler.cpp b/src/common/TcpHandler.cpp index 8eca292..9dbd952 100644 --- a/src/common/TcpHandler.cpp +++ b/src/common/TcpHandler.cpp @@ -1,4 +1,4 @@ -#include "common/common.h" +#include #include "common/TcpHandler.h" TcpHandler::TcpHandler(boost::asio::io_service &ioService) : diff --git a/src/common/abc.cpp b/src/common/abc.cpp new file mode 100644 index 0000000..cab1241 --- /dev/null +++ b/src/common/abc.cpp @@ -0,0 +1,26 @@ +#include +#include + +int main() +{ + boost::asio::io_service io_service; + try + { + HttpHandler httpHandler(io_service); + + std::string host = "localhost"; + std::string port = "8000"; + + httpHandler.Initialize(host, port); + std::string json = "{\"username\" : \"bpandey\",\"password\" : \"mysql\"}"; + httpHandler.SendPostRequest("/test/process-login/", json); + httpHandler.GetResponse(); + } + catch(int a) + { + std::cout << a; + } + + return 0; +} + From a2e4b7a653d73ae6c8d3643b1c6ec29f4994fc76 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Mon, 29 Dec 2014 20:39:02 +0545 Subject: [PATCH 52/54] nothing at all --- src/common/HttpHandler.cpp | 8 ++++---- src/common/abc.cpp | 9 +++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/common/HttpHandler.cpp b/src/common/HttpHandler.cpp index 26a67fe..5c76597 100644 --- a/src/common/HttpHandler.cpp +++ b/src/common/HttpHandler.cpp @@ -73,14 +73,14 @@ void HttpHandler::SendPostRequest(std::string location, std::string jsonString) { std::string requestStr = "POST " + location + " HTTP/1.0\r\n"; requestStr += "Host: " + m_hostName + "\r\n"; - requestStr += "Content-Type: application/jsonrequest\r\n"; + requestStr += "Content-Type: application/x-www-form-urlencoded\r\n"; requestStr += "Accept: */*\r\n"; requestStr += "Content-Length: " + std::to_string(jsonString.length()) + "\r\n"; requestStr += "Connection: Keep-Alive\r\n\r\n"; requestStr += jsonString; // convert requestStr to char* - int length = requestStr.length() + 1; + int length = requestStr.length(); // first create a temporary char* char *temp = new char[length]; strcpy(temp, requestStr.c_str()); @@ -98,11 +98,11 @@ void HttpHandler::GetResponse() { try { - boost::asio::read_until(*m_socket, m_serverResponse, "\r\n\r\n"); + boost::asio::read_until(*m_socket, m_serverResponse, ""); } catch (Exception &e) { - boost::asio::read_until(*m_socket, m_serverResponse, "\r\n"); + boost::asio::read_until(*m_socket, m_serverResponse, "\r\n\r\n"); } // following printing is just for the test purpose std::istream responseStream(&m_serverResponse); diff --git a/src/common/abc.cpp b/src/common/abc.cpp index cab1241..6b9e6ee 100644 --- a/src/common/abc.cpp +++ b/src/common/abc.cpp @@ -12,10 +12,15 @@ int main() std::string port = "8000"; httpHandler.Initialize(host, port); - std::string json = "{\"username\" : \"bpandey\",\"password\" : \"mysql\"}"; - httpHandler.SendPostRequest("/test/process-login/", json); + std::string json = "name=bpandey&password=mysql"; + //httpHandler.SendPostRequest("/test/process-login/", json); + httpHandler.SendGetRequest("/test/"); httpHandler.GetResponse(); } + catch(Exception &ex) + { + std::cout << ex.what(); + } catch(int a) { std::cout << a; From 807710bf5fab1bd11a37a6147b468e7dda6c39a0 Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Tue, 30 Dec 2014 22:30:19 +0545 Subject: [PATCH 53/54] json can now be read from server --- makefile | 39 +++++++++++++++++++++---------------- makefile1 | 26 +++++++++++++++++++++++++ makefileHttp | 31 ----------------------------- output | Bin 372230 -> 372230 bytes src/common/HttpHandler.cpp | 10 ++++++---- src/common/abc.cpp | 4 +--- 6 files changed, 55 insertions(+), 55 deletions(-) create mode 100644 makefile1 delete mode 100644 makefileHttp diff --git a/makefile b/makefile index 96ea3e3..a9155e8 100644 --- a/makefile +++ b/makefile @@ -1,26 +1,31 @@ -LIBFLAGS = `pkg-config --libs gtk+-3.0` -CFLAG = `pkg-config --cflags gtk+-3.0` - INC_DIR = include SRC_DIR = src -CC = g++ +CFLAG = `pkg-config --cflags gtk+-3.0` CFLAGS = --std=c++11 -#sources for common -SOURCES_COM = TcpHandler.cpp TcpListener.cpp -HEADERS_COM = TcpHandler.h TcpListener.h -OBJECTS_COM = $(SOURCES_COM:.cpp=.o) -FSOURCES_COM := $(addprefix $(SRC_DIR)/common/,$(SOURCES_COM)) -FHEADERS_COM := $(addprefix $(INC_DIR)/common/,$(HEADERS_FILE)) +CC = g++ + +#for common +SOURCES_COMM = TcpHandler.cpp HttpHandler.cpp +HEADERS_COMM = TcpHandler.h HttpHandler.h +OBJECTS_COMM = $(SOURCES_COMM:.cpp=.o) +FSOURCES_COMM := $(addprefix $(SRC_DIR)/common/, $(SOURCES_COMM)) +FHEADERS_COMM := $(addprefix $(INC_DIR)/common/, $(HEADERS_COMM)) + +LDFLAGS_COMM = -lboost_system -lboost_thread -lpthread -LDFLAGS_COM = -lboost_system -lboost_thread -lpthread +all: common + $(CC) -o output $(SRC_DIR)/common/abc.cpp *.o -Iinclude/ $(CFLAGS) $(CFLAG) $(LDFLAGS_COMM) -client: tcplistener.o tcphandler.o - $(CC) main.cpp -o $@ $(CFLAGS) +common: $(OBJECTS_COMM) + $(CC) -c -o common.o $< -tcphandler.o : $(FHEADERS_COM) - $(CC) -c $(CFLAGS) $(SRC_DIR)/common/TcpHandler.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) $(CFLAG) +%.o : src/common/%.cpp + $(CC) -c -o $@ $< $(LDFLAGS_COMM) -Iinclude/ $(CFLAG) $(CFLAGS) -tcplistener.o : $(FHEADERS_COM) - $(CC) -c $(CFLAGS) $(SRC_DIR)/common/TcpListener.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) $(CFLAG) +client.o: src/client/TcpClient.cpp + $(CC) -c -o $@ $< -Iinclude/ $(CFLAG) $(CFLAGS) + +clean: + rm *.o diff --git a/makefile1 b/makefile1 new file mode 100644 index 0000000..96ea3e3 --- /dev/null +++ b/makefile1 @@ -0,0 +1,26 @@ +LIBFLAGS = `pkg-config --libs gtk+-3.0` +CFLAG = `pkg-config --cflags gtk+-3.0` + +INC_DIR = include +SRC_DIR = src + +CC = g++ +CFLAGS = --std=c++11 + +#sources for common +SOURCES_COM = TcpHandler.cpp TcpListener.cpp +HEADERS_COM = TcpHandler.h TcpListener.h +OBJECTS_COM = $(SOURCES_COM:.cpp=.o) +FSOURCES_COM := $(addprefix $(SRC_DIR)/common/,$(SOURCES_COM)) +FHEADERS_COM := $(addprefix $(INC_DIR)/common/,$(HEADERS_FILE)) + +LDFLAGS_COM = -lboost_system -lboost_thread -lpthread + +client: tcplistener.o tcphandler.o + $(CC) main.cpp -o $@ $(CFLAGS) + +tcphandler.o : $(FHEADERS_COM) + $(CC) -c $(CFLAGS) $(SRC_DIR)/common/TcpHandler.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) $(CFLAG) + +tcplistener.o : $(FHEADERS_COM) + $(CC) -c $(CFLAGS) $(SRC_DIR)/common/TcpListener.cpp -g -o $@ -I$(INC_DIR) $(LDFLAGS_COM) $(CFLAG) diff --git a/makefileHttp b/makefileHttp deleted file mode 100644 index a9155e8..0000000 --- a/makefileHttp +++ /dev/null @@ -1,31 +0,0 @@ -INC_DIR = include -SRC_DIR = src - -CFLAG = `pkg-config --cflags gtk+-3.0` -CFLAGS = --std=c++11 - -CC = g++ - -#for common -SOURCES_COMM = TcpHandler.cpp HttpHandler.cpp -HEADERS_COMM = TcpHandler.h HttpHandler.h -OBJECTS_COMM = $(SOURCES_COMM:.cpp=.o) -FSOURCES_COMM := $(addprefix $(SRC_DIR)/common/, $(SOURCES_COMM)) -FHEADERS_COMM := $(addprefix $(INC_DIR)/common/, $(HEADERS_COMM)) - -LDFLAGS_COMM = -lboost_system -lboost_thread -lpthread - -all: common - $(CC) -o output $(SRC_DIR)/common/abc.cpp *.o -Iinclude/ $(CFLAGS) $(CFLAG) $(LDFLAGS_COMM) - -common: $(OBJECTS_COMM) - $(CC) -c -o common.o $< - -%.o : src/common/%.cpp - $(CC) -c -o $@ $< $(LDFLAGS_COMM) -Iinclude/ $(CFLAG) $(CFLAGS) - -client.o: src/client/TcpClient.cpp - $(CC) -c -o $@ $< -Iinclude/ $(CFLAG) $(CFLAGS) - -clean: - rm *.o diff --git a/output b/output index 8bc8711ee3cca04d7e06c19ae1d293ab99946c5c..7b65eed35625f74353cc3b09e5a156548945d0c2 100755 GIT binary patch delta 80231 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 diff --git a/src/common/HttpHandler.cpp b/src/common/HttpHandler.cpp index 5c76597..93a8f2e 100644 --- a/src/common/HttpHandler.cpp +++ b/src/common/HttpHandler.cpp @@ -16,7 +16,7 @@ void HttpHandler:: SendGetRequest(std::string location) // create a GET request std::string requestStr = "GET " + location + " HTTP/1.0\r\n"; requestStr += "Host: " + m_hostName + "\r\n"; - requestStr += "Accept: */*\r\n"; + requestStr += "Accept: application/json\r\n"; requestStr += "Connection: Keep-Alive\r\n\r\n"; // convert requestStr to char* @@ -73,8 +73,8 @@ void HttpHandler::SendPostRequest(std::string location, std::string jsonString) { std::string requestStr = "POST " + location + " HTTP/1.0\r\n"; requestStr += "Host: " + m_hostName + "\r\n"; - requestStr += "Content-Type: application/x-www-form-urlencoded\r\n"; - requestStr += "Accept: */*\r\n"; + requestStr += "Content-Type: application/json\r\n"; + requestStr += "Accept: application/json\r\n"; requestStr += "Content-Length: " + std::to_string(jsonString.length()) + "\r\n"; requestStr += "Connection: Keep-Alive\r\n\r\n"; requestStr += jsonString; @@ -98,7 +98,7 @@ void HttpHandler::GetResponse() { try { - boost::asio::read_until(*m_socket, m_serverResponse, ""); + boost::asio::read_until(*m_socket, m_serverResponse, "}"); } catch (Exception &e) { @@ -111,4 +111,6 @@ void HttpHandler::GetResponse() { std::cout << test << std::endl; } + std::getline(responseStream, test); + std::cout << test << std::endl; } diff --git a/src/common/abc.cpp b/src/common/abc.cpp index 6b9e6ee..ff72a7c 100644 --- a/src/common/abc.cpp +++ b/src/common/abc.cpp @@ -12,9 +12,7 @@ int main() std::string port = "8000"; httpHandler.Initialize(host, port); - std::string json = "name=bpandey&password=mysql"; - //httpHandler.SendPostRequest("/test/process-login/", json); - httpHandler.SendGetRequest("/test/"); + httpHandler.SendGetRequest("/test/process-login/"); httpHandler.GetResponse(); } catch(Exception &ex) From 5112155f2ae4bbd544eca973bb82613717cc4acb Mon Sep 17 00:00:00 2001 From: Bibek Pandey Date: Thu, 1 Jan 2015 21:29:41 +0545 Subject: [PATCH 54/54] the end of response from server is marked '<<>>' --- output | Bin 372230 -> 376326 bytes src/common/HttpHandler.cpp | 2 +- src/common/TcpRequest.cpp | 2 +- src/common/abc.cpp | 3 ++- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/output b/output index 7b65eed35625f74353cc3b09e5a156548945d0c2..2836de83f5101616b7bed05993c9b22386564e31 100755 GIT binary patch delta 98134 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%0AFLdfKgQ|kP)K&U>N$sf2}@nBwx0YF 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"); } catch (Exception &e) { diff --git a/src/common/TcpRequest.cpp b/src/common/TcpRequest.cpp index 9670d80..467fa11 100644 --- a/src/common/TcpRequest.cpp +++ b/src/common/TcpRequest.cpp @@ -168,4 +168,4 @@ std::string TcpRequest::GetPublicIp() uint16_t TcpRequest::GetPublicPort() { return GetValue("Public-Port").GetUint(); -} \ No newline at end of file +} diff --git a/src/common/abc.cpp b/src/common/abc.cpp index ff72a7c..19d0784 100644 --- a/src/common/abc.cpp +++ b/src/common/abc.cpp @@ -12,7 +12,8 @@ int main() std::string port = "8000"; httpHandler.Initialize(host, port); - httpHandler.SendGetRequest("/test/process-login/"); + std::string jsondata = "{\"name\":\"bibek\",\"age\":22}"; + httpHandler.SendPostRequest("/", jsondata); httpHandler.GetResponse(); } catch(Exception &ex)